diff --git a/openapi/cafe.yaml b/openapi/cafe.yaml index d127374..a43275d 100644 --- a/openapi/cafe.yaml +++ b/openapi/cafe.yaml @@ -48,11 +48,27 @@ components: securitySchemes: OAuth2: type: oauth2 - description: OAuth2 authorization for API access. + description: | + OAuth2 authorization for API access. The token endpoint accepts `grant_type=authorization_code`, `grant_type=client_credentials`, and `grant_type=refresh_token`. + + ### Differences from the OAuth2 specifications + + A standard OAuth2 client library can drive these flows, with the following to account for. + + Two behaviors do not conform to the specifications: + + - **Errors use RFC 9457 problem+json, not RFC 6749 Section 5.2.** Failures return `application/problem+json` with `type`, `title`, `status`, and `instance`. There is no `error` or `error_description` field, so the standard codes (`invalid_grant`, `invalid_client`, `unsupported_grant_type`) never appear — branch on the HTTP status and `title` instead. A refresh token that is expired, already rotated, or unrecognized returns `400` with a `title` of `Refresh token has expired` or `Invalid refresh token`, where a conformant server would return `error: invalid_grant`. + - **`refresh_token` is not a registrable grant type.** RFC 7591 Section 2 lists it, but `/oauth2/register` accepts only `authorization_code` and `client_credentials` in `grantTypes`. Refreshing requires no registration: holding a refresh token issued to the client is the authorization. A consequence is that refresh capability cannot be disabled per client — every `authorization_code` grant returns a refresh token, so a client intended for a shared or public device cannot be registered without one. + + Two are choices the specifications leave to the server: + + - **Refresh tokens rotate on every use.** A successful refresh retires the token presented and returns a replacement in `refresh_token`, as RFC 6749 Section 6 permits and the OAuth2 Security Best Current Practice recommends. Store the new value; the old one stops working. Refresh tokens expire 30 days after they are issued, and rotation restarts that window. The authorization code flow returns a refresh token with every access token; the client credentials flow returns none (RFC 6749 Section 4.4.3). + - **`scope` accepts commas.** The space-delimited form required by RFC 6749 is always accepted and recommended; comma-separated values are additionally tolerated. flows: authorizationCode: authorizationUrl: https://api.cafe.redocly.com/oauth2/authorize tokenUrl: https://api.cafe.redocly.com/oauth2/token + refreshUrl: https://api.cafe.redocly.com/oauth2/token scopes: menu:read: Read access to menu items and images menu:write: Write access to menu items (create, delete) diff --git a/openapi/components/schemas/RegisterClientObject.yaml b/openapi/components/schemas/RegisterClientObject.yaml index 070fcd4..d95876d 100644 --- a/openapi/components/schemas/RegisterClientObject.yaml +++ b/openapi/components/schemas/RegisterClientObject.yaml @@ -8,6 +8,7 @@ properties: items: type: string format: uri + default: [] description: List of redirect URIs (optional, defaults to empty array). scopes: type: array @@ -19,6 +20,12 @@ properties: - orders:read - orders:write - revenue:read + default: + - menu:read + - menu:write + - orders:read + - orders:write + - revenue:read description: List of scopes. grantTypes: type: array @@ -27,6 +34,9 @@ properties: enum: - authorization_code - client_credentials - description: List of grant types. + default: + - authorization_code + - client_credentials + description: List of grant types. `refresh_token` is not registrable; any client holding a refresh token may present it at the token endpoint. required: - name diff --git a/openapi/paths/oauth2_register.yaml b/openapi/paths/oauth2_register.yaml index ab8eae0..1e00427 100644 --- a/openapi/paths/oauth2_register.yaml +++ b/openapi/paths/oauth2_register.yaml @@ -2,18 +2,21 @@ post: tags: - Authorization summary: Create OAuth2 client - description: > - Register a new OAuth2 client for dynamic client registration. + description: | + Register a new OAuth2 client for dynamic client registration. This endpoint implements the Dynamic Client Registration Protocol (RFC 7591), using camelCase field names instead of the RFC's snake_case convention (e.g., `redirectUris` instead of `redirect_uris`, `grantTypes` instead of `grant_types`). The `name` field is required. Other fields are optional. If not provided: - - `redirectUris` defaults to an empty array. Note: When using the - `authorization_code` grant type, - `redirectUris` must be provided (per RFC 7591 Section 2). - - `scopes` defaults to all available scopes (menu:read, menu:write, - orders:read, orders:write) - - `grantTypes` defaults to both supported grant types (authorization_code, - client_credentials) + - `redirectUris` defaults to an empty array. Note: When using the `authorization_code` grant type, `redirectUris` must be provided (per RFC 7591 Section 2). + - `scopes` defaults to all available scopes (menu:read, menu:write, orders:read, orders:write, revenue:read) + - `grantTypes` defaults to `authorization_code` and `client_credentials` + + These defaults interact: a request that supplies only `name` pairs `authorization_code` with an empty `redirectUris`, which is not a usable combination. + Supply `redirectUris` explicitly to register the `authorization_code` grant, or set `grantTypes` to `client_credentials` alone for a client that needs no redirect URI. + + Refresh tokens require no registration and `refresh_token` is not a value you can register in `grantTypes`. + The token endpoint returns a refresh token alongside every access token it issues for the `authorization_code` grant, and accepts `grant_type=refresh_token` from any client presenting a refresh token issued to it. + The `client_credentials` grant returns no refresh token (RFC 6749 Section 4.4.3); those clients request a new access token with their own credentials instead. Returns the registered client information per RFC 7591, including: @@ -32,8 +35,6 @@ post: RegisterClientObject: dataValue: name: auth - redirectUris: - - https://api.cafe.redocly.com/callback scopes: - menu:read - menu:write @@ -42,6 +43,17 @@ post: - revenue:read grantTypes: - client_credentials + RegisterClientForAuthorizationCode: + dataValue: + name: pos-terminal + redirectUris: + - https://api.cafe.redocly.com/callback + scopes: + - menu:read + - orders:read + - orders:write + grantTypes: + - authorization_code responses: '201': description: OAuth2 client registered successfully. @@ -51,7 +63,5 @@ post: $ref: ../components/schemas/OAuth2Client.yaml '400': $ref: ../components/responses/BadRequest.yaml - '401': - $ref: ../components/responses/Unauthorized.yaml '500': $ref: ../components/responses/InternalServerError.yaml