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;