From 80ef0d81a76a0e7b97b2434935fcccf09d01c3ef Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 18 Oct 2024 17:19:52 +0200 Subject: [PATCH 1/4] add the OAuth2Provider routes --- CHANGELOG.md | 9 + api_spec.yaml | 535 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 543 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c310154..5f096d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) +## [4.0.0] - 2024-10-18 + +### Changes + +- Updated the expected response types for the `GET /{apiBasePath}/.well-known/openid-configuration` +- Added the `OAuth2Provider` recipe. Added APIs: + - `POST /{apiBasePath}/oauth2/provider` + + ## [3.1.0] - 2024-10-18 ### Changes diff --git a/api_spec.yaml b/api_spec.yaml index 79bae82..c2aeaf8 100644 --- a/api_spec.yaml +++ b/api_spec.yaml @@ -4,7 +4,7 @@ info: description: | These are the APIs exposed by our backend SDK. To be consumed by the frontend only. `` in all the APIs are optional. Its default value is `public` - version: "3.1.0" + version: "4.0.0" title: Frontend Driver Interface contact: email: team@supertokens.io @@ -18,6 +18,7 @@ tags: - name: TOTP Recipe - name: JWT Recipe - name: OpenId Recipe + - name: OAuth2Provider Recipe paths: /{apiBasePath}/mfa/info: @@ -1704,6 +1705,45 @@ paths: type: string description: URL for fetching a list JsonWebKey, used for JWT signature verification. Refer to /jwt/jwks.json API in the JWT recipe for JWK details example: https://api.example.com/auth/jwt/jwks.json + authorization_endpoint: + type: string + description: URL of the authorization endpoint + example: https://api.example.com/auth/oauth/authorize + token_endpoint: + type: string + description: URL of the token endpoint + example: https://api.example.com/auth/oauth/token + userinfo_endpoint: + type: string + description: URL of the userinfo endpoint + example: https://api.example.com/auth/oauth/userinfo + revocation_endpoint: + type: string + description: URL of the token revocation endpoint + example: https://api.example.com/auth/oauth/revoke + token_introspection_endpoint: + type: string + description: URL of the token introspection endpoint + example: https://api.example.com/auth/oauth/introspect + end_session_endpoint: + type: string + description: URL of the end session endpoint + example: https://api.example.com/auth/oauth/end_session + subject_types_supported: + type: array + items: + type: string + enum: ["public"] + id_token_signing_alg_values_supported: + type: array + items: + type: string + enum: ["RS256"] + response_types_supported: + type: array + items: + type: string + enum: ["code", "id_token", "id_token token"] - $ref: '#/components/schemas/generalErrorResponse' '400': @@ -1715,6 +1755,484 @@ paths: '500': $ref: '#/components/responses/500' + /{apiBasePath}/oauth/login: + get: + tags: + - OAuth2Provider Recipe + operationId: oauthLoginGET + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Continues the OAuth2 login flow after the login page + responses: + '200': + description: The next url in the login flow + content: + application/json: + schema: + oneOf: + - type: object + properties: + frontendRedirectTo: + type: string + description: The URL to redirect the user to + example: https://client.com/callback?code=asdf1234567890&status=asdf1234 + - $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + + /{apiBasePath}/oauth/auth: + get: + tags: + - OAuth2Provider Recipe + operationId: oauthAuthGET + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Starts the OAuth2 login flow + responses: + '302': + description: Redirects the user to the login page or back to the client app + headers: + Location: + schema: + type: string + example: https://auth.example.com/auth/?loginChallenge=1234567890 + + '200': + description: A general error response + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/token: + post: + tags: + - OAuth2Provider Recipe + operationId: oauthTokenPOST + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Exchanges an OAuth2 grant (e.g.: authorization code) for an access token (and optionally a refresh/id token) + responses: + '200': + description: Issued tokens + content: + application/json: + schema: + oneOf: + - type: object + required: + - expires_in + - token_type + - scope + properties: + access_token: + type: string + desciption: 'The access token issued by the authorization server.' + + expires_in: + type: number + desciption: 'The lifetime in seconds of the access token (integer). For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated.' + + id_token: + type: string + desciption: 'To retrieve a refresh token request the id_token scope.' + + refresh_token: + type: string + desciption: 'The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request.' + + scope: + type: string + desciption: 'The scope of the access token' + + token_type: + type: string + desciption: 'The type of the token issued' + - $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/userinfo: + get: + tags: + - OAuth2Provider Recipe + operationId: oauthUserInfoGET + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Retrieves user information based on the access token passed to it + responses: + '200': + description: Retrieved user information + content: + application/json: + schema: + oneOf: + - type: object + required: + - sub + properties: + sub: + $ref: '#/components/schemas/userId' + email: + type: string + description: The email of the user + example: johndoe@gmail.com + email_verified: + type: boolean + description: Whether the email is verified + example: true + emails: + type: array + items: + type: string + example: johndoe@gmail.com + phoneNumber: + type: string + description: The phoneNumber of the user + example: '0036701234567' + phoneNumber_verified: + type: boolean + description: Whether the phoneNumber is verified + example: true + phoneNumbers: + type: array + items: + type: string + example: '0036701234567' + roles: + type: array + items: + type: string + example: admin + permissions: + type: array + items: + type: string + example: 'user:create' + + - $ref: '#/components/schemas/generalErrorResponse' + + '401': + description: The access token is expired, revoked or malformed + headers: + WWW-Authenticate: + schema: + type: string + example: 'Bearer error="invalid_token"' + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: Invalid or expired OAuth2 access token + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/revoke: + post: + tags: + - OAuth2Provider Recipe + operationId: oauthRevokePOST + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Revokes an access/refresh token + responses: + '200': + description: Revoked the access/refresh token + content: + application/json: + schema: + oneOf: + - type: object + required: + - status + properties: + status: + $ref: '#/components/schemas/statusOK' + + - $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/introspect: + post: + tags: + - OAuth2Provider Recipe + operationId: oauthIntrospectPOST + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Introspects an access/refresh token + responses: + '200': + description: Information about the token + content: + application/json: + schema: + oneOf: + - type: object + required: + - active + properties: + active: + type: boolean + description: Whether the token is active or not + example: true + token_type: + type: string + description: The type of the token + example: Bearer + token_use: + type: string + description: The use of the token + example: access_token + sub: + $ref: '#/components/schemas/userId' + email: + type: string + description: The email of the user + example: johndoe@gmail.com + email_verified: + type: boolean + description: Whether the email is verified + example: true + emails: + type: array + items: + type: string + example: johndoe@gmail.com + phoneNumber: + type: string + description: The phoneNumber of the user + example: '0036701234567' + phoneNumber_verified: + type: boolean + description: Whether the phoneNumber is verified + example: true + phoneNumbers: + type: array + items: + type: string + example: '0036701234567' + roles: + type: array + items: + type: string + example: admin + permissions: + type: array + items: + type: string + example: 'user:create' + + - $ref: '#/components/schemas/generalErrorResponse' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/end_session: + post: + tags: + - OAuth2Provider Recipe + operationId: oauthEndSessionPOST + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Redirects the user to a page where they can log out and revoke the oauth tokens + responses: + '302': + description: Redirects the user to the logout page or back to the client app + headers: + Location: + schema: + type: string + example: https://auth.example.com/auth/oauth/logout?logoutChallenge=1234567890 + + '200': + description: A general error response + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + get: + tags: + - OAuth2Provider Recipe + operationId: oauthEndSessionGET + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Redirects the user to a page where they can log out and revoke the oauth tokens + responses: + '302': + description: Redirects the user to the logout page or back to the client app + headers: + Location: + schema: + type: string + example: https://auth.example.com/auth/oauth/logout?logoutChallenge=1234567890 + + '200': + description: A general error response + content: + application/json: + schema: + $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/logininfo: + get: + tags: + - OAuth2Provider Recipe + operationId: oauthLoginInfoGET + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Retrieves information about the OAuth2 login + responses: + '200': + description: Information about the current login flow + content: + application/json: + schema: + oneOf: + - type: object + required: + - status + - info + properties: + status: + $ref: '#/components/schemas/statusOK' + info: + type: object + description: Information about the current login flow + required: + - clientId + properties: + clientId: + type: string + description: The ID of the client. + clientName: + type: string + description: The name of the client. + tosUri: + type: string + description: The URI of the client's terms of service. + policyUri: + type: string + description: The URI of the client's privacy policy. + logoUri: + type: string + description: The URI of the client's logo. + clientUri: + type: string + description: The URI of the client we can link to on the login page + + - $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + + /{apiBasePath}/oauth/logout: + post: + tags: + - OAuth2Provider Recipe + operationId: oauthLogoutPOST + parameters: + - $ref: '#/components/parameters/apiBasePath' + description: | + Logs out the user and revokes the access/refresh tokens based on the id_token_hint passed to the end_session endpoint + responses: + '200': + description: Accepts the logout request specified by the challenge and gets where the user should be redirected to + content: + application/json: + schema: + oneOf: + - type: object + properties: + frontendRedirectTo: + type: string + description: The URL to redirect the user to + example: https://auth.example.com/auth/oauth/logout?logoutChallenge=1234567890 + - $ref: '#/components/schemas/generalErrorResponse' + + '400': + $ref: '#/components/responses/400-oauth-error' + + '404': + $ref: '#/components/responses/404' + + '500': + $ref: '#/components/responses/500' + /example: get: tags: @@ -1879,6 +2397,21 @@ components: text/plain: schema: $ref: "#/components/schemas/notFound" + + 400-oauth-error: + description: error code 400 for OAuth2 errors + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: invalid_request + error_description: + type: string + example: 'Unsupported grant type: password' + 403-factor-setup: description: A claim validation error happened during factor setup From 9f49a7f29f12729d457951276942bd772b8354eb Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 21 Oct 2024 01:28:28 +0200 Subject: [PATCH 2/4] chore: update changelog --- CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f096d3..6ee392b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changes - Updated the expected response types for the `GET /{apiBasePath}/.well-known/openid-configuration` -- Added the `OAuth2Provider` recipe. Added APIs: - - `POST /{apiBasePath}/oauth2/provider` +- Added the `OAuth2Provider` recipe. New APIs: + - `GET /{apiBasePath}/oauth/login` + - `GET /{apiBasePath}/oauth/auth` + - `POST /{apiBasePath}/oauth/token` + - `GET /{apiBasePath}/oauth/userinfo` + - `POST /{apiBasePath}/oauth/revoke` + - `POST /{apiBasePath}/oauth/introspect` + - `POST /{apiBasePath}/oauth/end_session` + - `GET /{apiBasePath}/oauth/end_session` + - `GET /{apiBasePath}/oauth/logininfo` + - `POST /{apiBasePath}/oauth/logout` ## [3.1.0] - 2024-10-18 From 3612d82172e32fc450c712ad4cbe5ca8e2abceaf Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 21 Oct 2024 01:57:28 +0200 Subject: [PATCH 3/4] feat: describe OAuth2 input types --- api_spec.yaml | 152 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 10 deletions(-) diff --git a/api_spec.yaml b/api_spec.yaml index c2aeaf8..500380c 100644 --- a/api_spec.yaml +++ b/api_spec.yaml @@ -1762,6 +1762,11 @@ paths: operationId: oauthLoginGET parameters: - $ref: '#/components/parameters/apiBasePath' + - in: query + name: loginChallenge + required: true + schema: + type: string description: | Continues the OAuth2 login flow after the login page responses: @@ -1797,7 +1802,7 @@ paths: parameters: - $ref: '#/components/parameters/apiBasePath' description: | - Starts the OAuth2 login flow + Starts the OAuth2 login flow - for a detailed description of all input parameters please see the OAuth2 and OpenID Connect Core specs responses: '302': description: Redirects the user to the login page or back to the client app @@ -1831,7 +1836,7 @@ paths: parameters: - $ref: '#/components/parameters/apiBasePath' description: | - Exchanges an OAuth2 grant (e.g.: authorization code) for an access token (and optionally a refresh/id token) + Exchanges an OAuth2 grant (e.g.: authorization code) for an access token (and optionally a refresh/id token) - for a detailed description of all input parameters please see the OAuth2 and OpenID Connect Core specs responses: '200': description: Issued tokens @@ -1847,27 +1852,27 @@ paths: properties: access_token: type: string - desciption: 'The access token issued by the authorization server.' + description: 'The access token issued by the authorization server.' expires_in: type: number - desciption: 'The lifetime in seconds of the access token (integer). For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated.' + description: 'The lifetime in seconds of the access token (integer). For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated.' id_token: type: string - desciption: 'To retrieve a refresh token request the id_token scope.' + description: 'To retrieve a refresh token request the id_token scope.' refresh_token: type: string - desciption: 'The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request.' + description: 'The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request.' scope: type: string - desciption: 'The scope of the access token' + description: 'The scope of the access token' token_type: type: string - desciption: 'The type of the token issued' + description: 'The type of the token issued' - $ref: '#/components/schemas/generalErrorResponse' '400': @@ -1886,6 +1891,8 @@ paths: operationId: oauthUserInfoGET parameters: - $ref: '#/components/parameters/apiBasePath' + security: + - OAuth2AccessTokenBearer: [] description: | Retrieves user information based on the access token passed to it responses: @@ -1970,7 +1977,39 @@ paths: parameters: - $ref: '#/components/parameters/apiBasePath' description: | - Revokes an access/refresh token + Revokes an access/refresh token - the client id and secret can also be provided in an authorization header using the Basic scheme + requestBody: + content: + application/json: + schema: + type: object + required: + - token + properties: + token: + type: string + example: asdfasdfasfd + client_id: + type: string + example: st-cl-test-client + client_secret: + type: string + example: superSecret + x-www-form-urlencoded: + schema: + type: object + required: + - token + properties: + token: + type: string + example: asdfasdfasfd + client_id: + type: string + example: st-cl-test-client + client_secret: + type: string + example: superSecret responses: '200': description: Revoked the access/refresh token @@ -2005,6 +2044,26 @@ paths: - $ref: '#/components/parameters/apiBasePath' description: | Introspects an access/refresh token + requestBody: + content: + application/json: + schema: + type: object + required: + - token + properties: + token: + type: string + example: asdfasdfasfd + application/x-www-form-urlencoded: + schema: + type: object + required: + - token + properties: + token: + type: string + example: asdfasdfasfd responses: '200': description: Information about the token @@ -2082,8 +2141,36 @@ paths: operationId: oauthEndSessionPOST parameters: - $ref: '#/components/parameters/apiBasePath' + requestBody: + content: + application/json: + schema: + type: object + properties: + id_token_hint: + type: string + example: asdfasdfASDF + client_id: + type: string + example: st-cl-example-client + post_logout_redirect_uri: + type: string + example: https://client.example.com/logoutCallback + application/x-www-form-urlencoded: + schema: + type: object + properties: + id_token_hint: + type: string + example: asdfasdfASDF + client_id: + type: string + example: st-cl-example-client + post_logout_redirect_uri: + type: string + example: https://client.example.com/logoutCallback description: | - Redirects the user to a page where they can log out and revoke the oauth tokens + Redirects the user to a page where they can log out and revoke the oauth tokens - for a detailed description of input parameters please see the user initiated logout spec responses: '302': description: Redirects the user to the logout page or back to the client app @@ -2114,6 +2201,21 @@ paths: operationId: oauthEndSessionGET parameters: - $ref: '#/components/parameters/apiBasePath' + - in: query + name: id_token_hint + schema: + type: string + example: asdfasdfASDF + - in: query + name: client_id + schema: + type: string + example: st-cl-example-client + - in: query + name: post_logout_redirect_uri + schema: + type: string + example: https://client.example.com/logoutCallback description: | Redirects the user to a page where they can log out and revoke the oauth tokens responses: @@ -2148,6 +2250,11 @@ paths: operationId: oauthLoginInfoGET parameters: - $ref: '#/components/parameters/apiBasePath' + - in: query + name: loginChallenge + required: true + schema: + type: string description: | Retrieves information about the OAuth2 login responses: @@ -2209,6 +2316,26 @@ paths: - $ref: '#/components/parameters/apiBasePath' description: | Logs out the user and revokes the access/refresh tokens based on the id_token_hint passed to the end_session endpoint + requestBody: + content: + application/json: + schema: + type: object + required: + - logoutChallenge + properties: + logoutChallenge: + type: string + example: asdfasdfasfd + x-www-form-urlencoded: + schema: + type: object + required: + - logoutChallenge + properties: + logoutChallenge: + type: string + example: asdfasdfasfd responses: '200': description: Accepts the logout request specified by the challenge and gets where the user should be redirected to @@ -2792,3 +2919,8 @@ components: type: apiKey in: cookie name: sRefreshToken + + OAuth2AccessTokenBearer: + description: An OAuth2 access token returned by the token or authorization endpoints during OAuth flows + type: http + scheme: bearer From 2c16569d7e5e5f6593fdbc7e4d4eda038dc8c82b Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 21 Oct 2024 02:02:21 +0200 Subject: [PATCH 4/4] removed asdf examples --- api_spec.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/api_spec.yaml b/api_spec.yaml index 500380c..1325aa6 100644 --- a/api_spec.yaml +++ b/api_spec.yaml @@ -1894,7 +1894,7 @@ paths: security: - OAuth2AccessTokenBearer: [] description: | - Retrieves user information based on the access token passed to it + Retrieves user information based on the access token passed in the authorization header responses: '200': description: Retrieved user information @@ -1988,7 +1988,6 @@ paths: properties: token: type: string - example: asdfasdfasfd client_id: type: string example: st-cl-test-client @@ -2003,7 +2002,6 @@ paths: properties: token: type: string - example: asdfasdfasfd client_id: type: string example: st-cl-test-client @@ -2054,7 +2052,6 @@ paths: properties: token: type: string - example: asdfasdfasfd application/x-www-form-urlencoded: schema: type: object @@ -2063,7 +2060,6 @@ paths: properties: token: type: string - example: asdfasdfasfd responses: '200': description: Information about the token @@ -2149,7 +2145,6 @@ paths: properties: id_token_hint: type: string - example: asdfasdfASDF client_id: type: string example: st-cl-example-client @@ -2162,7 +2157,6 @@ paths: properties: id_token_hint: type: string - example: asdfasdfASDF client_id: type: string example: st-cl-example-client @@ -2205,7 +2199,6 @@ paths: name: id_token_hint schema: type: string - example: asdfasdfASDF - in: query name: client_id schema: @@ -2326,7 +2319,6 @@ paths: properties: logoutChallenge: type: string - example: asdfasdfasfd x-www-form-urlencoded: schema: type: object @@ -2335,7 +2327,6 @@ paths: properties: logoutChallenge: type: string - example: asdfasdfasfd responses: '200': description: Accepts the logout request specified by the challenge and gets where the user should be redirected to