From 6add9f2e558058264900c885d27816819dd3a203 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 17 Oct 2024 09:44:05 +0200 Subject: [PATCH 1/7] feat: add clientId and secret to the returned info about clients and refresh token rotation flag --- lib/build/recipe/oauth2provider/OAuth2Client.d.ts | 6 +++--- lib/build/recipe/oauth2provider/OAuth2Client.js | 3 ++- lib/build/recipe/oauth2provider/types.d.ts | 5 ++--- lib/ts/recipe/oauth2provider/OAuth2Client.ts | 8 +++++--- lib/ts/recipe/oauth2provider/types.ts | 5 ++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/build/recipe/oauth2provider/OAuth2Client.d.ts b/lib/build/recipe/oauth2provider/OAuth2Client.d.ts index 7ffba30b1..8d5ab3324 100644 --- a/lib/build/recipe/oauth2provider/OAuth2Client.d.ts +++ b/lib/build/recipe/oauth2provider/OAuth2Client.d.ts @@ -8,8 +8,6 @@ export declare class OAuth2Client { clientId: string; /** * OAuth 2.0 Client Secret - * The secret will be included in the create request as cleartext, and then - * never again. The secret is kept in hashed format and is not recoverable once lost. */ clientSecret?: string; /** @@ -136,9 +134,10 @@ export declare class OAuth2Client { updatedAt: string; /** * Metadata - JSON object - * JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger. */ metadata: Record; + /** This flag is set to true if refresh tokens are updated upon use */ + enableRefreshTokenRotation: boolean; constructor({ clientId, clientSecret, @@ -166,6 +165,7 @@ export declare class OAuth2Client { createdAt, updatedAt, metadata, + enableRefreshTokenRotation, }: OAuth2ClientOptions); static fromAPIResponse(response: any): OAuth2Client; } diff --git a/lib/build/recipe/oauth2provider/OAuth2Client.js b/lib/build/recipe/oauth2provider/OAuth2Client.js index 4c700f04f..14622abd3 100644 --- a/lib/build/recipe/oauth2provider/OAuth2Client.js +++ b/lib/build/recipe/oauth2provider/OAuth2Client.js @@ -44,10 +44,10 @@ class OAuth2Client { createdAt, updatedAt, metadata = {}, + enableRefreshTokenRotation = false, }) { /** * Metadata - JSON object - * JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger. */ this.metadata = {}; this.clientId = clientId; @@ -76,6 +76,7 @@ class OAuth2Client { this.createdAt = createdAt; this.updatedAt = updatedAt; this.metadata = metadata; + this.enableRefreshTokenRotation = enableRefreshTokenRotation; } static fromAPIResponse(response) { return new OAuth2Client(utils_1.transformObjectKeys(response, "camelCase")); diff --git a/lib/build/recipe/oauth2provider/types.d.ts b/lib/build/recipe/oauth2provider/types.d.ts index 7d3987138..8c4f49b7e 100644 --- a/lib/build/recipe/oauth2provider/types.d.ts +++ b/lib/build/recipe/oauth2provider/types.d.ts @@ -533,6 +533,7 @@ export declare type OAuth2ClientOptions = { policyUri?: string; tosUri?: string; metadata?: Record; + enableRefreshTokenRotation?: boolean; }; export declare type GetOAuth2ClientsInput = { /** @@ -548,9 +549,7 @@ export declare type GetOAuth2ClientsInput = { */ clientName?: string; }; -export declare type CreateOAuth2ClientInput = Partial< - Omit ->; +export declare type CreateOAuth2ClientInput = Partial>; export declare type UpdateOAuth2ClientInput = NonNullableProperties< Omit > & { diff --git a/lib/ts/recipe/oauth2provider/OAuth2Client.ts b/lib/ts/recipe/oauth2provider/OAuth2Client.ts index 92546443b..f24de0a4d 100644 --- a/lib/ts/recipe/oauth2provider/OAuth2Client.ts +++ b/lib/ts/recipe/oauth2provider/OAuth2Client.ts @@ -25,8 +25,6 @@ export class OAuth2Client { /** * OAuth 2.0 Client Secret - * The secret will be included in the create request as cleartext, and then - * never again. The secret is kept in hashed format and is not recoverable once lost. */ clientSecret?: string; @@ -177,10 +175,12 @@ export class OAuth2Client { /** * Metadata - JSON object - * JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger. */ metadata: Record = {}; + /** This flag is set to true if refresh tokens are updated upon use */ + enableRefreshTokenRotation: boolean; + constructor({ clientId, clientSecret, @@ -208,6 +208,7 @@ export class OAuth2Client { createdAt, updatedAt, metadata = {}, + enableRefreshTokenRotation = false, }: OAuth2ClientOptions) { this.clientId = clientId; this.clientSecret = clientSecret; @@ -235,6 +236,7 @@ export class OAuth2Client { this.createdAt = createdAt; this.updatedAt = updatedAt; this.metadata = metadata; + this.enableRefreshTokenRotation = enableRefreshTokenRotation; } static fromAPIResponse(response: any): OAuth2Client { diff --git a/lib/ts/recipe/oauth2provider/types.ts b/lib/ts/recipe/oauth2provider/types.ts index c964f7ce0..63488692b 100644 --- a/lib/ts/recipe/oauth2provider/types.ts +++ b/lib/ts/recipe/oauth2provider/types.ts @@ -541,6 +541,7 @@ export type OAuth2ClientOptions = { policyUri?: string; tosUri?: string; metadata?: Record; + enableRefreshTokenRotation?: boolean; }; export type GetOAuth2ClientsInput = { @@ -560,9 +561,7 @@ export type GetOAuth2ClientsInput = { clientName?: string; }; -export type CreateOAuth2ClientInput = Partial< - Omit ->; +export type CreateOAuth2ClientInput = Partial>; export type UpdateOAuth2ClientInput = NonNullableProperties< Omit From 9fde8f2404a0680a7ca0a23d94ca1d3fbccd7982 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 17 Oct 2024 10:22:40 +0200 Subject: [PATCH 2/7] ci: update ci scripts to add new encryption key prop --- .circleci/forceRunCI.sh | 4 ++-- .circleci/setupAndTestBackendSDKWithFreeCore.sh | 1 + .circleci/setupAndTestWithAuthReact.sh | 1 + .circleci/setupAndTestWithFreeCore.sh | 1 + .circleci/setupAndTestWithFrontend.sh | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/forceRunCI.sh b/.circleci/forceRunCI.sh index e0a05f6d9..f8088f23e 100755 --- a/.circleci/forceRunCI.sh +++ b/.circleci/forceRunCI.sh @@ -2,8 +2,8 @@ PAT=`cat .pat` auth=`echo "${PAT}:" | tr -d '\n' | base64 --wrap=0` branch=`git rev-parse --abbrev-ref HEAD` -cdiCoreMap='{ "5.2": "feat/oauth-provider-base" }' -cdiPluginInterfaceMap='{ "5.2": "feat/oauth-provider-base" }' +cdiCoreMap='{ "5.2": "feat/oauth/remaining-changes" }' +cdiPluginInterfaceMap='{ "5.2": "feat/oauth/remaining-changes" }' fdiNodeMap='{ "3.1": "21.0", "4.0": "21.0" }' fdiWebsiteMap='{ "1.17": "20.1", "1.18": "20.1", "1.19": "20.1", "2.0": "20.1", "3.0": "20.1", "3.1": "20.1", "4.0": "20.1" }' fdiAuthReactMap='{ "3.1": "0.49", "4.0": "0.49" }' diff --git a/.circleci/setupAndTestBackendSDKWithFreeCore.sh b/.circleci/setupAndTestBackendSDKWithFreeCore.sh index b139658fb..0870c659a 100755 --- a/.circleci/setupAndTestBackendSDKWithFreeCore.sh +++ b/.circleci/setupAndTestBackendSDKWithFreeCore.sh @@ -77,6 +77,7 @@ git checkout $coreTag sed -i 's/# oauth_provider_public_service_url:/oauth_provider_public_service_url: "http:\/\/localhost:4444"/' devConfig.yaml sed -i 's/# oauth_provider_admin_service_url:/oauth_provider_admin_service_url: "http:\/\/localhost:4445"/' devConfig.yaml sed -i 's/# oauth_provider_consent_login_base_url:/oauth_provider_consent_login_base_url: "http:\/\/localhost:3001\/auth"/' devConfig.yaml +sed -i 's/# oauth_client_secret_encryption_key:/oauth_client_secret_encryption_key: "asdfasdfasdfasdfasdf"/' devConfig.yaml cd ../supertokens-plugin-interface git checkout $pluginInterfaceTag diff --git a/.circleci/setupAndTestWithAuthReact.sh b/.circleci/setupAndTestWithAuthReact.sh index 61ae3f2af..2622c5bbd 100755 --- a/.circleci/setupAndTestWithAuthReact.sh +++ b/.circleci/setupAndTestWithAuthReact.sh @@ -73,6 +73,7 @@ git checkout $coreTag sed -i 's/# oauth_provider_public_service_url:/oauth_provider_public_service_url: "http:\/\/localhost:4444"/' devConfig.yaml sed -i 's/# oauth_provider_admin_service_url:/oauth_provider_admin_service_url: "http:\/\/localhost:4445"/' devConfig.yaml sed -i 's/# oauth_provider_consent_login_base_url:/oauth_provider_consent_login_base_url: "http:\/\/localhost:3001\/auth"/' devConfig.yaml +sed -i 's/# oauth_client_secret_encryption_key:/oauth_client_secret_encryption_key: "asdfasdfasdfasdfasdf"/' devConfig.yaml cd ../supertokens-plugin-interface git checkout $pluginInterfaceTag diff --git a/.circleci/setupAndTestWithFreeCore.sh b/.circleci/setupAndTestWithFreeCore.sh index bda9aa416..100d4ae92 100755 --- a/.circleci/setupAndTestWithFreeCore.sh +++ b/.circleci/setupAndTestWithFreeCore.sh @@ -76,6 +76,7 @@ git checkout $coreTag sed -i 's/# oauth_provider_public_service_url:/oauth_provider_public_service_url: "http:\/\/localhost:4444"/' devConfig.yaml sed -i 's/# oauth_provider_admin_service_url:/oauth_provider_admin_service_url: "http:\/\/localhost:4445"/' devConfig.yaml sed -i 's/# oauth_provider_consent_login_base_url:/oauth_provider_consent_login_base_url: "http:\/\/localhost:3001\/auth"/' devConfig.yaml +sed -i 's/# oauth_client_secret_encryption_key:/oauth_client_secret_encryption_key: "asdfasdfasdfasdfasdf"/' devConfig.yaml cd ../supertokens-plugin-interface git checkout $pluginInterfaceTag diff --git a/.circleci/setupAndTestWithFrontend.sh b/.circleci/setupAndTestWithFrontend.sh index 9721c7a6e..937faf787 100755 --- a/.circleci/setupAndTestWithFrontend.sh +++ b/.circleci/setupAndTestWithFrontend.sh @@ -73,6 +73,7 @@ git checkout $coreTag sed -i 's/# oauth_provider_public_service_url:/oauth_provider_public_service_url: "http:\/\/localhost:4444"/' devConfig.yaml sed -i 's/# oauth_provider_admin_service_url:/oauth_provider_admin_service_url: "http:\/\/localhost:4445"/' devConfig.yaml sed -i 's/# oauth_provider_consent_login_base_url:/oauth_provider_consent_login_base_url: "http:\/\/localhost:3001\/auth"/' devConfig.yaml +sed -i 's/# oauth_client_secret_encryption_key:/oauth_client_secret_encryption_key: "asdfasdfasdfasdfasdf"/' devConfig.yaml cd ../supertokens-plugin-interface git checkout $pluginInterfaceTag From d914741858f2751a4f246bdc5b1adb5f01e95c58 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 17 Oct 2024 13:52:56 +0200 Subject: [PATCH 3/7] ci: re-add params in continuation --- .circleci/config_continue.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.circleci/config_continue.yml b/.circleci/config_continue.yml index 11ac5d92d..b250913d9 100644 --- a/.circleci/config_continue.yml +++ b/.circleci/config_continue.yml @@ -5,6 +5,26 @@ orbs: slack: circleci/slack@3.4.2 jq: circleci/jq@2.2.0 +parameters: + force: + type: boolean + default: false + cdi-core-map: + type: string + default: "{}" + cdi-plugin-interface-map: + type: string + default: "{}" + fdi-node-map: + type: string + default: "{}" + fdi-auth-react-map: + type: string + default: "{}" + fdi-website-map: + type: string + default: "{}" + jobs: test-dev-tag-as-not-passed: docker: From 53e43117f5c5415824e84b6054cb0d2c30442085 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 17 Oct 2024 14:26:56 +0200 Subject: [PATCH 4/7] chore: fix export definitions for custom framework --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 335447feb..885295016 100644 --- a/package.json +++ b/package.json @@ -65,15 +65,15 @@ "types": "./nextjs/index.d.ts", "default": "./nextjs/index.js" }, - "./customframework": { + "./custom": { "types": "./custom/index.d.ts", "default": "./custom/index.js" }, - "./customframework/index": { + "./custom/index": { "types": "./custom/index.d.ts", "default": "./custom/index.js" }, - "./customframework/index.js": { + "./custom/index.js": { "types": "./custom/index.d.ts", "default": "./custom/index.js" }, From d489e1d8ec9caf91a3bb6bd2fff015e8501745ed Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 18 Oct 2024 11:49:34 +0200 Subject: [PATCH 5/7] feat: remove the OAuth2Client recipe --- .../oauth2client/api/implementation.d.ts | 3 - .../recipe/oauth2client/api/implementation.js | 69 ------- lib/build/recipe/oauth2client/api/signin.d.ts | 9 - lib/build/recipe/oauth2client/api/signin.js | 74 ------- lib/build/recipe/oauth2client/constants.d.ts | 2 - lib/build/recipe/oauth2client/constants.js | 18 -- lib/build/recipe/oauth2client/index.d.ts | 23 --- lib/build/recipe/oauth2client/index.js | 70 ------- lib/build/recipe/oauth2client/recipe.d.ts | 38 ---- lib/build/recipe/oauth2client/recipe.js | 107 ---------- .../oauth2client/recipeImplementation.d.ts | 4 - .../oauth2client/recipeImplementation.js | 142 -------------- lib/build/recipe/oauth2client/types.d.ts | 155 --------------- lib/build/recipe/oauth2client/types.js | 16 -- lib/build/recipe/oauth2client/utils.d.ts | 7 - lib/build/recipe/oauth2client/utils.js | 45 ----- .../recipe/oauth2client/api/implementation.ts | 72 ------- lib/ts/recipe/oauth2client/api/signin.ts | 84 -------- lib/ts/recipe/oauth2client/constants.ts | 16 -- lib/ts/recipe/oauth2client/index.ts | 81 -------- lib/ts/recipe/oauth2client/recipe.ts | 137 ------------- .../oauth2client/recipeImplementation.ts | 185 ------------------ lib/ts/recipe/oauth2client/types.ts | 157 --------------- lib/ts/recipe/oauth2client/utils.ts | 48 ----- recipe/oauth2client/index.d.ts | 10 - recipe/oauth2client/index.js | 6 - recipe/oauth2client/types/index.d.ts | 10 - recipe/oauth2client/types/index.js | 6 - test/test-server/src/index.ts | 22 --- 29 files changed, 1616 deletions(-) delete mode 100644 lib/build/recipe/oauth2client/api/implementation.d.ts delete mode 100644 lib/build/recipe/oauth2client/api/implementation.js delete mode 100644 lib/build/recipe/oauth2client/api/signin.d.ts delete mode 100644 lib/build/recipe/oauth2client/api/signin.js delete mode 100644 lib/build/recipe/oauth2client/constants.d.ts delete mode 100644 lib/build/recipe/oauth2client/constants.js delete mode 100644 lib/build/recipe/oauth2client/index.d.ts delete mode 100644 lib/build/recipe/oauth2client/index.js delete mode 100644 lib/build/recipe/oauth2client/recipe.d.ts delete mode 100644 lib/build/recipe/oauth2client/recipe.js delete mode 100644 lib/build/recipe/oauth2client/recipeImplementation.d.ts delete mode 100644 lib/build/recipe/oauth2client/recipeImplementation.js delete mode 100644 lib/build/recipe/oauth2client/types.d.ts delete mode 100644 lib/build/recipe/oauth2client/types.js delete mode 100644 lib/build/recipe/oauth2client/utils.d.ts delete mode 100644 lib/build/recipe/oauth2client/utils.js delete mode 100644 lib/ts/recipe/oauth2client/api/implementation.ts delete mode 100644 lib/ts/recipe/oauth2client/api/signin.ts delete mode 100644 lib/ts/recipe/oauth2client/constants.ts delete mode 100644 lib/ts/recipe/oauth2client/index.ts delete mode 100644 lib/ts/recipe/oauth2client/recipe.ts delete mode 100644 lib/ts/recipe/oauth2client/recipeImplementation.ts delete mode 100644 lib/ts/recipe/oauth2client/types.ts delete mode 100644 lib/ts/recipe/oauth2client/utils.ts delete mode 100644 recipe/oauth2client/index.d.ts delete mode 100644 recipe/oauth2client/index.js delete mode 100644 recipe/oauth2client/types/index.d.ts delete mode 100644 recipe/oauth2client/types/index.js diff --git a/lib/build/recipe/oauth2client/api/implementation.d.ts b/lib/build/recipe/oauth2client/api/implementation.d.ts deleted file mode 100644 index dd40e7025..000000000 --- a/lib/build/recipe/oauth2client/api/implementation.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -// @ts-nocheck -import { APIInterface } from "../"; -export default function getAPIInterface(): APIInterface; diff --git a/lib/build/recipe/oauth2client/api/implementation.js b/lib/build/recipe/oauth2client/api/implementation.js deleted file mode 100644 index 5a5e1d56f..000000000 --- a/lib/build/recipe/oauth2client/api/implementation.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; -Object.defineProperty(exports, "__esModule", { value: true }); -const session_1 = __importDefault(require("../../session")); -function getAPIInterface() { - return { - signInPOST: async function (input) { - const { options, tenantId, userContext, clientId } = input; - let normalisedClientId = clientId; - if (normalisedClientId === undefined) { - if (options.config.providerConfigs.length > 1) { - throw new Error( - "Should never come here: clientId is undefined and there are multiple providerConfigs" - ); - } - normalisedClientId = options.config.providerConfigs[0].clientId; - } - const providerConfig = await options.recipeImplementation.getProviderConfig({ - clientId: normalisedClientId, - userContext, - }); - let oAuthTokensToUse = {}; - if ("redirectURIInfo" in input && input.redirectURIInfo !== undefined) { - oAuthTokensToUse = await options.recipeImplementation.exchangeAuthCodeForOAuthTokens({ - providerConfig, - redirectURIInfo: input.redirectURIInfo, - userContext, - }); - } else if ("oAuthTokens" in input && input.oAuthTokens !== undefined) { - oAuthTokensToUse = input.oAuthTokens; - } else { - throw Error("should never come here"); - } - const { userId, rawUserInfo } = await options.recipeImplementation.getUserInfo({ - providerConfig, - oAuthTokens: oAuthTokensToUse, - userContext, - }); - const { user, recipeUserId } = await options.recipeImplementation.signIn({ - userId, - tenantId, - rawUserInfo, - oAuthTokens: oAuthTokensToUse, - userContext, - }); - const session = await session_1.default.createNewSession( - options.req, - options.res, - tenantId, - recipeUserId, - undefined, - undefined, - userContext - ); - return { - status: "OK", - user, - session, - oAuthTokens: oAuthTokensToUse, - rawUserInfo, - }; - }, - }; -} -exports.default = getAPIInterface; diff --git a/lib/build/recipe/oauth2client/api/signin.d.ts b/lib/build/recipe/oauth2client/api/signin.d.ts deleted file mode 100644 index 72cd6e46b..000000000 --- a/lib/build/recipe/oauth2client/api/signin.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -// @ts-nocheck -import { APIInterface, APIOptions } from ".."; -import { UserContext } from "../../../types"; -export default function signInAPI( - apiImplementation: APIInterface, - tenantId: string, - options: APIOptions, - userContext: UserContext -): Promise; diff --git a/lib/build/recipe/oauth2client/api/signin.js b/lib/build/recipe/oauth2client/api/signin.js deleted file mode 100644 index 57155dc81..000000000 --- a/lib/build/recipe/oauth2client/api/signin.js +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; -Object.defineProperty(exports, "__esModule", { value: true }); -const error_1 = __importDefault(require("../../../error")); -const utils_1 = require("../../../utils"); -async function signInAPI(apiImplementation, tenantId, options, userContext) { - if (apiImplementation.signInPOST === undefined) { - return false; - } - const bodyParams = await options.req.getJSONBody(); - let redirectURIInfo; - let oAuthTokens; - if (bodyParams.clientId === undefined && options.config.providerConfigs.length > 1) { - throw new error_1.default({ - type: error_1.default.BAD_INPUT_ERROR, - message: "Please provide the clientId in request body", - }); - } - if (bodyParams.redirectURIInfo !== undefined) { - if (bodyParams.redirectURIInfo.redirectURI === undefined) { - throw new error_1.default({ - type: error_1.default.BAD_INPUT_ERROR, - message: "Please provide the redirectURI in request body", - }); - } - redirectURIInfo = bodyParams.redirectURIInfo; - } else if (bodyParams.oAuthTokens !== undefined) { - oAuthTokens = bodyParams.oAuthTokens; - } else { - throw new error_1.default({ - type: error_1.default.BAD_INPUT_ERROR, - message: "Please provide one of redirectURIInfo or oAuthTokens in the request body", - }); - } - let result = await apiImplementation.signInPOST({ - tenantId, - clientId: bodyParams.clientId, - redirectURIInfo, - oAuthTokens, - options, - userContext, - }); - if (result.status === "OK") { - utils_1.send200Response( - options.res, - Object.assign( - { status: result.status }, - utils_1.getBackwardsCompatibleUserInfo(options.req, result, userContext) - ) - ); - } else { - utils_1.send200Response(options.res, result); - } - return true; -} -exports.default = signInAPI; diff --git a/lib/build/recipe/oauth2client/constants.d.ts b/lib/build/recipe/oauth2client/constants.d.ts deleted file mode 100644 index 1fb91e760..000000000 --- a/lib/build/recipe/oauth2client/constants.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @ts-nocheck -export declare const SIGN_IN_API = "/oauth/client/signin"; diff --git a/lib/build/recipe/oauth2client/constants.js b/lib/build/recipe/oauth2client/constants.js deleted file mode 100644 index 4f42a6cb4..000000000 --- a/lib/build/recipe/oauth2client/constants.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SIGN_IN_API = void 0; -exports.SIGN_IN_API = "/oauth/client/signin"; diff --git a/lib/build/recipe/oauth2client/index.d.ts b/lib/build/recipe/oauth2client/index.d.ts deleted file mode 100644 index ff01c3827..000000000 --- a/lib/build/recipe/oauth2client/index.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -// @ts-nocheck -import Recipe from "./recipe"; -import { RecipeInterface, APIInterface, APIOptions, OAuthTokens } from "./types"; -export default class Wrapper { - static init: typeof Recipe.init; - static exchangeAuthCodeForOAuthTokens( - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string | undefined; - }, - clientId?: string, - userContext?: Record - ): Promise; - static getUserInfo( - oAuthTokens: OAuthTokens, - userContext?: Record - ): Promise; -} -export declare let init: typeof Recipe.init; -export declare let exchangeAuthCodeForOAuthTokens: typeof Wrapper.exchangeAuthCodeForOAuthTokens; -export declare let getUserInfo: typeof Wrapper.getUserInfo; -export type { RecipeInterface, APIInterface, APIOptions }; diff --git a/lib/build/recipe/oauth2client/index.js b/lib/build/recipe/oauth2client/index.js deleted file mode 100644 index e24e2d323..000000000 --- a/lib/build/recipe/oauth2client/index.js +++ /dev/null @@ -1,70 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getUserInfo = exports.exchangeAuthCodeForOAuthTokens = exports.init = void 0; -const utils_1 = require("../../utils"); -const jwt_1 = require("../session/jwt"); -const recipe_1 = __importDefault(require("./recipe")); -class Wrapper { - static async exchangeAuthCodeForOAuthTokens(redirectURIInfo, clientId, userContext) { - let normalisedClientId = clientId; - const instance = recipe_1.default.getInstanceOrThrowError(); - const recipeInterfaceImpl = instance.recipeInterfaceImpl; - const normalisedUserContext = utils_1.getUserContext(userContext); - if (normalisedClientId === undefined) { - if (instance.config.providerConfigs.length > 1) { - throw new Error("clientId is required if there are more than one provider configs defined"); - } - normalisedClientId = instance.config.providerConfigs[0].clientId; - } - const providerConfig = await recipeInterfaceImpl.getProviderConfig({ - clientId: normalisedClientId, - userContext: normalisedUserContext, - }); - return await recipeInterfaceImpl.exchangeAuthCodeForOAuthTokens({ - providerConfig, - redirectURIInfo, - userContext: normalisedUserContext, - }); - } - static async getUserInfo(oAuthTokens, userContext) { - const recipeInterfaceImpl = recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl; - const normalisedUserContext = utils_1.getUserContext(userContext); - if (oAuthTokens.access_token === undefined) { - throw new Error("access_token is required to get user info"); - } - const preparseJWTInfo = jwt_1.parseJWTWithoutSignatureVerification(oAuthTokens.access_token); - const providerConfig = await recipeInterfaceImpl.getProviderConfig({ - clientId: preparseJWTInfo.payload.client_id, - userContext: normalisedUserContext, - }); - return await recipe_1.default.getInstanceOrThrowError().recipeInterfaceImpl.getUserInfo({ - providerConfig, - oAuthTokens, - userContext: normalisedUserContext, - }); - } -} -exports.default = Wrapper; -Wrapper.init = recipe_1.default.init; -exports.init = Wrapper.init; -exports.exchangeAuthCodeForOAuthTokens = Wrapper.exchangeAuthCodeForOAuthTokens; -exports.getUserInfo = Wrapper.getUserInfo; diff --git a/lib/build/recipe/oauth2client/recipe.d.ts b/lib/build/recipe/oauth2client/recipe.d.ts deleted file mode 100644 index 180227169..000000000 --- a/lib/build/recipe/oauth2client/recipe.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -// @ts-nocheck -import RecipeModule from "../../recipeModule"; -import { NormalisedAppinfo, APIHandled, RecipeListFunction, HTTPMethod, UserContext } from "../../types"; -import { TypeInput, TypeNormalisedInput, RecipeInterface, APIInterface } from "./types"; -import STError from "../../error"; -import NormalisedURLPath from "../../normalisedURLPath"; -import type { BaseRequest, BaseResponse } from "../../framework"; -export default class Recipe extends RecipeModule { - private static instance; - static RECIPE_ID: string; - config: TypeNormalisedInput; - recipeInterfaceImpl: RecipeInterface; - apiImpl: APIInterface; - isInServerlessEnv: boolean; - constructor( - recipeId: string, - appInfo: NormalisedAppinfo, - isInServerlessEnv: boolean, - config: TypeInput, - _recipes: {} - ); - static init(config: TypeInput): RecipeListFunction; - static getInstanceOrThrowError(): Recipe; - static reset(): void; - getAPIsHandled: () => APIHandled[]; - handleAPIRequest: ( - id: string, - tenantId: string, - req: BaseRequest, - res: BaseResponse, - _path: NormalisedURLPath, - _method: HTTPMethod, - userContext: UserContext - ) => Promise; - handleError: (err: STError, _request: BaseRequest, _response: BaseResponse) => Promise; - getAllCORSHeaders: () => string[]; - isErrorFromThisRecipe: (err: any) => err is STError; -} diff --git a/lib/build/recipe/oauth2client/recipe.js b/lib/build/recipe/oauth2client/recipe.js deleted file mode 100644 index daf6b07f3..000000000 --- a/lib/build/recipe/oauth2client/recipe.js +++ /dev/null @@ -1,107 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; -Object.defineProperty(exports, "__esModule", { value: true }); -const recipeModule_1 = __importDefault(require("../../recipeModule")); -const utils_1 = require("./utils"); -const error_1 = __importDefault(require("../../error")); -const constants_1 = require("./constants"); -const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); -const signin_1 = __importDefault(require("./api/signin")); -const recipeImplementation_1 = __importDefault(require("./recipeImplementation")); -const implementation_1 = __importDefault(require("./api/implementation")); -const querier_1 = require("../../querier"); -const supertokens_js_override_1 = __importDefault(require("supertokens-js-override")); -class Recipe extends recipeModule_1.default { - constructor(recipeId, appInfo, isInServerlessEnv, config, _recipes) { - super(recipeId, appInfo); - this.getAPIsHandled = () => { - return [ - { - method: "post", - pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.SIGN_IN_API), - id: constants_1.SIGN_IN_API, - disabled: this.apiImpl.signInPOST === undefined, - }, - ]; - }; - this.handleAPIRequest = async (id, tenantId, req, res, _path, _method, userContext) => { - let options = { - config: this.config, - recipeId: this.getRecipeId(), - isInServerlessEnv: this.isInServerlessEnv, - recipeImplementation: this.recipeInterfaceImpl, - req, - res, - appInfo: this.getAppInfo(), - }; - if (id === constants_1.SIGN_IN_API) { - return await signin_1.default(this.apiImpl, tenantId, options, userContext); - } - return false; - }; - this.handleError = async (err, _request, _response) => { - throw err; - }; - this.getAllCORSHeaders = () => { - return []; - }; - this.isErrorFromThisRecipe = (err) => { - return error_1.default.isErrorFromSuperTokens(err) && err.fromRecipe === Recipe.RECIPE_ID; - }; - this.config = utils_1.validateAndNormaliseUserInput(appInfo, config); - this.isInServerlessEnv = isInServerlessEnv; - { - let builder = new supertokens_js_override_1.default( - recipeImplementation_1.default(querier_1.Querier.getNewInstanceOrThrowError(recipeId), this.config) - ); - this.recipeInterfaceImpl = builder.override(this.config.override.functions).build(); - } - { - let builder = new supertokens_js_override_1.default(implementation_1.default()); - this.apiImpl = builder.override(this.config.override.apis).build(); - } - } - static init(config) { - return (appInfo, isInServerlessEnv) => { - if (Recipe.instance === undefined) { - Recipe.instance = new Recipe(Recipe.RECIPE_ID, appInfo, isInServerlessEnv, config, {}); - return Recipe.instance; - } else { - throw new Error("OAuth2Client recipe has already been initialised. Please check your code for bugs."); - } - }; - } - static getInstanceOrThrowError() { - if (Recipe.instance !== undefined) { - return Recipe.instance; - } - throw new Error("Initialisation not done. Did you forget to call the OAuth2Client.init function?"); - } - static reset() { - if (process.env.TEST_MODE !== "testing") { - throw new Error("calling testing function in non testing env"); - } - Recipe.instance = undefined; - } -} -exports.default = Recipe; -Recipe.instance = undefined; -Recipe.RECIPE_ID = "oauth2client"; diff --git a/lib/build/recipe/oauth2client/recipeImplementation.d.ts b/lib/build/recipe/oauth2client/recipeImplementation.d.ts deleted file mode 100644 index 24599a2c7..000000000 --- a/lib/build/recipe/oauth2client/recipeImplementation.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-nocheck -import { RecipeInterface, TypeNormalisedInput } from "./types"; -import { Querier } from "../../querier"; -export default function getRecipeImplementation(_querier: Querier, config: TypeNormalisedInput): RecipeInterface; diff --git a/lib/build/recipe/oauth2client/recipeImplementation.js b/lib/build/recipe/oauth2client/recipeImplementation.js deleted file mode 100644 index 351fa3a45..000000000 --- a/lib/build/recipe/oauth2client/recipeImplementation.js +++ /dev/null @@ -1,142 +0,0 @@ -"use strict"; -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; -Object.defineProperty(exports, "__esModule", { value: true }); -const recipeUserId_1 = __importDefault(require("../../recipeUserId")); -const thirdpartyUtils_1 = require("../../thirdpartyUtils"); -const __1 = require("../.."); -const logger_1 = require("../../logger"); -const jose_1 = require("jose"); -function getRecipeImplementation(_querier, config) { - let providerConfigsWithOIDCInfo = {}; - return { - signIn: async function ({ userId, tenantId, userContext, oAuthTokens, rawUserInfo }) { - const user = await __1.getUser(userId, userContext); - if (user === undefined) { - throw new Error(`Failed to getUser from the userId ${userId} in the ${tenantId} tenant`); - } - return { - status: "OK", - user, - recipeUserId: new recipeUserId_1.default(userId), - oAuthTokens, - rawUserInfo, - }; - }, - getProviderConfig: async function ({ clientId }) { - if (providerConfigsWithOIDCInfo[clientId] !== undefined) { - return providerConfigsWithOIDCInfo[clientId]; - } - const providerConfig = config.providerConfigs.find( - (providerConfig) => providerConfig.clientId === clientId - ); - const oidcInfo = await thirdpartyUtils_1.getOIDCDiscoveryInfo(providerConfig.oidcDiscoveryEndpoint); - if (oidcInfo.authorization_endpoint === undefined) { - throw new Error("Failed to authorization_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.token_endpoint === undefined) { - throw new Error("Failed to token_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.userinfo_endpoint === undefined) { - throw new Error("Failed to userinfo_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.jwks_uri === undefined) { - throw new Error("Failed to jwks_uri from the oidcDiscoveryEndpoint."); - } - providerConfigsWithOIDCInfo[clientId] = Object.assign(Object.assign({}, providerConfig), { - authorizationEndpoint: oidcInfo.authorization_endpoint, - tokenEndpoint: oidcInfo.token_endpoint, - userInfoEndpoint: oidcInfo.userinfo_endpoint, - jwksURI: oidcInfo.jwks_uri, - }); - return providerConfigsWithOIDCInfo[clientId]; - }, - exchangeAuthCodeForOAuthTokens: async function ({ providerConfig, redirectURIInfo }) { - if (providerConfig.tokenEndpoint === undefined) { - throw new Error("OAuth2Client provider's tokenEndpoint is not configured."); - } - const tokenAPIURL = providerConfig.tokenEndpoint; - const accessTokenAPIParams = { - client_id: providerConfig.clientId, - redirect_uri: redirectURIInfo.redirectURI, - code: redirectURIInfo.redirectURIQueryParams["code"], - grant_type: "authorization_code", - }; - if (providerConfig.clientSecret !== undefined) { - accessTokenAPIParams["client_secret"] = providerConfig.clientSecret; - } - if (redirectURIInfo.pkceCodeVerifier !== undefined) { - accessTokenAPIParams["code_verifier"] = redirectURIInfo.pkceCodeVerifier; - } - const tokenResponse = await thirdpartyUtils_1.doPostRequest(tokenAPIURL, accessTokenAPIParams); - if (tokenResponse.status >= 400) { - logger_1.logDebugMessage( - `Received response with status ${tokenResponse.status} and body ${tokenResponse.stringResponse}` - ); - throw new Error( - `Received response with status ${tokenResponse.status} and body ${tokenResponse.stringResponse}` - ); - } - return tokenResponse.jsonResponse; - }, - getUserInfo: async function ({ providerConfig, oAuthTokens }) { - var _a, _b; - let jwks; - const accessToken = oAuthTokens["access_token"]; - const idToken = oAuthTokens["id_token"]; - let rawUserInfo = { - fromUserInfoAPI: {}, - fromIdTokenPayload: {}, - }; - if (idToken && providerConfig.jwksURI !== undefined) { - if (jwks === undefined) { - jwks = jose_1.createRemoteJWKSet(new URL(providerConfig.jwksURI)); - } - rawUserInfo.fromIdTokenPayload = await thirdpartyUtils_1.verifyIdTokenFromJWKSEndpointAndGetPayload( - idToken, - jwks, - { - audience: providerConfig.clientId, - } - ); - } - if (accessToken && providerConfig.userInfoEndpoint !== undefined) { - const headers = { - Authorization: "Bearer " + accessToken, - }; - const queryParams = {}; - const userInfoFromAccessToken = await thirdpartyUtils_1.doGetRequest( - providerConfig.userInfoEndpoint, - queryParams, - headers - ); - if (userInfoFromAccessToken.status >= 400) { - logger_1.logDebugMessage( - `Received response with status ${userInfoFromAccessToken.status} and body ${userInfoFromAccessToken.stringResponse}` - ); - throw new Error( - `Received response with status ${userInfoFromAccessToken.status} and body ${userInfoFromAccessToken.stringResponse}` - ); - } - rawUserInfo.fromUserInfoAPI = userInfoFromAccessToken.jsonResponse; - } - let userId = undefined; - if (((_a = rawUserInfo.fromIdTokenPayload) === null || _a === void 0 ? void 0 : _a.sub) !== undefined) { - userId = rawUserInfo.fromIdTokenPayload["sub"]; - } else if (((_b = rawUserInfo.fromUserInfoAPI) === null || _b === void 0 ? void 0 : _b.sub) !== undefined) { - userId = rawUserInfo.fromUserInfoAPI["sub"]; - } - if (userId === undefined) { - throw new Error(`Failed to get userId from both the idToken and userInfo endpoint.`); - } - return { - userId, - rawUserInfo, - }; - }, - }; -} -exports.default = getRecipeImplementation; diff --git a/lib/build/recipe/oauth2client/types.d.ts b/lib/build/recipe/oauth2client/types.d.ts deleted file mode 100644 index 84fab1847..000000000 --- a/lib/build/recipe/oauth2client/types.d.ts +++ /dev/null @@ -1,155 +0,0 @@ -// @ts-nocheck -import type { BaseRequest, BaseResponse } from "../../framework"; -import { NormalisedAppinfo, UserContext } from "../../types"; -import OverrideableBuilder from "supertokens-js-override"; -import { SessionContainerInterface } from "../session/types"; -import { GeneralErrorResponse, User } from "../../types"; -import RecipeUserId from "../../recipeUserId"; -export declare type UserInfo = { - userId: string; - rawUserInfo: { - fromIdTokenPayload?: { - [key: string]: any; - }; - fromUserInfoAPI?: { - [key: string]: any; - }; - }; -}; -export declare type ProviderConfigInput = { - clientId: string; - clientSecret?: string; - oidcDiscoveryEndpoint: string; -}; -export declare type ProviderConfigWithOIDCInfo = ProviderConfigInput & { - authorizationEndpoint: string; - tokenEndpoint: string; - userInfoEndpoint: string; - jwksURI: string; -}; -export declare type OAuthTokens = { - access_token?: string; - id_token?: string; -}; -export declare type OAuthTokenResponse = { - access_token: string; - id_token?: string; - refresh_token?: string; - expires_in: number; - scope?: string; - token_type: string; -}; -export declare type TypeInput = { - providerConfigs: ProviderConfigInput[]; - override?: { - functions?: ( - originalImplementation: RecipeInterface, - builder?: OverrideableBuilder - ) => RecipeInterface; - apis?: (originalImplementation: APIInterface, builder?: OverrideableBuilder) => APIInterface; - }; -}; -export declare type TypeNormalisedInput = { - providerConfigs: ProviderConfigInput[]; - override: { - functions: ( - originalImplementation: RecipeInterface, - builder?: OverrideableBuilder - ) => RecipeInterface; - apis: (originalImplementation: APIInterface, builder?: OverrideableBuilder) => APIInterface; - }; -}; -export declare type RecipeInterface = { - getProviderConfig(input: { clientId: string; userContext: UserContext }): Promise; - signIn(input: { - userId: string; - oAuthTokens: OAuthTokens; - rawUserInfo: { - fromIdTokenPayload?: { - [key: string]: any; - }; - fromUserInfoAPI?: { - [key: string]: any; - }; - }; - tenantId: string; - userContext: UserContext; - }): Promise<{ - status: "OK"; - recipeUserId: RecipeUserId; - user: User; - oAuthTokens: OAuthTokens; - rawUserInfo: { - fromIdTokenPayload?: { - [key: string]: any; - }; - fromUserInfoAPI?: { - [key: string]: any; - }; - }; - }>; - exchangeAuthCodeForOAuthTokens(input: { - providerConfig: ProviderConfigWithOIDCInfo; - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string | undefined; - }; - userContext: UserContext; - }): Promise; - getUserInfo(input: { - providerConfig: ProviderConfigWithOIDCInfo; - oAuthTokens: OAuthTokens; - userContext: UserContext; - }): Promise; -}; -export declare type APIOptions = { - recipeImplementation: RecipeInterface; - config: TypeNormalisedInput; - recipeId: string; - isInServerlessEnv: boolean; - req: BaseRequest; - res: BaseResponse; - appInfo: NormalisedAppinfo; -}; -export declare type APIInterface = { - signInPOST: ( - input: { - tenantId: string; - clientId?: string; - options: APIOptions; - userContext: UserContext; - } & ( - | { - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string; - }; - } - | { - oAuthTokens: { - [key: string]: any; - }; - } - ) - ) => Promise< - | { - status: "OK"; - user: User; - session: SessionContainerInterface; - oAuthTokens: { - [key: string]: any; - }; - rawUserInfo: { - fromIdTokenPayload?: { - [key: string]: any; - }; - fromUserInfoAPI?: { - [key: string]: any; - }; - }; - } - | GeneralErrorResponse - >; -}; diff --git a/lib/build/recipe/oauth2client/types.js b/lib/build/recipe/oauth2client/types.js deleted file mode 100644 index 9f1237319..000000000 --- a/lib/build/recipe/oauth2client/types.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/build/recipe/oauth2client/utils.d.ts b/lib/build/recipe/oauth2client/utils.d.ts deleted file mode 100644 index 6a930e641..000000000 --- a/lib/build/recipe/oauth2client/utils.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-nocheck -import { NormalisedAppinfo } from "../../types"; -import { TypeInput, TypeNormalisedInput } from "./types"; -export declare function validateAndNormaliseUserInput( - _appInfo: NormalisedAppinfo, - config: TypeInput -): TypeNormalisedInput; diff --git a/lib/build/recipe/oauth2client/utils.js b/lib/build/recipe/oauth2client/utils.js deleted file mode 100644 index 25e759254..000000000 --- a/lib/build/recipe/oauth2client/utils.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.validateAndNormaliseUserInput = void 0; -function validateAndNormaliseUserInput(_appInfo, config) { - if (config === undefined || config.providerConfigs === undefined) { - throw new Error("Please pass providerConfigs argument in the OAuth2Client recipe."); - } - if (config.providerConfigs.some((providerConfig) => providerConfig.clientId === undefined)) { - throw new Error("Please pass clientId for all providerConfigs."); - } - if (!config.providerConfigs.every((providerConfig) => providerConfig.clientId.startsWith("stcl_"))) { - throw new Error( - `Only Supertokens OAuth ClientIds are supported in the OAuth2Client recipe. For any other OAuth Clients use the ThirdParty recipe.` - ); - } - if (config.providerConfigs.some((providerConfig) => providerConfig.oidcDiscoveryEndpoint === undefined)) { - throw new Error("Please pass oidcDiscoveryEndpoint for all providerConfigs."); - } - let override = Object.assign( - { - functions: (originalImplementation) => originalImplementation, - apis: (originalImplementation) => originalImplementation, - }, - config === null || config === void 0 ? void 0 : config.override - ); - return { - providerConfigs: config.providerConfigs, - override, - }; -} -exports.validateAndNormaliseUserInput = validateAndNormaliseUserInput; diff --git a/lib/ts/recipe/oauth2client/api/implementation.ts b/lib/ts/recipe/oauth2client/api/implementation.ts deleted file mode 100644 index 613b81937..000000000 --- a/lib/ts/recipe/oauth2client/api/implementation.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { APIInterface } from "../"; -import Session from "../../session"; -import { OAuthTokens } from "../types"; - -export default function getAPIInterface(): APIInterface { - return { - signInPOST: async function (input) { - const { options, tenantId, userContext, clientId } = input; - - let normalisedClientId = clientId; - if (normalisedClientId === undefined) { - if (options.config.providerConfigs.length > 1) { - throw new Error( - "Should never come here: clientId is undefined and there are multiple providerConfigs" - ); - } - - normalisedClientId = options.config.providerConfigs[0].clientId!; - } - const providerConfig = await options.recipeImplementation.getProviderConfig({ - clientId: normalisedClientId, - userContext, - }); - - let oAuthTokensToUse: OAuthTokens = {}; - - if ("redirectURIInfo" in input && input.redirectURIInfo !== undefined) { - oAuthTokensToUse = await options.recipeImplementation.exchangeAuthCodeForOAuthTokens({ - providerConfig, - redirectURIInfo: input.redirectURIInfo, - userContext, - }); - } else if ("oAuthTokens" in input && input.oAuthTokens !== undefined) { - oAuthTokensToUse = input.oAuthTokens; - } else { - throw Error("should never come here"); - } - - const { userId, rawUserInfo } = await options.recipeImplementation.getUserInfo({ - providerConfig, - oAuthTokens: oAuthTokensToUse, - userContext, - }); - - const { user, recipeUserId } = await options.recipeImplementation.signIn({ - userId, - tenantId, - rawUserInfo, - oAuthTokens: oAuthTokensToUse, - userContext, - }); - - const session = await Session.createNewSession( - options.req, - options.res, - tenantId, - recipeUserId, - undefined, - undefined, - userContext - ); - - return { - status: "OK", - user, - session, - oAuthTokens: oAuthTokensToUse, - rawUserInfo, - }; - }, - }; -} diff --git a/lib/ts/recipe/oauth2client/api/signin.ts b/lib/ts/recipe/oauth2client/api/signin.ts deleted file mode 100644 index a4662cf27..000000000 --- a/lib/ts/recipe/oauth2client/api/signin.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -import STError from "../../../error"; -import { getBackwardsCompatibleUserInfo, send200Response } from "../../../utils"; -import { APIInterface, APIOptions } from ".."; -import { UserContext } from "../../../types"; - -export default async function signInAPI( - apiImplementation: APIInterface, - tenantId: string, - options: APIOptions, - userContext: UserContext -): Promise { - if (apiImplementation.signInPOST === undefined) { - return false; - } - - const bodyParams = await options.req.getJSONBody(); - - let redirectURIInfo: - | undefined - | { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string; - }; - let oAuthTokens: any; - - if (bodyParams.clientId === undefined && options.config.providerConfigs.length > 1) { - throw new STError({ - type: STError.BAD_INPUT_ERROR, - message: "Please provide the clientId in request body", - }); - } - - if (bodyParams.redirectURIInfo !== undefined) { - if (bodyParams.redirectURIInfo.redirectURI === undefined) { - throw new STError({ - type: STError.BAD_INPUT_ERROR, - message: "Please provide the redirectURI in request body", - }); - } - redirectURIInfo = bodyParams.redirectURIInfo; - } else if (bodyParams.oAuthTokens !== undefined) { - oAuthTokens = bodyParams.oAuthTokens; - } else { - throw new STError({ - type: STError.BAD_INPUT_ERROR, - message: "Please provide one of redirectURIInfo or oAuthTokens in the request body", - }); - } - - let result = await apiImplementation.signInPOST({ - tenantId, - clientId: bodyParams.clientId, - redirectURIInfo, - oAuthTokens, - options, - userContext, - }); - - if (result.status === "OK") { - send200Response(options.res, { - status: result.status, - ...getBackwardsCompatibleUserInfo(options.req, result, userContext), - }); - } else { - send200Response(options.res, result); - } - return true; -} diff --git a/lib/ts/recipe/oauth2client/constants.ts b/lib/ts/recipe/oauth2client/constants.ts deleted file mode 100644 index 8e45f0567..000000000 --- a/lib/ts/recipe/oauth2client/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -export const SIGN_IN_API = "/oauth/client/signin"; diff --git a/lib/ts/recipe/oauth2client/index.ts b/lib/ts/recipe/oauth2client/index.ts deleted file mode 100644 index 9d175ef1c..000000000 --- a/lib/ts/recipe/oauth2client/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -import { getUserContext } from "../../utils"; -import { parseJWTWithoutSignatureVerification } from "../session/jwt"; -import Recipe from "./recipe"; -import { RecipeInterface, APIInterface, APIOptions, OAuthTokens } from "./types"; - -export default class Wrapper { - static init = Recipe.init; - - static async exchangeAuthCodeForOAuthTokens( - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string | undefined; - }, - clientId?: string, - userContext?: Record - ) { - let normalisedClientId = clientId; - const instance = Recipe.getInstanceOrThrowError(); - const recipeInterfaceImpl = instance.recipeInterfaceImpl; - const normalisedUserContext = getUserContext(userContext); - if (normalisedClientId === undefined) { - if (instance.config.providerConfigs.length > 1) { - throw new Error("clientId is required if there are more than one provider configs defined"); - } - - normalisedClientId = instance.config.providerConfigs[0].clientId!; - } - - const providerConfig = await recipeInterfaceImpl.getProviderConfig({ - clientId: normalisedClientId, - userContext: normalisedUserContext, - }); - return await recipeInterfaceImpl.exchangeAuthCodeForOAuthTokens({ - providerConfig, - redirectURIInfo, - userContext: normalisedUserContext, - }); - } - - static async getUserInfo(oAuthTokens: OAuthTokens, userContext?: Record) { - const recipeInterfaceImpl = Recipe.getInstanceOrThrowError().recipeInterfaceImpl; - const normalisedUserContext = getUserContext(userContext); - if (oAuthTokens.access_token === undefined) { - throw new Error("access_token is required to get user info"); - } - const preparseJWTInfo = parseJWTWithoutSignatureVerification(oAuthTokens.access_token!); - const providerConfig = await recipeInterfaceImpl.getProviderConfig({ - clientId: preparseJWTInfo.payload.client_id, - userContext: normalisedUserContext, - }); - return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.getUserInfo({ - providerConfig, - oAuthTokens, - userContext: normalisedUserContext, - }); - } -} - -export let init = Wrapper.init; - -export let exchangeAuthCodeForOAuthTokens = Wrapper.exchangeAuthCodeForOAuthTokens; - -export let getUserInfo = Wrapper.getUserInfo; - -export type { RecipeInterface, APIInterface, APIOptions }; diff --git a/lib/ts/recipe/oauth2client/recipe.ts b/lib/ts/recipe/oauth2client/recipe.ts deleted file mode 100644 index 7da475c5c..000000000 --- a/lib/ts/recipe/oauth2client/recipe.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -import RecipeModule from "../../recipeModule"; -import { NormalisedAppinfo, APIHandled, RecipeListFunction, HTTPMethod, UserContext } from "../../types"; -import { TypeInput, TypeNormalisedInput, RecipeInterface, APIInterface } from "./types"; -import { validateAndNormaliseUserInput } from "./utils"; -import STError from "../../error"; -import { SIGN_IN_API } from "./constants"; -import NormalisedURLPath from "../../normalisedURLPath"; -import signInAPI from "./api/signin"; -import RecipeImplementation from "./recipeImplementation"; -import APIImplementation from "./api/implementation"; -import { Querier } from "../../querier"; -import type { BaseRequest, BaseResponse } from "../../framework"; -import OverrideableBuilder from "supertokens-js-override"; - -export default class Recipe extends RecipeModule { - private static instance: Recipe | undefined = undefined; - static RECIPE_ID = "oauth2client"; - - config: TypeNormalisedInput; - - recipeInterfaceImpl: RecipeInterface; - - apiImpl: APIInterface; - - isInServerlessEnv: boolean; - - constructor( - recipeId: string, - appInfo: NormalisedAppinfo, - isInServerlessEnv: boolean, - config: TypeInput, - _recipes: {} - ) { - super(recipeId, appInfo); - this.config = validateAndNormaliseUserInput(appInfo, config); - this.isInServerlessEnv = isInServerlessEnv; - - { - let builder = new OverrideableBuilder( - RecipeImplementation(Querier.getNewInstanceOrThrowError(recipeId), this.config) - ); - this.recipeInterfaceImpl = builder.override(this.config.override.functions).build(); - } - { - let builder = new OverrideableBuilder(APIImplementation()); - this.apiImpl = builder.override(this.config.override.apis).build(); - } - } - - static init(config: TypeInput): RecipeListFunction { - return (appInfo, isInServerlessEnv) => { - if (Recipe.instance === undefined) { - Recipe.instance = new Recipe(Recipe.RECIPE_ID, appInfo, isInServerlessEnv, config, {}); - - return Recipe.instance; - } else { - throw new Error("OAuth2Client recipe has already been initialised. Please check your code for bugs."); - } - }; - } - - static getInstanceOrThrowError(): Recipe { - if (Recipe.instance !== undefined) { - return Recipe.instance; - } - throw new Error("Initialisation not done. Did you forget to call the OAuth2Client.init function?"); - } - - static reset() { - if (process.env.TEST_MODE !== "testing") { - throw new Error("calling testing function in non testing env"); - } - Recipe.instance = undefined; - } - - getAPIsHandled = (): APIHandled[] => { - return [ - { - method: "post", - pathWithoutApiBasePath: new NormalisedURLPath(SIGN_IN_API), - id: SIGN_IN_API, - disabled: this.apiImpl.signInPOST === undefined, - }, - ]; - }; - - handleAPIRequest = async ( - id: string, - tenantId: string, - req: BaseRequest, - res: BaseResponse, - _path: NormalisedURLPath, - _method: HTTPMethod, - userContext: UserContext - ): Promise => { - let options = { - config: this.config, - recipeId: this.getRecipeId(), - isInServerlessEnv: this.isInServerlessEnv, - recipeImplementation: this.recipeInterfaceImpl, - req, - res, - appInfo: this.getAppInfo(), - }; - if (id === SIGN_IN_API) { - return await signInAPI(this.apiImpl, tenantId, options, userContext); - } - return false; - }; - - handleError = async (err: STError, _request: BaseRequest, _response: BaseResponse): Promise => { - throw err; - }; - - getAllCORSHeaders = (): string[] => { - return []; - }; - - isErrorFromThisRecipe = (err: any): err is STError => { - return STError.isErrorFromSuperTokens(err) && err.fromRecipe === Recipe.RECIPE_ID; - }; -} diff --git a/lib/ts/recipe/oauth2client/recipeImplementation.ts b/lib/ts/recipe/oauth2client/recipeImplementation.ts deleted file mode 100644 index 59e90967b..000000000 --- a/lib/ts/recipe/oauth2client/recipeImplementation.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { - OAuthTokenResponse, - OAuthTokens, - ProviderConfigWithOIDCInfo, - RecipeInterface, - TypeNormalisedInput, - UserInfo, -} from "./types"; -import { Querier } from "../../querier"; -import RecipeUserId from "../../recipeUserId"; -import { User as UserType } from "../../types"; -import { - doGetRequest, - doPostRequest, - getOIDCDiscoveryInfo, - verifyIdTokenFromJWKSEndpointAndGetPayload, -} from "../../thirdpartyUtils"; -import { getUser } from "../.."; -import { logDebugMessage } from "../../logger"; -import { JWTVerifyGetKey, createRemoteJWKSet } from "jose"; - -export default function getRecipeImplementation(_querier: Querier, config: TypeNormalisedInput): RecipeInterface { - let providerConfigsWithOIDCInfo: Record = {}; - - return { - signIn: async function ({ - userId, - tenantId, - userContext, - oAuthTokens, - rawUserInfo, - }): Promise<{ - status: "OK"; - user: UserType; - recipeUserId: RecipeUserId; - oAuthTokens: OAuthTokens; - rawUserInfo: { - fromIdTokenPayload?: { [key: string]: any }; - fromUserInfoAPI?: { [key: string]: any }; - }; - }> { - const user = await getUser(userId, userContext); - - if (user === undefined) { - throw new Error(`Failed to getUser from the userId ${userId} in the ${tenantId} tenant`); - } - - return { - status: "OK", - user, - recipeUserId: new RecipeUserId(userId), - oAuthTokens, - rawUserInfo, - }; - }, - getProviderConfig: async function ({ clientId }) { - if (providerConfigsWithOIDCInfo[clientId] !== undefined) { - return providerConfigsWithOIDCInfo[clientId]; - } - const providerConfig = config.providerConfigs.find( - (providerConfig) => providerConfig.clientId === clientId - )!; - const oidcInfo = await getOIDCDiscoveryInfo(providerConfig.oidcDiscoveryEndpoint); - - if (oidcInfo.authorization_endpoint === undefined) { - throw new Error("Failed to authorization_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.token_endpoint === undefined) { - throw new Error("Failed to token_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.userinfo_endpoint === undefined) { - throw new Error("Failed to userinfo_endpoint from the oidcDiscoveryEndpoint."); - } - if (oidcInfo.jwks_uri === undefined) { - throw new Error("Failed to jwks_uri from the oidcDiscoveryEndpoint."); - } - - providerConfigsWithOIDCInfo[clientId] = { - ...providerConfig, - authorizationEndpoint: oidcInfo.authorization_endpoint, - tokenEndpoint: oidcInfo.token_endpoint, - userInfoEndpoint: oidcInfo.userinfo_endpoint, - jwksURI: oidcInfo.jwks_uri, - }; - - return providerConfigsWithOIDCInfo[clientId]; - }, - exchangeAuthCodeForOAuthTokens: async function (this: RecipeInterface, { providerConfig, redirectURIInfo }) { - if (providerConfig.tokenEndpoint === undefined) { - throw new Error("OAuth2Client provider's tokenEndpoint is not configured."); - } - const tokenAPIURL = providerConfig.tokenEndpoint; - const accessTokenAPIParams: { [key: string]: string } = { - client_id: providerConfig.clientId, - redirect_uri: redirectURIInfo.redirectURI, - code: redirectURIInfo.redirectURIQueryParams["code"], - grant_type: "authorization_code", - }; - if (providerConfig.clientSecret !== undefined) { - accessTokenAPIParams["client_secret"] = providerConfig.clientSecret; - } - if (redirectURIInfo.pkceCodeVerifier !== undefined) { - accessTokenAPIParams["code_verifier"] = redirectURIInfo.pkceCodeVerifier; - } - - const tokenResponse = await doPostRequest(tokenAPIURL, accessTokenAPIParams); - - if (tokenResponse.status >= 400) { - logDebugMessage( - `Received response with status ${tokenResponse.status} and body ${tokenResponse.stringResponse}` - ); - throw new Error( - `Received response with status ${tokenResponse.status} and body ${tokenResponse.stringResponse}` - ); - } - - return tokenResponse.jsonResponse as OAuthTokenResponse; - }, - getUserInfo: async function ({ providerConfig, oAuthTokens }): Promise { - let jwks: JWTVerifyGetKey | undefined; - - const accessToken = oAuthTokens["access_token"]; - const idToken = oAuthTokens["id_token"]; - - let rawUserInfo: { - fromUserInfoAPI: any; - fromIdTokenPayload: any; - } = { - fromUserInfoAPI: {}, - fromIdTokenPayload: {}, - }; - - if (idToken && providerConfig.jwksURI !== undefined) { - if (jwks === undefined) { - jwks = createRemoteJWKSet(new URL(providerConfig.jwksURI)); - } - - rawUserInfo.fromIdTokenPayload = await verifyIdTokenFromJWKSEndpointAndGetPayload(idToken, jwks, { - audience: providerConfig.clientId, - }); - } - - if (accessToken && providerConfig.userInfoEndpoint !== undefined) { - const headers: { [key: string]: string } = { - Authorization: "Bearer " + accessToken, - }; - const queryParams: { [key: string]: string } = {}; - - const userInfoFromAccessToken = await doGetRequest( - providerConfig.userInfoEndpoint, - queryParams, - headers - ); - - if (userInfoFromAccessToken.status >= 400) { - logDebugMessage( - `Received response with status ${userInfoFromAccessToken.status} and body ${userInfoFromAccessToken.stringResponse}` - ); - throw new Error( - `Received response with status ${userInfoFromAccessToken.status} and body ${userInfoFromAccessToken.stringResponse}` - ); - } - - rawUserInfo.fromUserInfoAPI = userInfoFromAccessToken.jsonResponse; - } - - let userId: string | undefined = undefined; - - if (rawUserInfo.fromIdTokenPayload?.sub !== undefined) { - userId = rawUserInfo.fromIdTokenPayload["sub"]; - } else if (rawUserInfo.fromUserInfoAPI?.sub !== undefined) { - userId = rawUserInfo.fromUserInfoAPI["sub"]; - } - - if (userId === undefined) { - throw new Error(`Failed to get userId from both the idToken and userInfo endpoint.`); - } - - return { - userId, - rawUserInfo, - }; - }, - }; -} diff --git a/lib/ts/recipe/oauth2client/types.ts b/lib/ts/recipe/oauth2client/types.ts deleted file mode 100644 index 795e39086..000000000 --- a/lib/ts/recipe/oauth2client/types.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -import type { BaseRequest, BaseResponse } from "../../framework"; -import { NormalisedAppinfo, UserContext } from "../../types"; -import OverrideableBuilder from "supertokens-js-override"; -import { SessionContainerInterface } from "../session/types"; -import { GeneralErrorResponse, User } from "../../types"; -import RecipeUserId from "../../recipeUserId"; - -export type UserInfo = { - userId: string; - rawUserInfo: { fromIdTokenPayload?: { [key: string]: any }; fromUserInfoAPI?: { [key: string]: any } }; -}; - -export type ProviderConfigInput = { - clientId: string; - clientSecret?: string; - oidcDiscoveryEndpoint: string; -}; - -export type ProviderConfigWithOIDCInfo = ProviderConfigInput & { - authorizationEndpoint: string; - tokenEndpoint: string; - userInfoEndpoint: string; - jwksURI: string; -}; - -export type OAuthTokens = { - access_token?: string; - id_token?: string; -}; - -export type OAuthTokenResponse = { - access_token: string; - id_token?: string; - refresh_token?: string; - expires_in: number; - scope?: string; - token_type: string; -}; - -export type TypeInput = { - providerConfigs: ProviderConfigInput[]; - override?: { - functions?: ( - originalImplementation: RecipeInterface, - builder?: OverrideableBuilder - ) => RecipeInterface; - apis?: (originalImplementation: APIInterface, builder?: OverrideableBuilder) => APIInterface; - }; -}; - -export type TypeNormalisedInput = { - providerConfigs: ProviderConfigInput[]; - override: { - functions: ( - originalImplementation: RecipeInterface, - builder?: OverrideableBuilder - ) => RecipeInterface; - apis: (originalImplementation: APIInterface, builder?: OverrideableBuilder) => APIInterface; - }; -}; - -export type RecipeInterface = { - getProviderConfig(input: { clientId: string; userContext: UserContext }): Promise; - - signIn(input: { - userId: string; - oAuthTokens: OAuthTokens; - rawUserInfo: { - fromIdTokenPayload?: { [key: string]: any }; - fromUserInfoAPI?: { [key: string]: any }; - }; - tenantId: string; - userContext: UserContext; - }): Promise<{ - status: "OK"; - recipeUserId: RecipeUserId; - user: User; - oAuthTokens: OAuthTokens; - rawUserInfo: { - fromIdTokenPayload?: { [key: string]: any }; - fromUserInfoAPI?: { [key: string]: any }; - }; - }>; - exchangeAuthCodeForOAuthTokens(input: { - providerConfig: ProviderConfigWithOIDCInfo; - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string | undefined; - }; - userContext: UserContext; - }): Promise; - getUserInfo(input: { - providerConfig: ProviderConfigWithOIDCInfo; - oAuthTokens: OAuthTokens; - userContext: UserContext; - }): Promise; -}; - -export type APIOptions = { - recipeImplementation: RecipeInterface; - config: TypeNormalisedInput; - recipeId: string; - isInServerlessEnv: boolean; - req: BaseRequest; - res: BaseResponse; - appInfo: NormalisedAppinfo; -}; - -export type APIInterface = { - signInPOST: ( - input: { - tenantId: string; - clientId?: string; - options: APIOptions; - userContext: UserContext; - } & ( - | { - redirectURIInfo: { - redirectURI: string; - redirectURIQueryParams: any; - pkceCodeVerifier?: string; - }; - } - | { - oAuthTokens: { [key: string]: any }; - } - ) - ) => Promise< - | { - status: "OK"; - user: User; - session: SessionContainerInterface; - oAuthTokens: { [key: string]: any }; - rawUserInfo: { - fromIdTokenPayload?: { [key: string]: any }; - fromUserInfoAPI?: { [key: string]: any }; - }; - } - | GeneralErrorResponse - >; -}; diff --git a/lib/ts/recipe/oauth2client/utils.ts b/lib/ts/recipe/oauth2client/utils.ts deleted file mode 100644 index 54c990144..000000000 --- a/lib/ts/recipe/oauth2client/utils.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -import { NormalisedAppinfo } from "../../types"; -import { TypeInput, TypeNormalisedInput, RecipeInterface, APIInterface } from "./types"; - -export function validateAndNormaliseUserInput(_appInfo: NormalisedAppinfo, config: TypeInput): TypeNormalisedInput { - if (config === undefined || config.providerConfigs === undefined) { - throw new Error("Please pass providerConfigs argument in the OAuth2Client recipe."); - } - - if (config.providerConfigs.some((providerConfig) => providerConfig.clientId === undefined)) { - throw new Error("Please pass clientId for all providerConfigs."); - } - - if (!config.providerConfigs.every((providerConfig) => providerConfig.clientId.startsWith("stcl_"))) { - throw new Error( - `Only Supertokens OAuth ClientIds are supported in the OAuth2Client recipe. For any other OAuth Clients use the ThirdParty recipe.` - ); - } - - if (config.providerConfigs.some((providerConfig) => providerConfig.oidcDiscoveryEndpoint === undefined)) { - throw new Error("Please pass oidcDiscoveryEndpoint for all providerConfigs."); - } - - let override = { - functions: (originalImplementation: RecipeInterface) => originalImplementation, - apis: (originalImplementation: APIInterface) => originalImplementation, - ...config?.override, - }; - - return { - providerConfigs: config.providerConfigs, - override, - }; -} diff --git a/recipe/oauth2client/index.d.ts b/recipe/oauth2client/index.d.ts deleted file mode 100644 index 89f4241f8..000000000 --- a/recipe/oauth2client/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "../../lib/build/recipe/oauth2client"; -/** - * 'export *' does not re-export a default. - * import NextJS from "supertokens-node/nextjs"; - * the above import statement won't be possible unless either - * - user add "esModuleInterop": true in their tsconfig.json file - * - we do the following change: - */ -import * as _default from "../../lib/build/recipe/oauth2client"; -export default _default; diff --git a/recipe/oauth2client/index.js b/recipe/oauth2client/index.js deleted file mode 100644 index f1b31d6db..000000000 --- a/recipe/oauth2client/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -exports.__esModule = true; -__export(require("../../lib/build/recipe/oauth2client")); diff --git a/recipe/oauth2client/types/index.d.ts b/recipe/oauth2client/types/index.d.ts deleted file mode 100644 index e475d4576..000000000 --- a/recipe/oauth2client/types/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "../../../lib/build/recipe/oauth2client/types"; -/** - * 'export *' does not re-export a default. - * import NextJS from "supertokens-node/nextjs"; - * the above import statement won't be possible unless either - * - user add "esModuleInterop": true in their tsconfig.json file - * - we do the following change: - */ -import * as _default from "../../../lib/build/recipe/oauth2client/types"; -export default _default; diff --git a/recipe/oauth2client/types/index.js b/recipe/oauth2client/types/index.js deleted file mode 100644 index 01b5c40c6..000000000 --- a/recipe/oauth2client/types/index.js +++ /dev/null @@ -1,6 +0,0 @@ -"use strict"; -function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; -} -exports.__esModule = true; -__export(require("../../../lib/build/recipe/oauth2client/types")); diff --git a/test/test-server/src/index.ts b/test/test-server/src/index.ts index e41f2f41e..bc12e3139 100644 --- a/test/test-server/src/index.ts +++ b/test/test-server/src/index.ts @@ -21,8 +21,6 @@ import { TypeInput as MFATypeInput } from "../../../lib/build/recipe/multifactor import TOTPRecipe from "../../../lib/build/recipe/totp/recipe"; import OAuth2ProviderRecipe from "../../../lib/build/recipe/oauth2provider/recipe"; import { TypeInput as OAuth2ProviderTypeInput } from "../../../lib/build/recipe/oauth2provider/types"; -import OAuth2ClientRecipe from "../../../lib/build/recipe/oauth2client/recipe"; -import { TypeInput as OAuth2ClientTypeInput } from "../../../lib/build/recipe/oauth2client/types"; import { TypeInput as OpenIdRecipeTypeInput } from "../../../lib/build/recipe/openid/types"; import UserMetadataRecipe from "../../../lib/build/recipe/usermetadata/recipe"; import SuperTokensRecipe from "../../../lib/build/supertokens"; @@ -38,7 +36,6 @@ import { verifySession } from "../../../recipe/session/framework/express"; import ThirdParty from "../../../recipe/thirdparty"; import TOTP from "../../../recipe/totp"; import OAuth2Provider from "../../../recipe/oauth2provider"; -import OAuth2Client from "../../../recipe/oauth2client"; import accountlinkingRoutes from "./accountlinking"; import emailpasswordRoutes from "./emailpassword"; import emailverificationRoutes from "./emailverification"; @@ -100,7 +97,6 @@ function STReset() { MultiFactorAuthRecipe.reset(); TOTPRecipe.reset(); OAuth2ProviderRecipe.reset(); - OAuth2ClientRecipe.reset(); SuperTokensRecipe.reset(); DashboardRecipe.reset(); } @@ -325,24 +321,6 @@ function initST(config: any) { } recipeList.push(OAuth2Provider.init(initConfig)); } - if (recipe.recipeId === "oauth2client") { - let initConfig: OAuth2ClientTypeInput = { - ...config, - }; - if (initConfig.override?.functions) { - initConfig.override = { - ...initConfig.override, - functions: getFunc(`${initConfig.override.functions}`), - }; - } - if (initConfig.override?.apis) { - initConfig.override = { - ...initConfig.override, - apis: getFunc(`${initConfig.override.apis}`), - }; - } - recipeList.push(OAuth2Client.init(initConfig)); - } }); init.recipeList = recipeList; From 41432573021d6f53dc8fe273a529561a1e783baa Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 18 Oct 2024 12:13:07 +0200 Subject: [PATCH 6/7] chore: update changelog + remove unused prop from test server --- CHANGELOG.md | 8 ++++++-- test/auth-react-server/index.js | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32eec5d61..b5392a2bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,16 +27,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - By setting this to true you can enable MFA flows (trying to connect to the session user) - If set to false, the sign-in/up will be considered a first-factor - Changed APIs: + - `EmailPassword.signInPOST` + - `EmailPassword.signUpPOST` - `ThirdParty.signInUpPOST` - `Passwordless.createCodePOST` - `Passwordless.consumeCodePOST` - - `Passwordless.consumeCodePOST` + - `Passwordless.resendCodePOST` - Changed functions: + - `EmailPassword.signIn` + - `EmailPassword.signUp` - `ThirdParty.signInUp` - `ThirdPary.manuallyCreateOrUpdateUser` - `Passwordless.createCode` - `Passwordless.consumeCode` -- We no longer try to load the session if `shouldTryLinkingWithSessionUser` is set to false and overwriteSessionDuringSignInUp is set to true or left as the default value. +- We no longer try to load the session if `shouldTryLinkingWithSessionUser` is set to false. - Changed the return type of `getOpenIdConfiguration` and `getOpenIdDiscoveryConfigurationGET`, and added the following props: - authorization_endpoint - token_endpoint diff --git a/test/auth-react-server/index.js b/test/auth-react-server/index.js index 51ad8fa28..ee3692521 100644 --- a/test/auth-react-server/index.js +++ b/test/auth-react-server/index.js @@ -761,7 +761,6 @@ function initST({ passwordlessConfig } = {}) { [ "session", Session.init({ - overwriteSessionDuringSignIn: true, override: { apis: function (originalImplementation) { return { From 9c5914825a44caeab4e7ba0837305b802ccff8c3 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 21 Oct 2024 02:04:34 +0200 Subject: [PATCH 7/7] refactor: small consistency improvements --- .circleci/forceRunCI.sh | 4 ++-- lib/build/recipe/oauth2provider/api/loginInfo.js | 11 +++++++++-- lib/ts/recipe/oauth2provider/api/loginInfo.ts | 12 ++++++++++-- lib/ts/recipe/oauth2provider/types.ts | 3 +-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.circleci/forceRunCI.sh b/.circleci/forceRunCI.sh index f8088f23e..41e2a62e9 100755 --- a/.circleci/forceRunCI.sh +++ b/.circleci/forceRunCI.sh @@ -4,9 +4,9 @@ branch=`git rev-parse --abbrev-ref HEAD` cdiCoreMap='{ "5.2": "feat/oauth/remaining-changes" }' cdiPluginInterfaceMap='{ "5.2": "feat/oauth/remaining-changes" }' -fdiNodeMap='{ "3.1": "21.0", "4.0": "21.0" }' +fdiNodeMap='{ "3.1": "feat/add_clientId_secret_and_refreshTokenRotation_settings", "4.0": "feat/add_clientId_secret_and_refreshTokenRotation_settings" }' fdiWebsiteMap='{ "1.17": "20.1", "1.18": "20.1", "1.19": "20.1", "2.0": "20.1", "3.0": "20.1", "3.1": "20.1", "4.0": "20.1" }' -fdiAuthReactMap='{ "3.1": "0.49", "4.0": "0.49" }' +fdiAuthReactMap='{ "3.1": "0.48", "4.0": "0.48" }' data=`jq -cn --arg branch "$branch" \ --arg cdiCoreMap "$cdiCoreMap" \ diff --git a/lib/build/recipe/oauth2provider/api/loginInfo.js b/lib/build/recipe/oauth2provider/api/loginInfo.js index 15b9da808..d0572e82c 100644 --- a/lib/build/recipe/oauth2provider/api/loginInfo.js +++ b/lib/build/recipe/oauth2provider/api/loginInfo.js @@ -22,7 +22,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../../../utils"); const error_1 = __importDefault(require("../../../error")); async function loginInfoGET(apiImplementation, options, userContext) { - var _a; + var _a, _b; if (apiImplementation.loginInfoGET === undefined) { return false; } @@ -41,7 +41,14 @@ async function loginInfoGET(apiImplementation, options, userContext) { loginChallenge, userContext, }); - utils_1.send200Response(options.res, response); + if ("error" in response) { + utils_1.sendNon200Response(options.res, (_b = response.statusCode) !== null && _b !== void 0 ? _b : 400, { + error: response.error, + error_description: response.errorDescription, + }); + } else { + utils_1.send200Response(options.res, response); + } return true; } exports.default = loginInfoGET; diff --git a/lib/ts/recipe/oauth2provider/api/loginInfo.ts b/lib/ts/recipe/oauth2provider/api/loginInfo.ts index 2c13ddad0..91bb5a0c3 100644 --- a/lib/ts/recipe/oauth2provider/api/loginInfo.ts +++ b/lib/ts/recipe/oauth2provider/api/loginInfo.ts @@ -13,7 +13,7 @@ * under the License. */ -import { send200Response } from "../../../utils"; +import { send200Response, sendNon200Response } from "../../../utils"; import { APIInterface, APIOptions } from ".."; import { UserContext } from "../../../types"; import SuperTokensError from "../../../error"; @@ -43,6 +43,14 @@ export default async function loginInfoGET( userContext, }); - send200Response(options.res, response); + if ("error" in response) { + sendNon200Response(options.res, response.statusCode ?? 400, { + error: response.error, + error_description: response.errorDescription, + }); + } else { + send200Response(options.res, response); + } + return true; } diff --git a/lib/ts/recipe/oauth2provider/types.ts b/lib/ts/recipe/oauth2provider/types.ts index 63488692b..e73880482 100644 --- a/lib/ts/recipe/oauth2provider/types.ts +++ b/lib/ts/recipe/oauth2provider/types.ts @@ -136,8 +136,7 @@ export type LoginRequest = { export type TokenInfo = { // The access token issued by the authorization server. access_token?: string; - // The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated. - // integer + // 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. expires_in: number; // To retrieve a refresh token request the id_token scope. id_token?: string;