From d292b0e9e05c800d8b074e2408aac51341a65062 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 13 Jun 2024 22:01:27 +0200 Subject: [PATCH 01/16] feat: add initial oauth2 client apis --- .gitignore | 3 +- lib/build/querier.d.ts | 7 +- lib/build/querier.js | 13 +- .../emailpassword/recipeImplementation.js | 1 + .../multitenancy/recipeImplementation.js | 2 + lib/build/recipe/oauth2/api/auth.d.ts | 8 + lib/build/recipe/oauth2/api/auth.js | 37 +++ lib/build/recipe/oauth2/api/consent.d.ts | 8 + lib/build/recipe/oauth2/api/consent.js | 63 ++++ lib/build/recipe/oauth2/api/implementation.js | 160 +++++++++- lib/build/recipe/oauth2/api/login.d.ts | 8 + lib/build/recipe/oauth2/api/login.js | 87 ++++++ lib/build/recipe/oauth2/api/logout.d.ts | 8 + lib/build/recipe/oauth2/api/logout.js | 57 ++++ lib/build/recipe/oauth2/api/token.d.ts | 8 + lib/build/recipe/oauth2/api/token.js | 30 ++ lib/build/recipe/oauth2/constants.d.ts | 5 + lib/build/recipe/oauth2/constants.js | 7 +- lib/build/recipe/oauth2/recipe.d.ts | 9 +- lib/build/recipe/oauth2/recipe.js | 96 +++++- .../recipe/oauth2/recipeImplementation.d.ts | 2 +- .../recipe/oauth2/recipeImplementation.js | 181 ++++++++++- lib/build/recipe/oauth2/types.d.ts | 216 ++++++++++++- .../api/getOpenIdDiscoveryConfiguration.js | 5 + lib/build/recipe/openid/index.d.ts | 5 + lib/build/recipe/openid/recipe.js | 2 +- .../recipe/openid/recipeImplementation.d.ts | 4 +- .../recipe/openid/recipeImplementation.js | 9 +- lib/build/recipe/openid/types.d.ts | 10 + .../passwordless/recipeImplementation.js | 1 + lib/build/recipe/session/index.d.ts | 5 + lib/build/recipe/session/sessionFunctions.js | 2 + lib/build/recipe/totp/recipeImplementation.js | 1 + .../usermetadata/recipeImplementation.js | 1 + .../recipe/userroles/recipeImplementation.js | 2 + lib/build/supertokens.js | 1 + lib/build/utils.js | 2 + lib/ts/querier.ts | 21 +- .../emailpassword/recipeImplementation.ts | 1 + .../multitenancy/recipeImplementation.ts | 2 + lib/ts/recipe/oauth2/api/auth.ts | 43 +++ lib/ts/recipe/oauth2/api/consent.ts | 66 ++++ lib/ts/recipe/oauth2/api/implementation.ts | 156 +++++++++- lib/ts/recipe/oauth2/api/login.ts | 76 +++++ lib/ts/recipe/oauth2/api/logout.ts | 63 ++++ lib/ts/recipe/oauth2/api/token.ts | 37 +++ lib/ts/recipe/oauth2/constants.ts | 6 + lib/ts/recipe/oauth2/recipe.ts | 103 ++++++- lib/ts/recipe/oauth2/recipeImplementation.ts | 190 +++++++++++- lib/ts/recipe/oauth2/types.ts | 284 +++++++++++++++++- .../api/getOpenIdDiscoveryConfiguration.ts | 5 + lib/ts/recipe/openid/api/implementation.ts | 11 +- lib/ts/recipe/openid/recipe.ts | 4 +- lib/ts/recipe/openid/recipeImplementation.ts | 19 +- lib/ts/recipe/openid/types.ts | 10 + .../passwordless/recipeImplementation.ts | 1 + lib/ts/recipe/session/sessionFunctions.ts | 2 + lib/ts/recipe/totp/recipeImplementation.ts | 1 + .../usermetadata/recipeImplementation.ts | 1 + .../recipe/userroles/recipeImplementation.ts | 8 +- lib/ts/supertokens.ts | 1 + lib/ts/utils.ts | 2 + test/querier.test.js | 2 +- test/with-typescript/index.ts | 5 + 64 files changed, 2114 insertions(+), 72 deletions(-) create mode 100644 lib/build/recipe/oauth2/api/auth.d.ts create mode 100644 lib/build/recipe/oauth2/api/auth.js create mode 100644 lib/build/recipe/oauth2/api/consent.d.ts create mode 100644 lib/build/recipe/oauth2/api/consent.js create mode 100644 lib/build/recipe/oauth2/api/login.d.ts create mode 100644 lib/build/recipe/oauth2/api/login.js create mode 100644 lib/build/recipe/oauth2/api/logout.d.ts create mode 100644 lib/build/recipe/oauth2/api/logout.js create mode 100644 lib/build/recipe/oauth2/api/token.d.ts create mode 100644 lib/build/recipe/oauth2/api/token.js create mode 100644 lib/ts/recipe/oauth2/api/auth.ts create mode 100644 lib/ts/recipe/oauth2/api/consent.ts create mode 100644 lib/ts/recipe/oauth2/api/login.ts create mode 100644 lib/ts/recipe/oauth2/api/logout.ts create mode 100644 lib/ts/recipe/oauth2/api/token.ts diff --git a/.gitignore b/.gitignore index d13b6071e..fdb5e981f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /node_modules +test/test-server/node_modules /examples/**/node_modules .DS_Store /.history @@ -10,4 +11,4 @@ releasePassword .tmp .idea /test_report -/.nyc_output \ No newline at end of file +/.nyc_output diff --git a/lib/build/querier.d.ts b/lib/build/querier.d.ts index 0e0ad52bd..7365599c2 100644 --- a/lib/build/querier.d.ts +++ b/lib/build/querier.d.ts @@ -49,7 +49,12 @@ export declare class Querier { body: any; headers: Headers; }>; - sendPutRequest: (path: NormalisedURLPath, body: any, userContext: UserContext) => Promise; + sendPutRequest: ( + path: NormalisedURLPath, + body: any, + params: Record, + userContext: UserContext + ) => Promise; invalidateCoreCallCache: (userContext: UserContext, updGlobalCacheTagIfNecessary?: boolean) => void; getAllCoreUrlsForPath(path: string): string[]; private sendRequestHelper; diff --git a/lib/build/querier.js b/lib/build/querier.js index 121687dcb..c6dd0c1d4 100644 --- a/lib/build/querier.js +++ b/lib/build/querier.js @@ -250,6 +250,9 @@ class Querier { method: "GET", headers, }); + if (response.status === 302) { + return response; + } if (response.status === 200 && !Querier.disableCache) { // If the request was successful, we save the result into the cache // plus we update the cache tag @@ -314,7 +317,7 @@ class Querier { ); }; // path should start with "/" - this.sendPutRequest = async (path, body, userContext) => { + this.sendPutRequest = async (path, body, params, userContext) => { var _a; this.invalidateCoreCallCache(userContext); const { body: respBody } = await this.sendRequestHelper( @@ -336,6 +339,7 @@ class Querier { method: "put", headers: headers, body: body, + params: params, }, userContext ); @@ -345,7 +349,12 @@ class Querier { body = request.body; } } - return utils_1.doFetch(url, { + const finalURL = new URL(url); + const searchParams = new URLSearchParams( + Object.entries(params).filter(([_, value]) => value !== undefined) + ); + finalURL.search = searchParams.toString(); + return utils_1.doFetch(finalURL.toString(), { method: "PUT", body: body !== undefined ? JSON.stringify(body) : undefined, headers, diff --git a/lib/build/recipe/emailpassword/recipeImplementation.js b/lib/build/recipe/emailpassword/recipeImplementation.js index 937dca20e..f3ccf2847 100644 --- a/lib/build/recipe/emailpassword/recipeImplementation.js +++ b/lib/build/recipe/emailpassword/recipeImplementation.js @@ -190,6 +190,7 @@ function getRecipeInterface(querier, getEmailPasswordConfig) { email: input.email, password: input.password, }, + {}, input.userContext ); if (response.status === "OK") { diff --git a/lib/build/recipe/multitenancy/recipeImplementation.js b/lib/build/recipe/multitenancy/recipeImplementation.js index cee1af3c8..31d8c09a4 100644 --- a/lib/build/recipe/multitenancy/recipeImplementation.js +++ b/lib/build/recipe/multitenancy/recipeImplementation.js @@ -16,6 +16,7 @@ function getRecipeInterface(querier) { let response = await querier.sendPutRequest( new normalisedURLPath_1.default(`/recipe/multitenancy/tenant`), Object.assign({ tenantId }, config), + {}, userContext ); return response; @@ -62,6 +63,7 @@ function getRecipeInterface(querier) { config, skipValidation, }, + {}, userContext ); return response; diff --git a/lib/build/recipe/oauth2/api/auth.d.ts b/lib/build/recipe/oauth2/api/auth.d.ts new file mode 100644 index 000000000..059876918 --- /dev/null +++ b/lib/build/recipe/oauth2/api/auth.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function authGET( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/auth.js b/lib/build/recipe/oauth2/api/auth.js new file mode 100644 index 000000000..a67d42c32 --- /dev/null +++ b/lib/build/recipe/oauth2/api/auth.js @@ -0,0 +1,37 @@ +"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 }); +const utils_1 = require("../../../utils"); +async function authGET(apiImplementation, options, userContext) { + if (apiImplementation.authGET === undefined) { + return false; + } + const origURL = options.req.getOriginalURL(); + const splitURL = origURL.split("?"); + const params = new URLSearchParams(splitURL[1]); + let response = await apiImplementation.authGET({ + options, + params: Object.fromEntries(params.entries()), + userContext, + }); + if ("redirectTo" in response) { + options.res.original.redirect(response.redirectTo); + } else { + utils_1.send200Response(options.res, response); + } + return true; +} +exports.default = authGET; diff --git a/lib/build/recipe/oauth2/api/consent.d.ts b/lib/build/recipe/oauth2/api/consent.d.ts new file mode 100644 index 000000000..b38df7de8 --- /dev/null +++ b/lib/build/recipe/oauth2/api/consent.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function consent( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/consent.js b/lib/build/recipe/oauth2/api/consent.js new file mode 100644 index 000000000..b95113d71 --- /dev/null +++ b/lib/build/recipe/oauth2/api/consent.js @@ -0,0 +1,63 @@ +"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 }); +const utils_1 = require("../../../utils"); +// TODO: separate post and get? +async function consent(apiImplementation, options, userContext) { + var _a; + if (utils_1.normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.consentPOST === undefined) { + return false; + } + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.consentPOST({ + options, + accept: reqBody.accept, + consentChallenge: reqBody.consentChallenge, + grantScope: reqBody.grantScope, + remember: reqBody.remember, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.consentGET === undefined) { + return false; + } + const consentChallenge = + (_a = options.req.getKeyValueFromQuery("consentChallenge")) !== null && _a !== void 0 + ? _a + : options.req.getKeyValueFromQuery("consent_challenge"); + if (consentChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.consentGET({ + options, + consentChallenge, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} +exports.default = consent; diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index 4a0dd5c82..0a728d0e2 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -13,8 +13,166 @@ * 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 supertokens_1 = __importDefault(require("../../../supertokens")); function getAPIImplementation() { - return {}; + return { + loginGET: async ({ loginChallenge, options, session, userContext }) => { + var _a, _b; + const request = await options.recipeImplementation.getLoginRequest({ + challenge: loginChallenge, + userContext, + }); + if (request.skip) { + const accept = await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: request.subject, + userContext, + }); + return { redirectTo: accept.redirectTo }; + } else if (session) { + if (session.getUserId() !== request.subject) { + // TODO? + } + const accept = await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: session.getUserId(), + userContext, + }); + return { redirectTo: accept.redirectTo }; + } + const appInfo = supertokens_1.default.getInstanceOrThrowError().appInfo; + const websiteDomain = appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous(); + const websiteBasePath = appInfo.websiteBasePath.getAsStringDangerous(); + // TODO: + return { + redirectTo: + websiteDomain + + websiteBasePath + + `?hint=${ + (_b = (_a = request.oidcContext) === null || _a === void 0 ? void 0 : _a.login_hint) !== null && + _b !== void 0 + ? _b + : "" + }&redirectToPath=${encodeURIComponent( + "/continue-/auth/oauth2/login?login_challenge=" + loginChallenge + )}`, + }; + }, + loginPOST: async ({ loginChallenge, accept, options, session, userContext }) => { + const res = accept + ? await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: session.getUserId(), + userContext, + }) + : await options.recipeImplementation.rejectLoginRequest({ + challenge: loginChallenge, + error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + return { redirectTo: res.redirectTo }; + }, + logoutGET: async ({ logoutChallenge, options, userContext }) => { + const request = await options.recipeImplementation.getLogoutRequest({ + challenge: logoutChallenge, + userContext, + }); + const appInfo = supertokens_1.default.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + + appInfo.websiteBasePath.getAsStringDangerous() + + `/logout?challenge=${request.challenge}`, + }; + }, + logoutPOST: async ({ logoutChallenge, accept, options, userContext }) => { + if (accept) { + const res = await options.recipeImplementation.acceptLogoutRequest({ + challenge: logoutChallenge, + userContext, + }); + return { redirectTo: res.redirectTo }; + } + await options.recipeImplementation.rejectLogoutRequest({ + challenge: logoutChallenge, + // error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + const appInfo = supertokens_1.default.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + appInfo.websiteBasePath.getAsStringDangerous(), + }; + }, + consentGET: async ({ consentChallenge, options, userContext }) => { + const request = await options.recipeImplementation.getConsentRequest({ + challenge: consentChallenge, + userContext, + }); + const appInfo = supertokens_1.default.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + + appInfo.websiteBasePath.getAsStringDangerous() + + `/consent?challenge=${request.challenge}&scopes=${request.requestedScope}&client=${request.client}&`, + }; + }, + consentPOST: async ({ consentChallenge, accept, remember, grantScope, options, userContext }) => { + const request = await options.recipeImplementation.getConsentRequest({ + challenge: consentChallenge, + userContext, + }); + const res = accept + ? await options.recipeImplementation.acceptConsentRequest({ + challenge: consentChallenge, + grantAccessTokenAudience: request.requestedAccessTokenAudience, + remember, + grantScope, + userContext, + }) + : await options.recipeImplementation.rejectConsentRequest({ + challenge: consentChallenge, + error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + return { redirectTo: res.redirectTo }; + }, + authGET: async (input) => { + const res = await input.options.recipeImplementation.authorization({ + params: input.params, + userContext: input.userContext, + }); + return res; + }, + tokenPOST: async (input) => { + return input.options.recipeImplementation.token({ body: input.body, userContext: input.userContext }); + }, + }; } exports.default = getAPIImplementation; diff --git a/lib/build/recipe/oauth2/api/login.d.ts b/lib/build/recipe/oauth2/api/login.d.ts new file mode 100644 index 000000000..6f4253cef --- /dev/null +++ b/lib/build/recipe/oauth2/api/login.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function login( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/login.js b/lib/build/recipe/oauth2/api/login.js new file mode 100644 index 000000000..878d1a6a8 --- /dev/null +++ b/lib/build/recipe/oauth2/api/login.js @@ -0,0 +1,87 @@ +"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 utils_1 = require("../../../utils"); +const session_1 = __importDefault(require("../../session")); +// TODO: separate post and get? +async function login(apiImplementation, options, userContext) { + var _a; + if (utils_1.normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.loginPOST === undefined) { + return false; + } + const session = await session_1.default.getSession( + options.req, + options.res, + { sessionRequired: true }, + userContext + ); + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.loginPOST({ + options, + accept: reqBody.accept, + loginChallenge: reqBody.loginChallenge, + session, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.loginGET === undefined) { + return false; + } + let session; + try { + session = await session_1.default.getSession( + options.req, + options.res, + { sessionRequired: false }, + userContext + ); + } catch (_b) { + // TODO: Claim validation failure + } + // TODO: take only one + const loginChallenge = + (_a = options.req.getKeyValueFromQuery("login_challenge")) !== null && _a !== void 0 + ? _a + : options.req.getKeyValueFromQuery("loginChallenge"); + if (loginChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.loginGET({ + options, + loginChallenge, + session, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} +exports.default = login; diff --git a/lib/build/recipe/oauth2/api/logout.d.ts b/lib/build/recipe/oauth2/api/logout.d.ts new file mode 100644 index 000000000..d9242afff --- /dev/null +++ b/lib/build/recipe/oauth2/api/logout.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function logout( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/logout.js b/lib/build/recipe/oauth2/api/logout.js new file mode 100644 index 000000000..ed9d07e8d --- /dev/null +++ b/lib/build/recipe/oauth2/api/logout.js @@ -0,0 +1,57 @@ +"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 }); +const utils_1 = require("../../../utils"); +// TODO: separate post and get? +async function logout(apiImplementation, options, userContext) { + if (utils_1.normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.logoutPOST === undefined) { + return false; + } + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.logoutPOST({ + options, + accept: reqBody.accept, + logoutChallenge: reqBody.logoutChallenge, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.logoutGET === undefined) { + return false; + } + const logoutChallenge = options.req.getKeyValueFromQuery("logoutChallenge"); + if (logoutChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.logoutGET({ + options, + logoutChallenge, + userContext, + }); + if ("status" in response) { + utils_1.send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} +exports.default = logout; diff --git a/lib/build/recipe/oauth2/api/token.d.ts b/lib/build/recipe/oauth2/api/token.d.ts new file mode 100644 index 000000000..c697b7744 --- /dev/null +++ b/lib/build/recipe/oauth2/api/token.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function tokenPOST( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/token.js b/lib/build/recipe/oauth2/api/token.js new file mode 100644 index 000000000..2a7c8d03d --- /dev/null +++ b/lib/build/recipe/oauth2/api/token.js @@ -0,0 +1,30 @@ +"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 }); +const utils_1 = require("../../../utils"); +async function tokenPOST(apiImplementation, options, userContext) { + if (apiImplementation.tokenPOST === undefined) { + return false; + } + let response = await apiImplementation.tokenPOST({ + options, + body: options.req.getFormData(), + userContext, + }); + utils_1.send200Response(options.res, response); + return true; +} +exports.default = tokenPOST; diff --git a/lib/build/recipe/oauth2/constants.d.ts b/lib/build/recipe/oauth2/constants.d.ts index 0d55262a7..cff481248 100644 --- a/lib/build/recipe/oauth2/constants.d.ts +++ b/lib/build/recipe/oauth2/constants.d.ts @@ -1,2 +1,7 @@ // @ts-nocheck export declare const OAUTH2_BASE_PATH = "/oauth2/"; +export declare const LOGIN_PATH = "/oauth2/login"; +export declare const LOGOUT_PATH = "/oauth2/logout"; +export declare const CONSENT_PATH = "/oauth2/consent"; +export declare const AUTH_PATH = "/oauth2/auth"; +export declare const TOKEN_PATH = "/oauth2/token"; diff --git a/lib/build/recipe/oauth2/constants.js b/lib/build/recipe/oauth2/constants.js index e8dc43a0d..6eb5434b8 100644 --- a/lib/build/recipe/oauth2/constants.js +++ b/lib/build/recipe/oauth2/constants.js @@ -14,5 +14,10 @@ * under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); -exports.OAUTH2_BASE_PATH = void 0; +exports.TOKEN_PATH = exports.AUTH_PATH = exports.CONSENT_PATH = exports.LOGOUT_PATH = exports.LOGIN_PATH = exports.OAUTH2_BASE_PATH = void 0; exports.OAUTH2_BASE_PATH = "/oauth2/"; +exports.LOGIN_PATH = "/oauth2/login"; +exports.LOGOUT_PATH = "/oauth2/logout"; +exports.CONSENT_PATH = "/oauth2/consent"; +exports.AUTH_PATH = "/oauth2/auth"; +exports.TOKEN_PATH = "/oauth2/token"; diff --git a/lib/build/recipe/oauth2/recipe.d.ts b/lib/build/recipe/oauth2/recipe.d.ts index c979f3d0c..2bab64401 100644 --- a/lib/build/recipe/oauth2/recipe.d.ts +++ b/lib/build/recipe/oauth2/recipe.d.ts @@ -13,18 +13,19 @@ export default class Recipe extends RecipeModule { apiImpl: APIInterface; isInServerlessEnv: boolean; constructor(recipeId: string, appInfo: NormalisedAppinfo, isInServerlessEnv: boolean, config?: TypeInput); + static getInstance(): Recipe | undefined; static getInstanceOrThrowError(): Recipe; static init(config?: TypeInput): RecipeListFunction; static reset(): void; getAPIsHandled(): APIHandled[]; handleAPIRequest: ( - _id: string, + id: string, _tenantId: string | undefined, - _req: BaseRequest, - _res: BaseResponse, + req: BaseRequest, + res: BaseResponse, _path: NormalisedURLPath, _method: HTTPMethod, - _userContext: UserContext + userContext: UserContext ) => Promise; handleError(error: error, _: BaseRequest, __: BaseResponse, _userContext: UserContext): Promise; getAllCORSHeaders(): string[]; diff --git a/lib/build/recipe/oauth2/recipe.js b/lib/build/recipe/oauth2/recipe.js index 44be8d270..cc397ccd8 100644 --- a/lib/build/recipe/oauth2/recipe.js +++ b/lib/build/recipe/oauth2/recipe.js @@ -20,25 +20,47 @@ var __importDefault = }; Object.defineProperty(exports, "__esModule", { value: true }); const error_1 = __importDefault(require("../../error")); +const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); const querier_1 = require("../../querier"); const recipeModule_1 = __importDefault(require("../../recipeModule")); +const auth_1 = __importDefault(require("./api/auth")); +const consent_1 = __importDefault(require("./api/consent")); const implementation_1 = __importDefault(require("./api/implementation")); +const login_1 = __importDefault(require("./api/login")); +const logout_1 = __importDefault(require("./api/logout")); +const token_1 = __importDefault(require("./api/token")); +const constants_1 = require("./constants"); const recipeImplementation_1 = __importDefault(require("./recipeImplementation")); const utils_1 = require("./utils"); const supertokens_js_override_1 = __importDefault(require("supertokens-js-override")); class Recipe extends recipeModule_1.default { constructor(recipeId, appInfo, isInServerlessEnv, config) { super(recipeId, appInfo); - 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, - // }; - throw new Error("Not implemented"); + 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, + }; + if (id === constants_1.LOGIN_PATH) { + return login_1.default(this.apiImpl, options, userContext); + } + if (id === constants_1.LOGOUT_PATH) { + return logout_1.default(this.apiImpl, options, userContext); + } + if (id === constants_1.CONSENT_PATH) { + return consent_1.default(this.apiImpl, options, userContext); + } + if (id === constants_1.TOKEN_PATH) { + return token_1.default(this.apiImpl, options, userContext); + } + if (id === constants_1.AUTH_PATH) { + return auth_1.default(this.apiImpl, options, userContext); + } + throw new Error("Should never come here: handleAPIRequest called with unknown id"); }; this.config = utils_1.validateAndNormaliseUserInput(this, appInfo, config); this.isInServerlessEnv = isInServerlessEnv; @@ -58,6 +80,9 @@ class Recipe extends recipeModule_1.default { } } /* Init functions */ + static getInstance() { + return Recipe.instance; + } static getInstanceOrThrowError() { if (Recipe.instance !== undefined) { return Recipe.instance; @@ -82,7 +107,56 @@ class Recipe extends recipeModule_1.default { } /* RecipeModule functions */ getAPIsHandled() { - return []; + return [ + { + method: "post", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.LOGIN_PATH), + id: constants_1.LOGIN_PATH, + disabled: this.apiImpl.loginPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.LOGIN_PATH), + id: constants_1.LOGIN_PATH, + disabled: this.apiImpl.loginGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.LOGOUT_PATH), + id: constants_1.LOGOUT_PATH, + disabled: this.apiImpl.logoutPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.LOGOUT_PATH), + id: constants_1.LOGOUT_PATH, + disabled: this.apiImpl.logoutGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.CONSENT_PATH), + id: constants_1.CONSENT_PATH, + disabled: this.apiImpl.consentPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.CONSENT_PATH), + id: constants_1.CONSENT_PATH, + disabled: this.apiImpl.consentGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.TOKEN_PATH), + id: constants_1.TOKEN_PATH, + disabled: this.apiImpl.tokenPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.AUTH_PATH), + id: constants_1.AUTH_PATH, + disabled: this.apiImpl.authGET === undefined, + }, + ]; } handleError(error, _, __, _userContext) { throw error; diff --git a/lib/build/recipe/oauth2/recipeImplementation.d.ts b/lib/build/recipe/oauth2/recipeImplementation.d.ts index 513f5df92..d2a1542a2 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.d.ts +++ b/lib/build/recipe/oauth2/recipeImplementation.d.ts @@ -3,7 +3,7 @@ import { Querier } from "../../querier"; import { NormalisedAppinfo } from "../../types"; import { RecipeInterface, TypeNormalisedInput } from "./types"; export default function getRecipeInterface( - _querier: Querier, + querier: Querier, _config: TypeNormalisedInput, _appInfo: NormalisedAppinfo ): RecipeInterface; diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index 67c4636f5..9c2ec89f2 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -13,8 +13,185 @@ * 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 }); -function getRecipeInterface(_querier, _config, _appInfo) { - return {}; +const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); +function getRecipeInterface(querier, _config, _appInfo) { + return { + getLoginRequest: async function (input) { + const resp = await querier.sendGetRequest( + new normalisedURLPath_1.default("/recipe/oauth2/admin/oauth2/auth/requests/login"), + { login_challenge: input.challenge }, + input.userContext + ); + return { + challenge: resp.challenge, + client: resp.client, + oidcContext: resp.oidc_context, + requestUrl: resp.request_url, + requestedAccessTokenAudience: resp.requested_access_token_audience, + requestedScope: resp.requested_scope, + sessionId: resp.session_id, + skip: resp.skip, + subject: resp.subject, + }; + }, + acceptLoginRequest: async function (input) { + const resp = await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/login/accept`), + { + acr: input.acr, + amr: input.amr, + context: input.context, + extend_session_lifespan: input.extendSessionLifespan, + force_subject_identifier: input.forceSubjectIdentifier, + identity_provider_session_id: input.identityProviderSessionId, + remember: input.remember, + remember_for: input.rememberFor, + subject: input.subject, + }, + { + login_challenge: input.challenge, + }, + input.userContext + ); + return { redirectTo: resp.redirect_to }; + }, + rejectLoginRequest: async function (input) { + const resp = await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/login/reject`), + { + error: input.error.error, + error_debug: input.error.errorDebug, + error_description: input.error.errorDescription, + error_hint: input.error.errorHint, + status_code: input.error.statusCode, + }, + { + login_challenge: input.challenge, + }, + input.userContext + ); + return { redirectTo: resp.redirect_to }; + }, + getConsentRequest: async function (input) { + const resp = await querier.sendGetRequest( + new normalisedURLPath_1.default("/recipe/oauth2/admin/oauth2/auth/requests/consent"), + { consent_challenge: input.challenge }, + input.userContext + ); + return { + acr: resp.acr, + amr: resp.amr, + challenge: resp.challenge, + client: resp.client, + context: resp.context, + loginChallenge: resp.login_challenge, + loginSessionId: resp.login_session_id, + oidcContext: resp.oidc_context, + requestUrl: resp.request_url, + requestedAccessTokenAudience: resp.requested_access_token_audience, + requestedScope: resp.requested_scope, + skip: resp.skip, + subject: resp.subject, + }; + }, + acceptConsentRequest: async function (input) { + const resp = await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/consent/accept`), + { + context: input.context, + grant_access_token_audience: input.grantAccessTokenAudience, + grant_scope: input.grantScope, + handled_at: input.handledAt, + remember: input.remember, + remember_for: input.rememberFor, + session: input.session, + }, + { + consent_challenge: input.challenge, + }, + input.userContext + ); + return { redirectTo: resp.redirect_to }; + }, + rejectConsentRequest: async function (input) { + const resp = await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/consent/reject`), + { + error: input.error.error, + error_debug: input.error.errorDebug, + error_description: input.error.errorDescription, + error_hint: input.error.errorHint, + status_code: input.error.statusCode, + }, + { + consent_challenge: input.challenge, + }, + input.userContext + ); + return { redirectTo: resp.redirect_to }; + }, + getLogoutRequest: async function (input) { + const resp = await querier.sendGetRequest( + new normalisedURLPath_1.default("/recipe/oauth2/admin/oauth2/auth/requests/logout"), + { logout_challenge: input.challenge }, + input.userContext + ); + return { + challenge: resp.challenge, + client: resp.client, + requestUrl: resp.request_url, + rpInitiated: resp.rp_initiated, + sid: resp.sid, + subject: resp.subject, + }; + }, + acceptLogoutRequest: async function (input) { + const resp = await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/consent/logout/accept`), + {}, + { + logout_challenge: input.challenge, + }, + input.userContext + ); + return { redirectTo: resp.redirect_to }; + }, + rejectLogoutRequest: async function (input) { + await querier.sendPutRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/admin/oauth2/auth/requests/consent/logout/reject`), + {}, + { + logout_challenge: input.challenge, + }, + input.userContext + ); + }, + authorization: async function (input) { + const resp = await querier.sendGetRequestWithResponseHeaders( + new normalisedURLPath_1.default(`/recipe/oauth2/pub/auth`), + input.params, + input.userContext + ); + const redirectTo = resp.headers.get("Location"); + if (redirectTo === undefined) { + throw new Error(resp.body); + } + return { redirectTo }; + }, + token: async function (input) { + // TODO: Untested and suspicios + return querier.sendGetRequest( + new normalisedURLPath_1.default(`/recipe/oauth2/pub/token`), + input.body, + input.userContext + ); + }, + }; } exports.default = getRecipeInterface; diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index f2e26d16d..2aea9a4a3 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -1,6 +1,8 @@ // @ts-nocheck import type { BaseRequest, BaseResponse } from "../../framework"; import OverrideableBuilder from "supertokens-js-override"; +import { GeneralErrorResponse, JSONObject, UserContext } from "../../types"; +import { SessionContainerInterface } from "../session/types"; export declare type TypeInput = { override?: { functions?: ( @@ -27,5 +29,215 @@ export declare type APIOptions = { req: BaseRequest; res: BaseResponse; }; -export declare type RecipeInterface = {}; -export declare type APIInterface = {}; +export declare type OAuth2Client = {}; +export declare type ErrorOAuth2 = { + error: string; + errorDescription: string; + errorDebug?: string; + errorHint?: string; + statusCode?: number; +}; +export declare type ConsentRequest = { + acr?: string; + amr?: string[]; + challenge: string; + client?: OAuth2Client; + context?: JSONObject; + loginChallenge?: string; + loginSessionId?: string; + oidcContext?: any; + requestUrl?: string; + requestedAccessTokenAudience?: string[]; + requestedScope?: string[]; + skip?: boolean; + subject?: string; +}; +export declare type LoginRequest = { + challenge: string; + client: OAuth2Client; + oidcContext?: any; + requestUrl: string; + requestedAccessTokenAudience?: string[]; + requestedScope?: string[]; + sessionId?: string; + skip: boolean; + subject: string; +}; +export declare type LogoutRequest = { + challenge: string; + client: OAuth2Client; + requestUrl: string; + rpInitiated: boolean; + sid: string; + subject: string; +}; +export declare type TokenInfo = { + accessToken: string; + expiresIn: number; + idToken: string; + refreshToken: string; + scope: string; + tokenType: string; +}; +export declare type RecipeInterface = { + authorization(input: { + params: any; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + token(input: { body: any; userContext: UserContext }): Promise; + getConsentRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptConsentRequest(input: { + challenge: string; + context?: any; + grantAccessTokenAudience?: string[]; + grantScope?: string[]; + handledAt?: string[]; + remember?: boolean; + rememberFor?: number; + session?: any; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + rejectConsentRequest(input: { + challenge: string; + error: ErrorOAuth2; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + getLoginRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptLoginRequest(input: { + challenge: string; + acr?: string; + amr?: string[]; + context?: any; + extendSessionLifespan?: boolean; + forceSubjectIdentifier?: string; + identityProviderSessionId?: string; + remember?: boolean; + rememberFor?: number; + subject: string; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + rejectLoginRequest(input: { + challenge: string; + error: ErrorOAuth2; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + getLogoutRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptLogoutRequest(input: { + challenge: string; + userContext: UserContext; + }): Promise<{ + redirectTo: string; + }>; + rejectLogoutRequest(input: { challenge: string; userContext: UserContext }): Promise; +}; +export declare type APIInterface = { + loginGET: + | undefined + | ((input: { + loginChallenge: string; + options: APIOptions; + session?: SessionContainerInterface; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + loginPOST: + | undefined + | ((input: { + loginChallenge: string; + accept: boolean; + session: SessionContainerInterface; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + logoutGET: + | undefined + | ((input: { + logoutChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + logoutPOST: + | undefined + | ((input: { + logoutChallenge: string; + accept: boolean; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + consentGET: + | undefined + | ((input: { + consentChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + consentPOST: + | undefined + | ((input: { + consentChallenge: string; + accept: boolean; + grantScope: string[]; + remember: boolean; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | GeneralErrorResponse + >); + authGET: + | undefined + | ((input: { + params: any; + options: APIOptions; + userContext: UserContext; + }) => Promise< + | { + redirectTo: string; + } + | ErrorOAuth2 + | GeneralErrorResponse + >); + tokenPOST: + | undefined + | ((input: { + body: any; + options: APIOptions; + userContext: UserContext; + }) => Promise); +}; diff --git a/lib/build/recipe/openid/api/getOpenIdDiscoveryConfiguration.js b/lib/build/recipe/openid/api/getOpenIdDiscoveryConfiguration.js index b308bfffb..7e2fdb593 100644 --- a/lib/build/recipe/openid/api/getOpenIdDiscoveryConfiguration.js +++ b/lib/build/recipe/openid/api/getOpenIdDiscoveryConfiguration.js @@ -14,6 +14,11 @@ async function getOpenIdDiscoveryConfiguration(apiImplementation, options, userC utils_1.send200Response(options.res, { issuer: result.issuer, jwks_uri: result.jwks_uri, + authorization_endpoint: result.authorization_endpoint, + token_endpoint: result.token_endpoint, + subject_types_supported: result.subject_types_supported, + id_token_signing_alg_values_supported: result.id_token_signing_alg_values_supported, + response_types_supported: result.response_types_supported, }); } else { utils_1.send200Response(options.res, result); diff --git a/lib/build/recipe/openid/index.d.ts b/lib/build/recipe/openid/index.d.ts index e94fd0092..a7f961b19 100644 --- a/lib/build/recipe/openid/index.d.ts +++ b/lib/build/recipe/openid/index.d.ts @@ -8,6 +8,11 @@ export default class OpenIdRecipeWrapper { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; }>; static createJWT( payload?: any, diff --git a/lib/build/recipe/openid/recipe.js b/lib/build/recipe/openid/recipe.js index fd7a67398..4e0eb29e1 100644 --- a/lib/build/recipe/openid/recipe.js +++ b/lib/build/recipe/openid/recipe.js @@ -79,7 +79,7 @@ class OpenIdRecipe extends recipeModule_1.default { override: this.config.override.jwtFeature, }); let builder = new supertokens_js_override_1.default( - recipeImplementation_1.default(this.config, this.jwtRecipe.recipeInterfaceImpl) + recipeImplementation_1.default(this.config, this.jwtRecipe.recipeInterfaceImpl, appInfo) ); this.recipeImplementation = builder.override(this.config.override.functions).build(); let apiBuilder = new supertokens_js_override_1.default(implementation_1.default()); diff --git a/lib/build/recipe/openid/recipeImplementation.d.ts b/lib/build/recipe/openid/recipeImplementation.d.ts index d4698099c..be9ecbb29 100644 --- a/lib/build/recipe/openid/recipeImplementation.d.ts +++ b/lib/build/recipe/openid/recipeImplementation.d.ts @@ -1,7 +1,9 @@ // @ts-nocheck import { RecipeInterface, TypeNormalisedInput } from "./types"; import { RecipeInterface as JWTRecipeInterface } from "../jwt/types"; +import { NormalisedAppinfo } from "../../types"; export default function getRecipeInterface( config: TypeNormalisedInput, - jwtRecipeImplementation: JWTRecipeInterface + jwtRecipeImplementation: JWTRecipeInterface, + appInfo: NormalisedAppinfo ): RecipeInterface; diff --git a/lib/build/recipe/openid/recipeImplementation.js b/lib/build/recipe/openid/recipeImplementation.js index 0edb22162..d3b582d2e 100644 --- a/lib/build/recipe/openid/recipeImplementation.js +++ b/lib/build/recipe/openid/recipeImplementation.js @@ -7,7 +7,8 @@ var __importDefault = Object.defineProperty(exports, "__esModule", { value: true }); const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); const constants_1 = require("../jwt/constants"); -function getRecipeInterface(config, jwtRecipeImplementation) { +const constants_2 = require("../oauth2/constants"); +function getRecipeInterface(config, jwtRecipeImplementation, appInfo) { return { getOpenIdDiscoveryConfiguration: async function () { let issuer = config.issuerDomain.getAsStringDangerous() + config.issuerPath.getAsStringDangerous(); @@ -16,10 +17,16 @@ function getRecipeInterface(config, jwtRecipeImplementation) { config.issuerPath .appendPath(new normalisedURLPath_1.default(constants_1.GET_JWKS_API)) .getAsStringDangerous(); + const apiBasePath = appInfo.apiDomain.getAsStringDangerous() + appInfo.apiBasePath.getAsStringDangerous(); return { status: "OK", issuer, jwks_uri, + authorization_endpoint: apiBasePath + constants_2.AUTH_PATH, + token_endpoint: apiBasePath + constants_2.TOKEN_PATH, + subject_types_supported: ["public"], + id_token_signing_alg_values_supported: ["RS256"], + response_types_supported: ["code", "id_token", "id_token token"], }; }, createJWT: async function ({ payload, validitySeconds, useStaticSigningKey, userContext }) { diff --git a/lib/build/recipe/openid/types.d.ts b/lib/build/recipe/openid/types.d.ts index 5b907a8d5..d99d7d529 100644 --- a/lib/build/recipe/openid/types.d.ts +++ b/lib/build/recipe/openid/types.d.ts @@ -66,6 +66,11 @@ export declare type APIInterface = { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; } | GeneralErrorResponse >); @@ -77,6 +82,11 @@ export declare type RecipeInterface = { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; }>; createJWT(input: { payload?: any; diff --git a/lib/build/recipe/passwordless/recipeImplementation.js b/lib/build/recipe/passwordless/recipeImplementation.js index 8a3782d64..2cf3a2aee 100644 --- a/lib/build/recipe/passwordless/recipeImplementation.js +++ b/lib/build/recipe/passwordless/recipeImplementation.js @@ -150,6 +150,7 @@ function getRecipeInterface(querier) { let response = await querier.sendPutRequest( new normalisedURLPath_1.default(`/recipe/user`), copyAndRemoveUserContextAndTenantId(input), + {}, input.userContext ); if (response.status !== "OK") { diff --git a/lib/build/recipe/session/index.d.ts b/lib/build/recipe/session/index.d.ts index 55fbe988b..b96971a45 100644 --- a/lib/build/recipe/session/index.d.ts +++ b/lib/build/recipe/session/index.d.ts @@ -177,6 +177,11 @@ export default class SessionWrapper { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; }>; static fetchAndSetClaim( sessionHandle: string, diff --git a/lib/build/recipe/session/sessionFunctions.js b/lib/build/recipe/session/sessionFunctions.js index 1f3d91722..90c828ce8 100644 --- a/lib/build/recipe/session/sessionFunctions.js +++ b/lib/build/recipe/session/sessionFunctions.js @@ -453,6 +453,7 @@ async function updateSessionDataInDatabase(helpers, sessionHandle, newSessionDat sessionHandle, userDataInDatabase: newSessionData, }, + {}, userContext ); if (response.status === "UNAUTHORISED") { @@ -470,6 +471,7 @@ async function updateAccessTokenPayload(helpers, sessionHandle, newAccessTokenPa sessionHandle, userDataInJWT: newAccessTokenPayload, }, + {}, userContext ); if (response.status === "UNAUTHORISED") { diff --git a/lib/build/recipe/totp/recipeImplementation.js b/lib/build/recipe/totp/recipeImplementation.js index 5465f884c..5ad44874b 100644 --- a/lib/build/recipe/totp/recipeImplementation.js +++ b/lib/build/recipe/totp/recipeImplementation.js @@ -100,6 +100,7 @@ function getRecipeInterface(querier, config) { existingDeviceName: input.existingDeviceName, newDeviceName: input.newDeviceName, }, + {}, input.userContext ); }, diff --git a/lib/build/recipe/usermetadata/recipeImplementation.js b/lib/build/recipe/usermetadata/recipeImplementation.js index e3aa25a5f..2fd78898b 100644 --- a/lib/build/recipe/usermetadata/recipeImplementation.js +++ b/lib/build/recipe/usermetadata/recipeImplementation.js @@ -36,6 +36,7 @@ function getRecipeInterface(querier) { userId, metadataUpdate, }, + {}, userContext ); }, diff --git a/lib/build/recipe/userroles/recipeImplementation.js b/lib/build/recipe/userroles/recipeImplementation.js index ca12893f9..47dbdc1ba 100644 --- a/lib/build/recipe/userroles/recipeImplementation.js +++ b/lib/build/recipe/userroles/recipeImplementation.js @@ -29,6 +29,7 @@ function getRecipeInterface(querier) { `/${tenantId === undefined ? constants_1.DEFAULT_TENANT_ID : tenantId}/recipe/user/role` ), { userId, role }, + {}, userContext ); }, @@ -63,6 +64,7 @@ function getRecipeInterface(querier) { return querier.sendPutRequest( new normalisedURLPath_1.default("/recipe/role"), { role, permissions }, + {}, userContext ); }, diff --git a/lib/build/supertokens.js b/lib/build/supertokens.js index 30486abfa..07b52a330 100644 --- a/lib/build/supertokens.js +++ b/lib/build/supertokens.js @@ -135,6 +135,7 @@ class SuperTokens { userIdType: input.userIdType, externalUserIdInfo: input.externalUserIdInfo, }, + {}, input.userContext ); } else { diff --git a/lib/build/utils.js b/lib/build/utils.js index 73a678826..db5118f3f 100644 --- a/lib/build/utils.js +++ b/lib/build/utils.js @@ -58,6 +58,7 @@ const doFetch = (input, init) => { ); init = { cache: "no-cache", + redirect: "manual", }; } else { if (init.cache === undefined) { @@ -65,6 +66,7 @@ const doFetch = (input, init) => { processState_1.PROCESS_STATE.ADDING_NO_CACHE_HEADER_IN_FETCH ); init.cache = "no-cache"; + init.redirect = "manual"; } } const fetchFunction = typeof fetch !== "undefined" ? fetch : cross_fetch_1.default; diff --git a/lib/ts/querier.ts b/lib/ts/querier.ts index 26daf1951..a60bf32d8 100644 --- a/lib/ts/querier.ts +++ b/lib/ts/querier.ts @@ -319,12 +319,15 @@ export class Querier { finalURL.search = searchParams.toString(); // Update cache and return - let response = await doFetch(finalURL.toString(), { method: "GET", headers, }); + if (response.status === 302) { + return response; + } + if (response.status === 200 && !Querier.disableCache) { // If the request was successful, we save the result into the cache // plus we update the cache tag @@ -400,7 +403,12 @@ export class Querier { }; // path should start with "/" - sendPutRequest = async (path: NormalisedURLPath, body: any, userContext: UserContext): Promise => { + sendPutRequest = async ( + path: NormalisedURLPath, + body: any, + params: Record, + userContext: UserContext + ): Promise => { this.invalidateCoreCallCache(userContext); const { body: respBody } = await this.sendRequestHelper( @@ -428,6 +436,7 @@ export class Querier { method: "put", headers: headers, body: body, + params: params, }, userContext ); @@ -438,7 +447,13 @@ export class Querier { } } - return doFetch(url, { + const finalURL = new URL(url); + const searchParams = new URLSearchParams( + Object.entries(params).filter(([_, value]) => value !== undefined) as string[][] + ); + finalURL.search = searchParams.toString(); + + return doFetch(finalURL.toString(), { method: "PUT", body: body !== undefined ? JSON.stringify(body) : undefined, headers, diff --git a/lib/ts/recipe/emailpassword/recipeImplementation.ts b/lib/ts/recipe/emailpassword/recipeImplementation.ts index e1f44b073..58e2e4f89 100644 --- a/lib/ts/recipe/emailpassword/recipeImplementation.ts +++ b/lib/ts/recipe/emailpassword/recipeImplementation.ts @@ -284,6 +284,7 @@ export default function getRecipeInterface( email: input.email, password: input.password, }, + {}, input.userContext ); diff --git a/lib/ts/recipe/multitenancy/recipeImplementation.ts b/lib/ts/recipe/multitenancy/recipeImplementation.ts index c6a655b84..c311b41c3 100644 --- a/lib/ts/recipe/multitenancy/recipeImplementation.ts +++ b/lib/ts/recipe/multitenancy/recipeImplementation.ts @@ -16,6 +16,7 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { tenantId, ...config, }, + {}, userContext ); @@ -68,6 +69,7 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { config, skipValidation, }, + {}, userContext ); return response; diff --git a/lib/ts/recipe/oauth2/api/auth.ts b/lib/ts/recipe/oauth2/api/auth.ts new file mode 100644 index 000000000..cdcb6fe37 --- /dev/null +++ b/lib/ts/recipe/oauth2/api/auth.ts @@ -0,0 +1,43 @@ +/* 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 { send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; + +export default async function authGET( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (apiImplementation.authGET === undefined) { + return false; + } + const origURL = options.req.getOriginalURL(); + const splitURL = origURL.split("?"); + const params = new URLSearchParams(splitURL[1]); + + let response = await apiImplementation.authGET({ + options, + params: Object.fromEntries(params.entries()), + userContext, + }); + if ("redirectTo" in response) { + options.res.original.redirect(response.redirectTo); + } else { + send200Response(options.res, response); + } + return true; +} diff --git a/lib/ts/recipe/oauth2/api/consent.ts b/lib/ts/recipe/oauth2/api/consent.ts new file mode 100644 index 000000000..efc254432 --- /dev/null +++ b/lib/ts/recipe/oauth2/api/consent.ts @@ -0,0 +1,66 @@ +/* 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 { normaliseHttpMethod, send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; + +// TODO: separate post and get? +export default async function consent( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.consentPOST === undefined) { + return false; + } + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.consentPOST({ + options, + accept: reqBody.accept, + consentChallenge: reqBody.consentChallenge, + grantScope: reqBody.grantScope, + remember: reqBody.remember, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.consentGET === undefined) { + return false; + } + const consentChallenge = + options.req.getKeyValueFromQuery("consentChallenge") ?? + options.req.getKeyValueFromQuery("consent_challenge"); + if (consentChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.consentGET({ + options, + consentChallenge, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index 3cba9819d..d27fc68bc 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -13,8 +13,162 @@ * under the License. */ +import SuperTokens from "../../../supertokens"; import { APIInterface } from "../types"; export default function getAPIImplementation(): APIInterface { - return {}; + return { + loginGET: async ({ loginChallenge, options, session, userContext }) => { + const request = await options.recipeImplementation.getLoginRequest({ + challenge: loginChallenge, + userContext, + }); + + if (request.skip) { + const accept = await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: request.subject, + userContext, + }); + return { redirectTo: accept.redirectTo }; + } else if (session) { + if (session.getUserId() !== request.subject) { + // TODO? + } + const accept = await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: session.getUserId(), + userContext, + }); + return { redirectTo: accept.redirectTo }; + } + const appInfo = SuperTokens.getInstanceOrThrowError().appInfo; + const websiteDomain = appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous(); + const websiteBasePath = appInfo.websiteBasePath.getAsStringDangerous(); + // TODO: + return { + redirectTo: + websiteDomain + + websiteBasePath + + `?hint=${request.oidcContext?.login_hint ?? ""}&redirectToPath=${encodeURIComponent( + "/continue-/auth/oauth2/login?login_challenge=" + loginChallenge + )}`, + }; + }, + loginPOST: async ({ loginChallenge, accept, options, session, userContext }) => { + const res = accept + ? await options.recipeImplementation.acceptLoginRequest({ + challenge: loginChallenge, + subject: session.getUserId(), + userContext, + }) + : await options.recipeImplementation.rejectLoginRequest({ + challenge: loginChallenge, + error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + return { redirectTo: res.redirectTo }; + }, + + logoutGET: async ({ logoutChallenge, options, userContext }) => { + const request = await options.recipeImplementation.getLogoutRequest({ + challenge: logoutChallenge, + userContext, + }); + + const appInfo = SuperTokens.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + + appInfo.websiteBasePath.getAsStringDangerous() + + `/logout?challenge=${request.challenge}`, + }; + }, + + logoutPOST: async ({ logoutChallenge, accept, options, userContext }) => { + if (accept) { + const res = await options.recipeImplementation.acceptLogoutRequest({ + challenge: logoutChallenge, + userContext, + }); + return { redirectTo: res.redirectTo }; + } + await options.recipeImplementation.rejectLogoutRequest({ + challenge: logoutChallenge, + // error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + const appInfo = SuperTokens.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + appInfo.websiteBasePath.getAsStringDangerous(), + }; + }, + + consentGET: async ({ consentChallenge, options, userContext }) => { + const request = await options.recipeImplementation.getConsentRequest({ + challenge: consentChallenge, + userContext, + }); + + const appInfo = SuperTokens.getInstanceOrThrowError().appInfo; + return { + redirectTo: + appInfo + .getOrigin({ + request: options.req, + userContext: userContext, + }) + .getAsStringDangerous() + + appInfo.websiteBasePath.getAsStringDangerous() + + `/consent?challenge=${request.challenge}&scopes=${request.requestedScope}&client=${request.client}&`, + }; + }, + + consentPOST: async ({ consentChallenge, accept, remember, grantScope, options, userContext }) => { + const request = await options.recipeImplementation.getConsentRequest({ + challenge: consentChallenge, + userContext, + }); + const res = accept + ? await options.recipeImplementation.acceptConsentRequest({ + challenge: consentChallenge, + grantAccessTokenAudience: request.requestedAccessTokenAudience, + remember, + grantScope, + userContext, + }) + : await options.recipeImplementation.rejectConsentRequest({ + challenge: consentChallenge, + error: { error: "access_denied", errorDescription: "The resource owner denied the request" }, + userContext, + }); + return { redirectTo: res.redirectTo }; + }, + authGET: async (input) => { + const res = await input.options.recipeImplementation.authorization({ + params: input.params, + userContext: input.userContext, + }); + return res; + }, + tokenPOST: async (input) => { + return input.options.recipeImplementation.token({ body: input.body, userContext: input.userContext }); + }, + }; } diff --git a/lib/ts/recipe/oauth2/api/login.ts b/lib/ts/recipe/oauth2/api/login.ts new file mode 100644 index 000000000..f1912059a --- /dev/null +++ b/lib/ts/recipe/oauth2/api/login.ts @@ -0,0 +1,76 @@ +/* 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 { normaliseHttpMethod, send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import Session from "../../session"; +import { UserContext } from "../../../types"; + +// TODO: separate post and get? +export default async function login( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.loginPOST === undefined) { + return false; + } + const session = await Session.getSession(options.req, options.res, { sessionRequired: true }, userContext); + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.loginPOST({ + options, + accept: reqBody.accept, + loginChallenge: reqBody.loginChallenge, + session, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.loginGET === undefined) { + return false; + } + + let session; + try { + session = await Session.getSession(options.req, options.res, { sessionRequired: false }, userContext); + } catch { + // TODO: Claim validation failure + } + + // TODO: take only one + const loginChallenge = + options.req.getKeyValueFromQuery("login_challenge") ?? options.req.getKeyValueFromQuery("loginChallenge"); + if (loginChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.loginGET({ + options, + loginChallenge, + session, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} diff --git a/lib/ts/recipe/oauth2/api/logout.ts b/lib/ts/recipe/oauth2/api/logout.ts new file mode 100644 index 000000000..c827a42e4 --- /dev/null +++ b/lib/ts/recipe/oauth2/api/logout.ts @@ -0,0 +1,63 @@ +/* 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 { normaliseHttpMethod, send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; + +// TODO: separate post and get? +export default async function logout( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (normaliseHttpMethod(options.req.getMethod()) === "post") { + if (apiImplementation.logoutPOST === undefined) { + return false; + } + const reqBody = await options.req.getJSONBody(); + let response = await apiImplementation.logoutPOST({ + options, + accept: reqBody.accept, + logoutChallenge: reqBody.logoutChallenge, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } else { + if (apiImplementation.logoutGET === undefined) { + return false; + } + + const logoutChallenge = options.req.getKeyValueFromQuery("logoutChallenge"); + if (logoutChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.logoutGET({ + options, + logoutChallenge, + userContext, + }); + if ("status" in response) { + send200Response(options.res, response); + } else { + options.res.original.redirect(response.redirectTo); + } + } + return true; +} diff --git a/lib/ts/recipe/oauth2/api/token.ts b/lib/ts/recipe/oauth2/api/token.ts new file mode 100644 index 000000000..1f4b85b60 --- /dev/null +++ b/lib/ts/recipe/oauth2/api/token.ts @@ -0,0 +1,37 @@ +/* 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 { send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; + +export default async function tokenPOST( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (apiImplementation.tokenPOST === undefined) { + return false; + } + + let response = await apiImplementation.tokenPOST({ + options, + body: options.req.getFormData(), + userContext, + }); + + send200Response(options.res, response); + return true; +} diff --git a/lib/ts/recipe/oauth2/constants.ts b/lib/ts/recipe/oauth2/constants.ts index 172ca6987..51daa6b6c 100644 --- a/lib/ts/recipe/oauth2/constants.ts +++ b/lib/ts/recipe/oauth2/constants.ts @@ -14,3 +14,9 @@ */ export const OAUTH2_BASE_PATH = "/oauth2/"; + +export const LOGIN_PATH = "/oauth2/login"; +export const LOGOUT_PATH = "/oauth2/logout"; +export const CONSENT_PATH = "/oauth2/consent"; +export const AUTH_PATH = "/oauth2/auth"; +export const TOKEN_PATH = "/oauth2/token"; diff --git a/lib/ts/recipe/oauth2/recipe.ts b/lib/ts/recipe/oauth2/recipe.ts index 72aa025ae..1de4a59fc 100644 --- a/lib/ts/recipe/oauth2/recipe.ts +++ b/lib/ts/recipe/oauth2/recipe.ts @@ -20,7 +20,13 @@ import NormalisedURLPath from "../../normalisedURLPath"; import { Querier } from "../../querier"; import RecipeModule from "../../recipeModule"; import { APIHandled, HTTPMethod, NormalisedAppinfo, RecipeListFunction, UserContext } from "../../types"; +import authGET from "./api/auth"; +import consentAPI from "./api/consent"; import APIImplementation from "./api/implementation"; +import loginAPI from "./api/login"; +import logoutAPI from "./api/logout"; +import tokenPOST from "./api/token"; +import { AUTH_PATH, CONSENT_PATH, LOGIN_PATH, LOGOUT_PATH, TOKEN_PATH } from "./constants"; import RecipeImplementation from "./recipeImplementation"; import { APIInterface, RecipeInterface, TypeInput, TypeNormalisedInput } from "./types"; import { validateAndNormaliseUserInput } from "./utils"; @@ -54,6 +60,9 @@ export default class Recipe extends RecipeModule { /* Init functions */ + static getInstance(): Recipe | undefined { + return Recipe.instance; + } static getInstanceOrThrowError(): Recipe { if (Recipe.instance !== undefined) { return Recipe.instance; @@ -82,28 +91,92 @@ export default class Recipe extends RecipeModule { /* RecipeModule functions */ getAPIsHandled(): APIHandled[] { - return []; + return [ + { + method: "post", + pathWithoutApiBasePath: new NormalisedURLPath(LOGIN_PATH), + id: LOGIN_PATH, + disabled: this.apiImpl.loginPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new NormalisedURLPath(LOGIN_PATH), + id: LOGIN_PATH, + disabled: this.apiImpl.loginGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new NormalisedURLPath(LOGOUT_PATH), + id: LOGOUT_PATH, + disabled: this.apiImpl.logoutPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new NormalisedURLPath(LOGOUT_PATH), + id: LOGOUT_PATH, + disabled: this.apiImpl.logoutGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new NormalisedURLPath(CONSENT_PATH), + id: CONSENT_PATH, + disabled: this.apiImpl.consentPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new NormalisedURLPath(CONSENT_PATH), + id: CONSENT_PATH, + disabled: this.apiImpl.consentGET === undefined, + }, + { + method: "post", + pathWithoutApiBasePath: new NormalisedURLPath(TOKEN_PATH), + id: TOKEN_PATH, + disabled: this.apiImpl.tokenPOST === undefined, + }, + { + method: "get", + pathWithoutApiBasePath: new NormalisedURLPath(AUTH_PATH), + id: AUTH_PATH, + disabled: this.apiImpl.authGET === undefined, + }, + ]; } handleAPIRequest = async ( - _id: string, + id: string, _tenantId: string | undefined, - _req: BaseRequest, - _res: BaseResponse, + req: BaseRequest, + res: BaseResponse, _path: NormalisedURLPath, _method: HTTPMethod, - _userContext: UserContext + userContext: UserContext ): Promise => { - // let options = { - // config: this.config, - // recipeId: this.getRecipeId(), - // isInServerlessEnv: this.isInServerlessEnv, - // recipeImplementation: this.recipeInterfaceImpl, - // req, - // res, - // }; - - throw new Error("Not implemented"); + let options = { + config: this.config, + recipeId: this.getRecipeId(), + isInServerlessEnv: this.isInServerlessEnv, + recipeImplementation: this.recipeInterfaceImpl, + req, + res, + }; + + if (id === LOGIN_PATH) { + return loginAPI(this.apiImpl, options, userContext); + } + if (id === LOGOUT_PATH) { + return logoutAPI(this.apiImpl, options, userContext); + } + if (id === CONSENT_PATH) { + return consentAPI(this.apiImpl, options, userContext); + } + if (id === TOKEN_PATH) { + return tokenPOST(this.apiImpl, options, userContext); + } + if (id === AUTH_PATH) { + return authGET(this.apiImpl, options, userContext); + } + throw new Error("Should never come here: handleAPIRequest called with unknown id"); }; handleError(error: error, _: BaseRequest, __: BaseResponse, _userContext: UserContext): Promise { diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index 1bd119bb2..266308164 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -13,14 +13,198 @@ * under the License. */ +import NormalisedURLPath from "../../normalisedURLPath"; import { Querier } from "../../querier"; import { NormalisedAppinfo } from "../../types"; -import { RecipeInterface, TypeNormalisedInput } from "./types"; +import { ConsentRequest, LoginRequest, LogoutRequest, RecipeInterface, TypeNormalisedInput } from "./types"; export default function getRecipeInterface( - _querier: Querier, + querier: Querier, _config: TypeNormalisedInput, _appInfo: NormalisedAppinfo ): RecipeInterface { - return {}; + return { + getLoginRequest: async function (this: RecipeInterface, input): Promise { + const resp = await querier.sendGetRequest( + new NormalisedURLPath("/recipe/oauth2/admin/oauth2/auth/requests/login"), + { login_challenge: input.challenge }, + input.userContext + ); + + return { + challenge: resp.challenge, + client: resp.client, + oidcContext: resp.oidc_context, + requestUrl: resp.request_url, + requestedAccessTokenAudience: resp.requested_access_token_audience, + requestedScope: resp.requested_scope, + sessionId: resp.session_id, + skip: resp.skip, + subject: resp.subject, + }; + }, + acceptLoginRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { + const resp = await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/login/accept`), + { + acr: input.acr, + amr: input.amr, + context: input.context, + extend_session_lifespan: input.extendSessionLifespan, + force_subject_identifier: input.forceSubjectIdentifier, + identity_provider_session_id: input.identityProviderSessionId, + remember: input.remember, + remember_for: input.rememberFor, + subject: input.subject, + }, + { + login_challenge: input.challenge, + }, + input.userContext + ); + + return { redirectTo: resp.redirect_to }; + }, + rejectLoginRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { + const resp = await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/login/reject`), + { + error: input.error.error, + error_debug: input.error.errorDebug, + error_description: input.error.errorDescription, + error_hint: input.error.errorHint, + status_code: input.error.statusCode, + }, + { + login_challenge: input.challenge, + }, + input.userContext + ); + + return { redirectTo: resp.redirect_to }; + }, + getConsentRequest: async function (this: RecipeInterface, input): Promise { + const resp = await querier.sendGetRequest( + new NormalisedURLPath("/recipe/oauth2/admin/oauth2/auth/requests/consent"), + { consent_challenge: input.challenge }, + input.userContext + ); + + return { + acr: resp.acr, + amr: resp.amr, + challenge: resp.challenge, + client: resp.client, + context: resp.context, + loginChallenge: resp.login_challenge, + loginSessionId: resp.login_session_id, + oidcContext: resp.oidc_context, + requestUrl: resp.request_url, + requestedAccessTokenAudience: resp.requested_access_token_audience, + requestedScope: resp.requested_scope, + skip: resp.skip, + subject: resp.subject, + }; + }, + acceptConsentRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { + const resp = await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/consent/accept`), + { + context: input.context, + grant_access_token_audience: input.grantAccessTokenAudience, + grant_scope: input.grantScope, + handled_at: input.handledAt, + remember: input.remember, + remember_for: input.rememberFor, + session: input.session, + }, + { + consent_challenge: input.challenge, + }, + input.userContext + ); + + return { redirectTo: resp.redirect_to }; + }, + + rejectConsentRequest: async function (this: RecipeInterface, input) { + const resp = await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/consent/reject`), + { + error: input.error.error, + error_debug: input.error.errorDebug, + error_description: input.error.errorDescription, + error_hint: input.error.errorHint, + status_code: input.error.statusCode, + }, + { + consent_challenge: input.challenge, + }, + input.userContext + ); + + return { redirectTo: resp.redirect_to }; + }, + + getLogoutRequest: async function (this: RecipeInterface, input): Promise { + const resp = await querier.sendGetRequest( + new NormalisedURLPath("/recipe/oauth2/admin/oauth2/auth/requests/logout"), + { logout_challenge: input.challenge }, + input.userContext + ); + + return { + challenge: resp.challenge, + client: resp.client, + requestUrl: resp.request_url, + rpInitiated: resp.rp_initiated, + sid: resp.sid, + subject: resp.subject, + }; + }, + acceptLogoutRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { + const resp = await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/consent/logout/accept`), + {}, + { + logout_challenge: input.challenge, + }, + input.userContext + ); + + return { redirectTo: resp.redirect_to }; + }, + rejectLogoutRequest: async function (this: RecipeInterface, input): Promise { + await querier.sendPutRequest( + new NormalisedURLPath(`/recipe/oauth2/admin/oauth2/auth/requests/consent/logout/reject`), + {}, + { + logout_challenge: input.challenge, + }, + input.userContext + ); + }, + authorization: async function (this: RecipeInterface, input) { + const resp = await querier.sendGetRequestWithResponseHeaders( + new NormalisedURLPath(`/recipe/oauth2/pub/auth`), + input.params, + input.userContext + ); + + const redirectTo = resp.headers.get("Location")!; + if (redirectTo === undefined) { + throw new Error(resp.body); + } + return { redirectTo }; + }, + + token: async function (this: RecipeInterface, input) { + // TODO: Untested and suspicios + return querier.sendGetRequest( + new NormalisedURLPath(`/recipe/oauth2/pub/token`), + input.body, + input.userContext + ); + }, + }; } diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 31bfdbf94..0441a26bc 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -15,6 +15,8 @@ import type { BaseRequest, BaseResponse } from "../../framework"; import OverrideableBuilder from "supertokens-js-override"; +import { GeneralErrorResponse, JSONObject, UserContext } from "../../types"; +import { SessionContainerInterface } from "../session/types"; export type TypeInput = { override?: { @@ -45,6 +47,284 @@ export type APIOptions = { res: BaseResponse; }; -export type RecipeInterface = {}; +export type OAuth2Client = {}; -export type APIInterface = {}; +export type ErrorOAuth2 = { + // The error should follow the OAuth2 error format (e.g. invalid_request, login_required). + // Defaults to request_denied. + error: string; + + // Description of the error in a human readable format. + errorDescription: string; + + // Debug contains information to help resolve the problem as a developer. Usually not exposed to the public but only in the server logs. + errorDebug?: string; + + // Hint to help resolve the error. + errorHint?: string; + + // Represents the HTTP status code of the error (e.g. 401 or 403) + // Defaults to 400 + statusCode?: number; +}; + +export type ConsentRequest = { + // ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. + acr?: string; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + amr?: string[]; + + // ID is the identifier ("authorization challenge") of the consent authorization request. It is used to identify the session. + challenge: string; + + // OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. + client?: OAuth2Client; + + // any (JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.) + context?: JSONObject; + + // LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate a login and consent request in the login & consent app. + loginChallenge?: string; + + // LoginSessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the "sid" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user. + loginSessionId?: string; + + // object (Contains optional information about the OpenID Connect request.) + oidcContext?: any; + + // RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters. + requestUrl?: string; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + requestedAccessTokenAudience?: string[]; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + requestedScope?: string[]; + + // Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you must not ask the user to grant the requested scopes. You must however either allow or deny the consent request using the usual API call. + skip?: boolean; + + // Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. + subject?: string; +}; + +export type LoginRequest = { + // ID is the identifier ("login challenge") of the login request. It is used to identify the session. + challenge: string; + + // OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. + client: OAuth2Client; + + // object (Contains optional information about the OpenID Connect request.) + oidcContext?: any; + + // RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters. + requestUrl: string; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + requestedAccessTokenAudience?: string[]; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + requestedScope?: string[]; + + // SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag) this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false) this will be a new random value. This value is used as the "sid" parameter in the ID Token and in OIDC Front-/Back- channel logout. It's value can generally be used to associate consecutive login requests by a certain user. + sessionId?: string; + + // Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. + // This feature allows you to update / set session information. + skip: boolean; + + // Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and skip is true, you MUST include this subject type when accepting the login request, or the request will fail. + subject: string; +}; + +export type LogoutRequest = { + // Challenge is the identifier ("logout challenge") of the logout authentication request. It is used to identify the session. + challenge: string; + + // OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. + client: OAuth2Client; + + // RequestURL is the original Logout URL requested. + requestUrl: string; + + // RPInitiated is set to true if the request was initiated by a Relying Party (RP), also known as an OAuth 2.0 Client. + rpInitiated: boolean; + + // SessionID is the login session ID that was requested to log out. + sid: string; + + // Subject is the user for whom the logout was request. + subject: string; +}; + +export type TokenInfo = { + // The access token issued by the authorization server. + accessToken: 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 + expiresIn: number; + // To retrieve a refresh token request the id_token scope. + idToken: string; + // The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request. + refreshToken: string; + // The scope of the access token + scope: string; + // The type of the token issued + tokenType: string; +}; + +export type RecipeInterface = { + authorization(input: { params: any; userContext: UserContext }): Promise<{ redirectTo: string }>; + token(input: { body: any; userContext: UserContext }): Promise; + getConsentRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptConsentRequest(input: { + challenge: string; + + // any (JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.) + context?: any; + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + grantAccessTokenAudience?: string[]; + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + grantScope?: string[]; + // string (NullTime implements sql.NullTime functionality.) + handledAt?: string[]; + // Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client asks the same user for the same, or a subset of, scope. + remember?: boolean; + + // RememberFor sets how long the consent authorization should be remembered for in seconds. If set to 0, the authorization will be remembered indefinitely. integer + rememberFor?: number; + + // object (Pass session data to a consent request.) + session?: any; + userContext: UserContext; + }): Promise<{ redirectTo: string }>; + + rejectConsentRequest(input: { + challenge: string; + error: ErrorOAuth2; + userContext: UserContext; + }): Promise<{ redirectTo: string }>; + + getLoginRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptLoginRequest(input: { + challenge: string; + + // ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. + acr?: string; + + // Array of strings (StringSliceJSONFormat represents []string{} which is encoded to/from JSON for SQL storage.) + amr?: string[]; + + // any (JSONRawMessage represents a json.RawMessage that works well with JSON, SQL, and Swagger.) + context?: any; + + // Extend OAuth2 authentication session lifespan + // If set to true, the OAuth2 authentication cookie lifespan is extended. This is for example useful if you want the user to be able to use prompt=none continuously. + // This value can only be set to true if the user has an authentication, which is the case if the skip value is true. + extendSessionLifespan?: boolean; + + // ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject ("user") identifier that is unique to the client. + // Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. + forceSubjectIdentifier?: string; + + // Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if pairwise is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via subject_type key in the client's configuration). + // Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). + // If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. + + // IdentityProviderSessionID is the session ID of the end-user that authenticated. If specified, we will use this value to propagate the logout. + identityProviderSessionId?: string; + + // Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. + remember?: boolean; + + // RememberFor sets how long the authentication should be remembered for in seconds. If set to 0, the authorization will be remembered for the duration of the browser session (using a session cookie). integer + rememberFor?: number; + + // Subject is the user ID of the end-user that authenticated. + subject: string; + userContext: UserContext; + }): Promise<{ redirectTo: string }>; + rejectLoginRequest(input: { + challenge: string; + error: ErrorOAuth2; + userContext: UserContext; + }): Promise<{ redirectTo: string }>; + + getLogoutRequest(input: { challenge: string; userContext: UserContext }): Promise; + acceptLogoutRequest(input: { challenge: string; userContext: UserContext }): Promise<{ redirectTo: string }>; + rejectLogoutRequest(input: { challenge: string; userContext: UserContext }): Promise; +}; + +export type APIInterface = { + // TODO: add json versions? + loginGET: + | undefined + | ((input: { + loginChallenge: string; + options: APIOptions; + session?: SessionContainerInterface; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + + loginPOST: + | undefined + | ((input: { + loginChallenge: string; + accept: boolean; + session: SessionContainerInterface; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + + logoutGET: + | undefined + | ((input: { + logoutChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + + logoutPOST: + | undefined + | ((input: { + logoutChallenge: string; + accept: boolean; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + + consentGET: + | undefined + | ((input: { + consentChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + + consentPOST: + | undefined + | ((input: { + consentChallenge: string; + accept: boolean; + grantScope: string[]; + remember: boolean; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | GeneralErrorResponse>); + authGET: + | undefined + | ((input: { + params: any; + options: APIOptions; + userContext: UserContext; + }) => Promise<{ redirectTo: string } | ErrorOAuth2 | GeneralErrorResponse>); + tokenPOST: + | undefined + | ((input: { + body: any; + options: APIOptions; + userContext: UserContext; + }) => Promise); +}; diff --git a/lib/ts/recipe/openid/api/getOpenIdDiscoveryConfiguration.ts b/lib/ts/recipe/openid/api/getOpenIdDiscoveryConfiguration.ts index 90c291574..7a331169b 100644 --- a/lib/ts/recipe/openid/api/getOpenIdDiscoveryConfiguration.ts +++ b/lib/ts/recipe/openid/api/getOpenIdDiscoveryConfiguration.ts @@ -34,6 +34,11 @@ export default async function getOpenIdDiscoveryConfiguration( send200Response(options.res, { issuer: result.issuer, jwks_uri: result.jwks_uri, + authorization_endpoint: result.authorization_endpoint, + token_endpoint: result.token_endpoint, + subject_types_supported: result.subject_types_supported, + id_token_signing_alg_values_supported: result.id_token_signing_alg_values_supported, + response_types_supported: result.response_types_supported, }); } else { send200Response(options.res, result); diff --git a/lib/ts/recipe/openid/api/implementation.ts b/lib/ts/recipe/openid/api/implementation.ts index ee1f83e21..72921dffd 100644 --- a/lib/ts/recipe/openid/api/implementation.ts +++ b/lib/ts/recipe/openid/api/implementation.ts @@ -12,18 +12,11 @@ * License for the specific language governing permissions and limitations * under the License. */ -import { APIInterface, APIOptions } from "../types"; -import { GeneralErrorResponse, UserContext } from "../../../types"; +import { APIInterface } from "../types"; export default function getAPIImplementation(): APIInterface { return { - getOpenIdDiscoveryConfigurationGET: async function ({ - options, - userContext, - }: { - options: APIOptions; - userContext: UserContext; - }): Promise<{ status: "OK"; issuer: string; jwks_uri: string } | GeneralErrorResponse> { + getOpenIdDiscoveryConfigurationGET: async function ({ options, userContext }) { return await options.recipeImplementation.getOpenIdDiscoveryConfiguration({ userContext }); }, }; diff --git a/lib/ts/recipe/openid/recipe.ts b/lib/ts/recipe/openid/recipe.ts index 1dc802de5..1726f9c43 100644 --- a/lib/ts/recipe/openid/recipe.ts +++ b/lib/ts/recipe/openid/recipe.ts @@ -44,7 +44,9 @@ export default class OpenIdRecipe extends RecipeModule { override: this.config.override.jwtFeature, }); - let builder = new OverrideableBuilder(RecipeImplementation(this.config, this.jwtRecipe.recipeInterfaceImpl)); + let builder = new OverrideableBuilder( + RecipeImplementation(this.config, this.jwtRecipe.recipeInterfaceImpl, appInfo) + ); this.recipeImplementation = builder.override(this.config.override.functions).build(); diff --git a/lib/ts/recipe/openid/recipeImplementation.ts b/lib/ts/recipe/openid/recipeImplementation.ts index f161e8d23..2ed40f6f5 100644 --- a/lib/ts/recipe/openid/recipeImplementation.ts +++ b/lib/ts/recipe/openid/recipeImplementation.ts @@ -16,26 +16,31 @@ import { RecipeInterface, TypeNormalisedInput } from "./types"; import { RecipeInterface as JWTRecipeInterface, JsonWebKey } from "../jwt/types"; import NormalisedURLPath from "../../normalisedURLPath"; import { GET_JWKS_API } from "../jwt/constants"; -import { UserContext } from "../../types"; +import { NormalisedAppinfo, UserContext } from "../../types"; +import { AUTH_PATH, TOKEN_PATH } from "../oauth2/constants"; export default function getRecipeInterface( config: TypeNormalisedInput, - jwtRecipeImplementation: JWTRecipeInterface + jwtRecipeImplementation: JWTRecipeInterface, + appInfo: NormalisedAppinfo ): RecipeInterface { return { - getOpenIdDiscoveryConfiguration: async function (): Promise<{ - status: "OK"; - issuer: string; - jwks_uri: string; - }> { + getOpenIdDiscoveryConfiguration: async function () { let issuer = config.issuerDomain.getAsStringDangerous() + config.issuerPath.getAsStringDangerous(); let jwks_uri = config.issuerDomain.getAsStringDangerous() + config.issuerPath.appendPath(new NormalisedURLPath(GET_JWKS_API)).getAsStringDangerous(); + + const apiBasePath = appInfo.apiDomain.getAsStringDangerous() + appInfo.apiBasePath.getAsStringDangerous(); return { status: "OK", issuer, jwks_uri, + authorization_endpoint: apiBasePath + AUTH_PATH, + token_endpoint: apiBasePath + TOKEN_PATH, + subject_types_supported: ["public"], + id_token_signing_alg_values_supported: ["RS256"], + response_types_supported: ["code", "id_token", "id_token token"], }; }, createJWT: async function ({ diff --git a/lib/ts/recipe/openid/types.ts b/lib/ts/recipe/openid/types.ts index c303cb0ca..22aa651a5 100644 --- a/lib/ts/recipe/openid/types.ts +++ b/lib/ts/recipe/openid/types.ts @@ -83,6 +83,11 @@ export type APIInterface = { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; } | GeneralErrorResponse >); @@ -95,6 +100,11 @@ export type RecipeInterface = { status: "OK"; issuer: string; jwks_uri: string; + authorization_endpoint: string; + token_endpoint: string; + subject_types_supported: string[]; + id_token_signing_alg_values_supported: string[]; + response_types_supported: string[]; }>; createJWT(input: { payload?: any; diff --git a/lib/ts/recipe/passwordless/recipeImplementation.ts b/lib/ts/recipe/passwordless/recipeImplementation.ts index 71ab078b0..920a3bc02 100644 --- a/lib/ts/recipe/passwordless/recipeImplementation.ts +++ b/lib/ts/recipe/passwordless/recipeImplementation.ts @@ -164,6 +164,7 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { let response = await querier.sendPutRequest( new NormalisedURLPath(`/recipe/user`), copyAndRemoveUserContextAndTenantId(input), + {}, input.userContext ); if (response.status !== "OK") { diff --git a/lib/ts/recipe/session/sessionFunctions.ts b/lib/ts/recipe/session/sessionFunctions.ts index 8dae9d1b1..30d1726e9 100644 --- a/lib/ts/recipe/session/sessionFunctions.ts +++ b/lib/ts/recipe/session/sessionFunctions.ts @@ -500,6 +500,7 @@ export async function updateSessionDataInDatabase( sessionHandle, userDataInDatabase: newSessionData, }, + {}, userContext ); if (response.status === "UNAUTHORISED") { @@ -522,6 +523,7 @@ export async function updateAccessTokenPayload( sessionHandle, userDataInJWT: newAccessTokenPayload, }, + {}, userContext ); if (response.status === "UNAUTHORISED") { diff --git a/lib/ts/recipe/totp/recipeImplementation.ts b/lib/ts/recipe/totp/recipeImplementation.ts index 745f23cf2..a87a35c39 100644 --- a/lib/ts/recipe/totp/recipeImplementation.ts +++ b/lib/ts/recipe/totp/recipeImplementation.ts @@ -123,6 +123,7 @@ export default function getRecipeInterface(querier: Querier, config: TypeNormali existingDeviceName: input.existingDeviceName, newDeviceName: input.newDeviceName, }, + {}, input.userContext ); }, diff --git a/lib/ts/recipe/usermetadata/recipeImplementation.ts b/lib/ts/recipe/usermetadata/recipeImplementation.ts index 51ab77ea5..2ab9d24e2 100644 --- a/lib/ts/recipe/usermetadata/recipeImplementation.ts +++ b/lib/ts/recipe/usermetadata/recipeImplementation.ts @@ -30,6 +30,7 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { userId, metadataUpdate, }, + {}, userContext ); }, diff --git a/lib/ts/recipe/userroles/recipeImplementation.ts b/lib/ts/recipe/userroles/recipeImplementation.ts index eaee083f7..7141a8bfe 100644 --- a/lib/ts/recipe/userroles/recipeImplementation.ts +++ b/lib/ts/recipe/userroles/recipeImplementation.ts @@ -24,6 +24,7 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { return querier.sendPutRequest( new NormalisedURLPath(`/${tenantId === undefined ? DEFAULT_TENANT_ID : tenantId}/recipe/user/role`), { userId, role }, + {}, userContext ); }, @@ -55,7 +56,12 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { }, createNewRoleOrAddPermissions: function ({ role, permissions, userContext }) { - return querier.sendPutRequest(new NormalisedURLPath("/recipe/role"), { role, permissions }, userContext); + return querier.sendPutRequest( + new NormalisedURLPath("/recipe/role"), + { role, permissions }, + {}, + userContext + ); }, getPermissionsForRole: function ({ role, userContext }) { diff --git a/lib/ts/supertokens.ts b/lib/ts/supertokens.ts index fe95fc396..104542b6e 100644 --- a/lib/ts/supertokens.ts +++ b/lib/ts/supertokens.ts @@ -333,6 +333,7 @@ export default class SuperTokens { userIdType: input.userIdType, externalUserIdInfo: input.externalUserIdInfo, }, + {}, input.userContext ); } else { diff --git a/lib/ts/utils.ts b/lib/ts/utils.ts index 82aabe181..968e96e1b 100644 --- a/lib/ts/utils.ts +++ b/lib/ts/utils.ts @@ -18,11 +18,13 @@ export const doFetch: typeof fetch = (input: RequestInfo | URL, init?: RequestIn ProcessState.getInstance().addState(PROCESS_STATE.ADDING_NO_CACHE_HEADER_IN_FETCH); init = { cache: "no-cache", + redirect: "manual", }; } else { if (init.cache === undefined) { ProcessState.getInstance().addState(PROCESS_STATE.ADDING_NO_CACHE_HEADER_IN_FETCH); init.cache = "no-cache"; + init.redirect = "manual"; } } const fetchFunction = typeof fetch !== "undefined" ? fetch : crossFetch; diff --git a/test/querier.test.js b/test/querier.test.js index 22c9646da..64ff73480 100644 --- a/test/querier.test.js +++ b/test/querier.test.js @@ -193,7 +193,7 @@ describe(`Querier: ${printPath("[test/querier.test.js]")}`, function () { assert.equal(await q.sendPostRequest(new NormalisedURLPath("/hello"), {}, {}), "Hello\n"); let hostsAlive = q.getHostsAliveForTesting(); assert.equal(hostsAlive.size, 2); - assert.equal(await q.sendPutRequest(new NormalisedURLPath("/hello"), {}, {}), "Hello\n"); // this will be the 4th API call + assert.equal(await q.sendPutRequest(new NormalisedURLPath("/hello"), {}, {}, {}), "Hello\n"); // this will be the 4th API call hostsAlive = q.getHostsAliveForTesting(); assert.equal(hostsAlive.size, 2); assert.equal(hostsAlive.has(connectionURI), true); diff --git a/test/with-typescript/index.ts b/test/with-typescript/index.ts index f16582249..4a2a3d500 100644 --- a/test/with-typescript/index.ts +++ b/test/with-typescript/index.ts @@ -1593,6 +1593,11 @@ Session.init({ getOpenIdDiscoveryConfiguration: async (input) => ({ issuer: "your issuer", jwks_uri: "https://your.api.domain/auth/jwt/jwks.json", + token_endpoint: "http://localhost:3000/auth/oauth2/token", + authorization_endpoint: "http://localhost:3000/auth/oauth2/auth", + id_token_signing_alg_values_supported: [], + response_types_supported: [], + subject_types_supported: [], status: "OK", }), }), From 508bfad6b129d47d52363a33a8131404294ac7bf Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Fri, 21 Jun 2024 22:24:59 +0530 Subject: [PATCH 02/16] feat: Add an api to get login info --- lib/build/recipe/oauth2/api/implementation.js | 13 ++++++ lib/build/recipe/oauth2/api/loginInfo.d.ts | 8 ++++ lib/build/recipe/oauth2/api/loginInfo.js | 38 ++++++++++++++++ lib/build/recipe/oauth2/constants.d.ts | 1 + lib/build/recipe/oauth2/constants.js | 3 +- lib/build/recipe/oauth2/recipe.js | 10 +++++ .../recipe/oauth2/recipeImplementation.js | 2 +- lib/build/recipe/oauth2/types.d.ts | 14 ++++++ lib/ts/recipe/oauth2/api/implementation.ts | 14 ++++++ lib/ts/recipe/oauth2/api/loginInfo.ts | 44 +++++++++++++++++++ lib/ts/recipe/oauth2/constants.ts | 1 + lib/ts/recipe/oauth2/recipe.ts | 12 ++++- lib/ts/recipe/oauth2/recipeImplementation.ts | 2 +- lib/ts/recipe/oauth2/types.ts | 20 +++++++++ 14 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 lib/build/recipe/oauth2/api/loginInfo.d.ts create mode 100644 lib/build/recipe/oauth2/api/loginInfo.js create mode 100644 lib/ts/recipe/oauth2/api/loginInfo.ts diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index 0a728d0e2..1363c212c 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -173,6 +173,19 @@ function getAPIImplementation() { tokenPOST: async (input) => { return input.options.recipeImplementation.token({ body: input.body, userContext: input.userContext }); }, + loginInfoGET: async ({ loginChallenge, options, userContext }) => { + const { client } = await options.recipeImplementation.getLoginRequest({ + challenge: loginChallenge, + userContext, + }); + return { + clientName: client.clientName, + tosUri: client.tosUri, + policyUri: client.policyUri, + logoUri: client.logoUri, + metadata: client.metadata, + }; + }, }; } exports.default = getAPIImplementation; diff --git a/lib/build/recipe/oauth2/api/loginInfo.d.ts b/lib/build/recipe/oauth2/api/loginInfo.d.ts new file mode 100644 index 000000000..536858263 --- /dev/null +++ b/lib/build/recipe/oauth2/api/loginInfo.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; +export default function loginInfoGET( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise; diff --git a/lib/build/recipe/oauth2/api/loginInfo.js b/lib/build/recipe/oauth2/api/loginInfo.js new file mode 100644 index 000000000..b1d06cd64 --- /dev/null +++ b/lib/build/recipe/oauth2/api/loginInfo.js @@ -0,0 +1,38 @@ +"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 }); +const utils_1 = require("../../../utils"); +async function loginInfoGET(apiImplementation, options, userContext) { + var _a; + if (apiImplementation.loginInfoGET === undefined) { + return false; + } + const loginChallenge = + (_a = options.req.getKeyValueFromQuery("login_challenge")) !== null && _a !== void 0 + ? _a + : options.req.getKeyValueFromQuery("loginChallenge"); + if (loginChallenge === undefined) { + throw new Error("TODO"); + } + let response = await apiImplementation.loginInfoGET({ + options, + loginChallenge, + userContext, + }); + utils_1.send200Response(options.res, response); + return true; +} +exports.default = loginInfoGET; diff --git a/lib/build/recipe/oauth2/constants.d.ts b/lib/build/recipe/oauth2/constants.d.ts index cff481248..e5fea5b40 100644 --- a/lib/build/recipe/oauth2/constants.d.ts +++ b/lib/build/recipe/oauth2/constants.d.ts @@ -5,3 +5,4 @@ export declare const LOGOUT_PATH = "/oauth2/logout"; export declare const CONSENT_PATH = "/oauth2/consent"; export declare const AUTH_PATH = "/oauth2/auth"; export declare const TOKEN_PATH = "/oauth2/token"; +export declare const LOGIN_INFO_PATH = "/oauth2/login/info"; diff --git a/lib/build/recipe/oauth2/constants.js b/lib/build/recipe/oauth2/constants.js index 6eb5434b8..f249f20d4 100644 --- a/lib/build/recipe/oauth2/constants.js +++ b/lib/build/recipe/oauth2/constants.js @@ -14,10 +14,11 @@ * under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); -exports.TOKEN_PATH = exports.AUTH_PATH = exports.CONSENT_PATH = exports.LOGOUT_PATH = exports.LOGIN_PATH = exports.OAUTH2_BASE_PATH = void 0; +exports.LOGIN_INFO_PATH = exports.TOKEN_PATH = exports.AUTH_PATH = exports.CONSENT_PATH = exports.LOGOUT_PATH = exports.LOGIN_PATH = exports.OAUTH2_BASE_PATH = void 0; exports.OAUTH2_BASE_PATH = "/oauth2/"; exports.LOGIN_PATH = "/oauth2/login"; exports.LOGOUT_PATH = "/oauth2/logout"; exports.CONSENT_PATH = "/oauth2/consent"; exports.AUTH_PATH = "/oauth2/auth"; exports.TOKEN_PATH = "/oauth2/token"; +exports.LOGIN_INFO_PATH = "/oauth2/login/info"; diff --git a/lib/build/recipe/oauth2/recipe.js b/lib/build/recipe/oauth2/recipe.js index ef7673b5e..8b031b748 100644 --- a/lib/build/recipe/oauth2/recipe.js +++ b/lib/build/recipe/oauth2/recipe.js @@ -29,6 +29,7 @@ const implementation_1 = __importDefault(require("./api/implementation")); const login_1 = __importDefault(require("./api/login")); const logout_1 = __importDefault(require("./api/logout")); const token_1 = __importDefault(require("./api/token")); +const loginInfo_1 = __importDefault(require("./api/loginInfo")); const constants_1 = require("./constants"); const recipeImplementation_1 = __importDefault(require("./recipeImplementation")); const utils_1 = require("./utils"); @@ -60,6 +61,9 @@ class Recipe extends recipeModule_1.default { if (id === constants_1.AUTH_PATH) { return auth_1.default(this.apiImpl, options, userContext); } + if (id === constants_1.LOGIN_INFO_PATH) { + return loginInfo_1.default(this.apiImpl, options, userContext); + } throw new Error("Should never come here: handleAPIRequest called with unknown id"); }; this.config = utils_1.validateAndNormaliseUserInput(this, appInfo, config); @@ -156,6 +160,12 @@ class Recipe extends recipeModule_1.default { id: constants_1.AUTH_PATH, disabled: this.apiImpl.authGET === undefined, }, + { + method: "get", + pathWithoutApiBasePath: new normalisedURLPath_1.default(constants_1.LOGIN_INFO_PATH), + id: constants_1.LOGIN_INFO_PATH, + disabled: this.apiImpl.loginInfoGET === undefined, + }, ]; } handleError(error, _, __, _userContext) { diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index 907ab666c..96c1a3ad7 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -32,7 +32,7 @@ function getRecipeInterface(querier, _config, _appInfo) { ); return { challenge: resp.challenge, - client: resp.client, + client: OAuth2Client_1.OAuth2Client.fromAPIResponse(resp.client), oidcContext: resp.oidc_context, requestUrl: resp.request_url, requestedAccessTokenAudience: resp.requested_access_token_audience, diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index 848ba8875..c87fe0315 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -79,6 +79,13 @@ export declare type TokenInfo = { scope: string; tokenType: string; }; +export declare type LoginInfo = { + clientName: string; + tosUri: string; + policyUri: string; + logoUri: string; + metadata?: Record | null; +}; export declare type RecipeInterface = { authorization(input: { params: any; @@ -296,6 +303,13 @@ export declare type APIInterface = { options: APIOptions; userContext: UserContext; }) => Promise); + loginInfoGET: + | undefined + | ((input: { + loginChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise); }; export declare type OAuth2ClientOptions = { clientId: string; diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index d27fc68bc..42b230fd3 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -170,5 +170,19 @@ export default function getAPIImplementation(): APIInterface { tokenPOST: async (input) => { return input.options.recipeImplementation.token({ body: input.body, userContext: input.userContext }); }, + loginInfoGET: async ({ loginChallenge, options, userContext }) => { + const { client } = await options.recipeImplementation.getLoginRequest({ + challenge: loginChallenge, + userContext, + }); + + return { + clientName: client.clientName, + tosUri: client.tosUri, + policyUri: client.policyUri, + logoUri: client.logoUri, + metadata: client.metadata, + }; + }, }; } diff --git a/lib/ts/recipe/oauth2/api/loginInfo.ts b/lib/ts/recipe/oauth2/api/loginInfo.ts new file mode 100644 index 000000000..b0d6241be --- /dev/null +++ b/lib/ts/recipe/oauth2/api/loginInfo.ts @@ -0,0 +1,44 @@ +/* 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 { send200Response } from "../../../utils"; +import { APIInterface, APIOptions } from ".."; +import { UserContext } from "../../../types"; + +export default async function loginInfoGET( + apiImplementation: APIInterface, + options: APIOptions, + userContext: UserContext +): Promise { + if (apiImplementation.loginInfoGET === undefined) { + return false; + } + + const loginChallenge = + options.req.getKeyValueFromQuery("login_challenge") ?? options.req.getKeyValueFromQuery("loginChallenge"); + + if (loginChallenge === undefined) { + throw new Error("TODO"); + } + + let response = await apiImplementation.loginInfoGET({ + options, + loginChallenge, + userContext, + }); + + send200Response(options.res, response); + return true; +} diff --git a/lib/ts/recipe/oauth2/constants.ts b/lib/ts/recipe/oauth2/constants.ts index 51daa6b6c..ddfdef4d6 100644 --- a/lib/ts/recipe/oauth2/constants.ts +++ b/lib/ts/recipe/oauth2/constants.ts @@ -20,3 +20,4 @@ export const LOGOUT_PATH = "/oauth2/logout"; export const CONSENT_PATH = "/oauth2/consent"; export const AUTH_PATH = "/oauth2/auth"; export const TOKEN_PATH = "/oauth2/token"; +export const LOGIN_INFO_PATH = "/oauth2/login/info"; diff --git a/lib/ts/recipe/oauth2/recipe.ts b/lib/ts/recipe/oauth2/recipe.ts index 06e7b14eb..0f6b66d26 100644 --- a/lib/ts/recipe/oauth2/recipe.ts +++ b/lib/ts/recipe/oauth2/recipe.ts @@ -26,7 +26,8 @@ import APIImplementation from "./api/implementation"; import loginAPI from "./api/login"; import logoutAPI from "./api/logout"; import tokenPOST from "./api/token"; -import { AUTH_PATH, CONSENT_PATH, LOGIN_PATH, LOGOUT_PATH, TOKEN_PATH } from "./constants"; +import loginInfoGET from "./api/loginInfo"; +import { AUTH_PATH, CONSENT_PATH, LOGIN_INFO_PATH, LOGIN_PATH, LOGOUT_PATH, TOKEN_PATH } from "./constants"; import RecipeImplementation from "./recipeImplementation"; import { APIInterface, RecipeInterface, TypeInput, TypeNormalisedInput } from "./types"; import { validateAndNormaliseUserInput } from "./utils"; @@ -140,6 +141,12 @@ export default class Recipe extends RecipeModule { id: AUTH_PATH, disabled: this.apiImpl.authGET === undefined, }, + { + method: "get", + pathWithoutApiBasePath: new NormalisedURLPath(LOGIN_INFO_PATH), + id: LOGIN_INFO_PATH, + disabled: this.apiImpl.loginInfoGET === undefined, + }, ]; } @@ -176,6 +183,9 @@ export default class Recipe extends RecipeModule { if (id === AUTH_PATH) { return authGET(this.apiImpl, options, userContext); } + if (id === LOGIN_INFO_PATH) { + return loginInfoGET(this.apiImpl, options, userContext); + } throw new Error("Should never come here: handleAPIRequest called with unknown id"); }; diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index b5eacf935..87723d268 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -35,7 +35,7 @@ export default function getRecipeInterface( return { challenge: resp.challenge, - client: resp.client, + client: OAuth2Client.fromAPIResponse(resp.client), oidcContext: resp.oidc_context, requestUrl: resp.request_url, requestedAccessTokenAudience: resp.requested_access_token_audience, diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 30533239c..3755b485c 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -174,6 +174,19 @@ export type TokenInfo = { tokenType: string; }; +export type LoginInfo = { + // The name of the client. + clientName: string; + // The URI of the client's terms of service. + tosUri: string; + // The URI of the client's privacy policy. + policyUri: string; + // The URI of the client's logo. + logoUri: string; + // The metadata associated with the client. + metadata?: Record | null; +}; + export type RecipeInterface = { authorization(input: { params: any; userContext: UserContext }): Promise<{ redirectTo: string }>; token(input: { body: any; userContext: UserContext }): Promise; @@ -387,6 +400,13 @@ export type APIInterface = { options: APIOptions; userContext: UserContext; }) => Promise); + loginInfoGET: + | undefined + | ((input: { + loginChallenge: string; + options: APIOptions; + userContext: UserContext; + }) => Promise); }; export type OAuth2ClientOptions = { From 849c6547a5039008c16897552e4c33dcc85b6b32 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 27 Jun 2024 01:36:50 +0200 Subject: [PATCH 03/16] fix: merge issues and FE path --- lib/build/querier.js | 2 +- lib/build/recipe/oauth2/api/implementation.js | 4 +- .../recipe/oauth2/recipeImplementation.js | 66 +++++++++---------- lib/ts/querier.ts | 2 +- lib/ts/recipe/oauth2/api/implementation.ts | 4 +- lib/ts/recipe/oauth2/recipeImplementation.ts | 66 +++++++++---------- 6 files changed, 70 insertions(+), 74 deletions(-) diff --git a/lib/build/querier.js b/lib/build/querier.js index b9679cb8a..9b75a073e 100644 --- a/lib/build/querier.js +++ b/lib/build/querier.js @@ -574,5 +574,5 @@ async function handleHydraAPICall(response) { headers: response.headers, }; } - return { body: { status: response.ok ? "OK" : "ERROR", headers: response.headers } }; + return { body: { status: response.ok ? "OK" : "ERROR" }, headers: response.headers }; } diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index 0a728d0e2..c8e7045a3 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -64,9 +64,7 @@ function getAPIImplementation() { _b !== void 0 ? _b : "" - }&redirectToPath=${encodeURIComponent( - "/continue-/auth/oauth2/login?login_challenge=" + loginChallenge - )}`, + }&loginChallenge=${loginChallenge}`, }; }, loginPOST: async ({ loginChallenge, accept, options, session, userContext }) => { diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index 907ab666c..8f49fe9c9 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -31,15 +31,15 @@ function getRecipeInterface(querier, _config, _appInfo) { input.userContext ); return { - challenge: resp.challenge, - client: resp.client, - oidcContext: resp.oidc_context, - requestUrl: resp.request_url, - requestedAccessTokenAudience: resp.requested_access_token_audience, - requestedScope: resp.requested_scope, - sessionId: resp.session_id, - skip: resp.skip, - subject: resp.subject, + challenge: resp.data.challenge, + client: resp.data.client, + oidcContext: resp.data.oidc_context, + requestUrl: resp.data.request_url, + requestedAccessTokenAudience: resp.data.requested_access_token_audience, + requestedScope: resp.data.requested_scope, + sessionId: resp.data.session_id, + skip: resp.data.skip, + subject: resp.data.subject, }; }, acceptLoginRequest: async function (input) { @@ -61,7 +61,7 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectLoginRequest: async function (input) { const resp = await querier.sendPutRequest( @@ -78,7 +78,7 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, getConsentRequest: async function (input) { const resp = await querier.sendGetRequest( @@ -87,19 +87,19 @@ function getRecipeInterface(querier, _config, _appInfo) { input.userContext ); return { - acr: resp.acr, - amr: resp.amr, - challenge: resp.challenge, - client: resp.client, - context: resp.context, - loginChallenge: resp.login_challenge, - loginSessionId: resp.login_session_id, - oidcContext: resp.oidc_context, - requestUrl: resp.request_url, - requestedAccessTokenAudience: resp.requested_access_token_audience, - requestedScope: resp.requested_scope, - skip: resp.skip, - subject: resp.subject, + acr: resp.data.acr, + amr: resp.data.amr, + challenge: resp.data.challenge, + client: resp.data.client, + context: resp.data.context, + loginChallenge: resp.data.login_challenge, + loginSessionId: resp.data.login_session_id, + oidcContext: resp.data.oidc_context, + requestUrl: resp.data.request_url, + requestedAccessTokenAudience: resp.data.requested_access_token_audience, + requestedScope: resp.data.requested_scope, + skip: resp.data.skip, + subject: resp.data.subject, }; }, acceptConsentRequest: async function (input) { @@ -119,7 +119,7 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectConsentRequest: async function (input) { const resp = await querier.sendPutRequest( @@ -136,7 +136,7 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, getLogoutRequest: async function (input) { const resp = await querier.sendGetRequest( @@ -145,12 +145,12 @@ function getRecipeInterface(querier, _config, _appInfo) { input.userContext ); return { - challenge: resp.challenge, - client: resp.client, - requestUrl: resp.request_url, - rpInitiated: resp.rp_initiated, - sid: resp.sid, - subject: resp.subject, + challenge: resp.data.challenge, + client: resp.data.client, + requestUrl: resp.data.request_url, + rpInitiated: resp.data.rp_initiated, + sid: resp.data.sid, + subject: resp.data.subject, }; }, acceptLogoutRequest: async function (input) { @@ -162,7 +162,7 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectLogoutRequest: async function (input) { await querier.sendPutRequest( diff --git a/lib/ts/querier.ts b/lib/ts/querier.ts index 244e95e31..ae12c5362 100644 --- a/lib/ts/querier.ts +++ b/lib/ts/querier.ts @@ -667,5 +667,5 @@ async function handleHydraAPICall(response: Response) { }; } - return { body: { status: response.ok ? "OK" : "ERROR", headers: response.headers } }; + return { body: { status: response.ok ? "OK" : "ERROR" }, headers: response.headers }; } diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index d27fc68bc..e1cb66252 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -55,9 +55,7 @@ export default function getAPIImplementation(): APIInterface { redirectTo: websiteDomain + websiteBasePath + - `?hint=${request.oidcContext?.login_hint ?? ""}&redirectToPath=${encodeURIComponent( - "/continue-/auth/oauth2/login?login_challenge=" + loginChallenge - )}`, + `?hint=${request.oidcContext?.login_hint ?? ""}&loginChallenge=${loginChallenge}`, }; }, loginPOST: async ({ loginChallenge, accept, options, session, userContext }) => { diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index b5eacf935..3a84a1128 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -34,15 +34,15 @@ export default function getRecipeInterface( ); return { - challenge: resp.challenge, - client: resp.client, - oidcContext: resp.oidc_context, - requestUrl: resp.request_url, - requestedAccessTokenAudience: resp.requested_access_token_audience, - requestedScope: resp.requested_scope, - sessionId: resp.session_id, - skip: resp.skip, - subject: resp.subject, + challenge: resp.data.challenge, + client: resp.data.client, + oidcContext: resp.data.oidc_context, + requestUrl: resp.data.request_url, + requestedAccessTokenAudience: resp.data.requested_access_token_audience, + requestedScope: resp.data.requested_scope, + sessionId: resp.data.session_id, + skip: resp.data.skip, + subject: resp.data.subject, }; }, acceptLoginRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { @@ -65,7 +65,7 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectLoginRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { const resp = await querier.sendPutRequest( @@ -83,7 +83,7 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, getConsentRequest: async function (this: RecipeInterface, input): Promise { const resp = await querier.sendGetRequest( @@ -93,19 +93,19 @@ export default function getRecipeInterface( ); return { - acr: resp.acr, - amr: resp.amr, - challenge: resp.challenge, - client: resp.client, - context: resp.context, - loginChallenge: resp.login_challenge, - loginSessionId: resp.login_session_id, - oidcContext: resp.oidc_context, - requestUrl: resp.request_url, - requestedAccessTokenAudience: resp.requested_access_token_audience, - requestedScope: resp.requested_scope, - skip: resp.skip, - subject: resp.subject, + acr: resp.data.acr, + amr: resp.data.amr, + challenge: resp.data.challenge, + client: resp.data.client, + context: resp.data.context, + loginChallenge: resp.data.login_challenge, + loginSessionId: resp.data.login_session_id, + oidcContext: resp.data.oidc_context, + requestUrl: resp.data.request_url, + requestedAccessTokenAudience: resp.data.requested_access_token_audience, + requestedScope: resp.data.requested_scope, + skip: resp.data.skip, + subject: resp.data.subject, }; }, acceptConsentRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { @@ -126,7 +126,7 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectConsentRequest: async function (this: RecipeInterface, input) { @@ -145,7 +145,7 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, getLogoutRequest: async function (this: RecipeInterface, input): Promise { @@ -156,12 +156,12 @@ export default function getRecipeInterface( ); return { - challenge: resp.challenge, - client: resp.client, - requestUrl: resp.request_url, - rpInitiated: resp.rp_initiated, - sid: resp.sid, - subject: resp.subject, + challenge: resp.data.challenge, + client: resp.data.client, + requestUrl: resp.data.request_url, + rpInitiated: resp.data.rp_initiated, + sid: resp.data.sid, + subject: resp.data.subject, }; }, acceptLogoutRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { @@ -174,7 +174,7 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.redirect_to }; + return { redirectTo: resp.data.redirect_to }; }, rejectLogoutRequest: async function (this: RecipeInterface, input): Promise { await querier.sendPutRequest( From 5bcedd6ae51329fed4d83a9ab48e9ce5226689cc Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 27 Jun 2024 04:01:15 +0200 Subject: [PATCH 04/16] fix: WIP fix for CSRF and redirection issues --- lib/build/querier.d.ts | 3 + lib/build/querier.js | 21 ++--- lib/build/recipe/jwt/recipeImplementation.js | 1 + lib/build/recipe/oauth2/api/auth.js | 7 ++ lib/build/recipe/oauth2/api/implementation.js | 1 + .../recipe/oauth2/recipeImplementation.js | 80 ++++++++++++++++--- lib/build/recipe/oauth2/types.d.ts | 4 + lib/ts/querier.ts | 10 ++- lib/ts/recipe/jwt/recipeImplementation.ts | 1 + lib/ts/recipe/oauth2/api/auth.ts | 7 ++ lib/ts/recipe/oauth2/api/implementation.ts | 1 + lib/ts/recipe/oauth2/recipeImplementation.ts | 75 ++++++++++++++--- lib/ts/recipe/oauth2/types.ts | 9 ++- 13 files changed, 187 insertions(+), 33 deletions(-) diff --git a/lib/build/querier.d.ts b/lib/build/querier.d.ts index dceac2ec0..6d7607b4b 100644 --- a/lib/build/querier.d.ts +++ b/lib/build/querier.d.ts @@ -3,6 +3,8 @@ import NormalisedURLDomain from "./normalisedURLDomain"; import NormalisedURLPath from "./normalisedURLPath"; import { UserContext } from "./types"; import { NetworkInterceptor } from "./types"; +export declare const hydraPubDomain: string; +export declare const hydraPubPathPrefix = "/recipe/oauth2/pub"; export declare class Querier { private static initCalled; private static hosts; @@ -44,6 +46,7 @@ export declare class Querier { sendGetRequestWithResponseHeaders: ( path: NormalisedURLPath, params: Record, + inpHeaders: Record | undefined, userContext: UserContext ) => Promise<{ body: any; diff --git a/lib/build/querier.js b/lib/build/querier.js index 9b75a073e..8f1a41977 100644 --- a/lib/build/querier.js +++ b/lib/build/querier.js @@ -6,7 +6,7 @@ var __importDefault = }; var _a, _b; Object.defineProperty(exports, "__esModule", { value: true }); -exports.Querier = void 0; +exports.Querier = exports.hydraPubPathPrefix = exports.hydraPubDomain = void 0; /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. * * This software is licensed under the Apache License, Version 2.0 (the @@ -27,9 +27,9 @@ const normalisedURLPath_1 = __importDefault(require("./normalisedURLPath")); const processState_1 = require("./processState"); const constants_1 = require("./constants"); const logger_1 = require("./logger"); -const hydraPubDomain = (_a = process.env.HYDRA_PUB) !== null && _a !== void 0 ? _a : "http://localhost:4444"; // This will be used as a domain for paths starting with hydraPubPathPrefix +exports.hydraPubDomain = (_a = process.env.HYDRA_PUB) !== null && _a !== void 0 ? _a : "http://localhost:4444"; // This will be used as a domain for paths starting with hydraPubPathPrefix const hydraAdmDomain = (_b = process.env.HYDRA_ADM) !== null && _b !== void 0 ? _b : "http://localhost:4445"; // This will be used as a domain for paths starting with hydraAdmPathPrefix -const hydraPubPathPrefix = "/recipe/oauth2/pub"; // Replaced with "/oauth2" when sending the request (/recipe/oauth2/pub/token -> /oauth2/token) +exports.hydraPubPathPrefix = "/recipe/oauth2/pub"; // Replaced with "/oauth2" when sending the request (/recipe/oauth2/pub/token -> /oauth2/token) const hydraAdmPathPrefix = "/recipe/oauth2/admin"; // Replaced with "/admin" when sending the request (/recipe/oauth2/admin/clients -> /admin/clients) class Querier { // we have rIdToCore so that recipes can force change the rId sent to core. This is a hack until the core is able @@ -273,14 +273,15 @@ class Querier { ); return respBody; }; - this.sendGetRequestWithResponseHeaders = async (path, params, userContext) => { + this.sendGetRequestWithResponseHeaders = async (path, params, inpHeaders, userContext) => { var _a; return await this.sendRequestHelper( path, "GET", async (url) => { let apiVersion = await this.getAPIVersion(); - let headers = { "cdi-version": apiVersion }; + let headers = inpHeaders !== null && inpHeaders !== void 0 ? inpHeaders : {}; + headers["cdi-version"] = apiVersion; if (Querier.apiKey !== undefined) { headers = Object.assign(Object.assign({}, headers), { "api-key": Querier.apiKey }); } @@ -430,10 +431,11 @@ class Querier { let currentDomain = this.__hosts[Querier.lastTriedIndex].domain.getAsStringDangerous(); let currentBasePath = this.__hosts[Querier.lastTriedIndex].basePath.getAsStringDangerous(); let strPath = path.getAsStringDangerous(); - const isHydraAPICall = strPath.startsWith(hydraAdmPathPrefix) || strPath.startsWith(hydraPubPathPrefix); - if (strPath.startsWith(hydraPubPathPrefix)) { - currentDomain = hydraPubDomain; - strPath = strPath.replace(hydraPubPathPrefix, "/oauth2"); + const isHydraAPICall = + strPath.startsWith(hydraAdmPathPrefix) || strPath.startsWith(exports.hydraPubPathPrefix); + if (strPath.startsWith(exports.hydraPubPathPrefix)) { + currentDomain = exports.hydraPubDomain; + strPath = strPath.replace(exports.hydraPubPathPrefix, "/oauth2"); } if (strPath.startsWith(hydraAdmPathPrefix)) { currentDomain = hydraAdmDomain; @@ -559,6 +561,7 @@ Querier.networkInterceptor = undefined; Querier.globalCacheTag = Date.now(); Querier.disableCache = false; async function handleHydraAPICall(response) { + console.log({ hydraResponse: response, text: await response.clone().text() }); const contentType = response.headers.get("Content-Type"); if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith("application/json")) { return { diff --git a/lib/build/recipe/jwt/recipeImplementation.js b/lib/build/recipe/jwt/recipeImplementation.js index 073f14b88..0ac8ddf6f 100644 --- a/lib/build/recipe/jwt/recipeImplementation.js +++ b/lib/build/recipe/jwt/recipeImplementation.js @@ -54,6 +54,7 @@ function getRecipeInterface(querier, config, appInfo) { const { body, headers } = await querier.sendGetRequestWithResponseHeaders( new normalisedURLPath_1.default("/.well-known/jwks.json"), {}, + undefined, userContext ); let validityInSeconds = defaultJWKSMaxAge; diff --git a/lib/build/recipe/oauth2/api/auth.js b/lib/build/recipe/oauth2/api/auth.js index a67d42c32..90103ef66 100644 --- a/lib/build/recipe/oauth2/api/auth.js +++ b/lib/build/recipe/oauth2/api/auth.js @@ -25,9 +25,16 @@ async function authGET(apiImplementation, options, userContext) { let response = await apiImplementation.authGET({ options, params: Object.fromEntries(params.entries()), + cookie: options.req.getHeaderValue("cookie"), userContext, }); if ("redirectTo" in response) { + // TODO: + if (response.setCookie) { + for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) { + options.res.setHeader("set-cookie", c, true); + } + } options.res.original.redirect(response.redirectTo); } else { utils_1.send200Response(options.res, response); diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index c8e7045a3..2167eaa84 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -164,6 +164,7 @@ function getAPIImplementation() { authGET: async (input) => { const res = await input.options.recipeImplementation.authorization({ params: input.params, + cookies: input.cookie, userContext: input.userContext, }); return res; diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index 8f49fe9c9..f4559d5de 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -20,6 +20,7 @@ var __importDefault = }; Object.defineProperty(exports, "__esModule", { value: true }); const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); +const querier_1 = require("../../querier"); const utils_1 = require("../../utils"); const OAuth2Client_1 = require("./OAuth2Client"); function getRecipeInterface(querier, _config, _appInfo) { @@ -32,7 +33,7 @@ function getRecipeInterface(querier, _config, _appInfo) { ); return { challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client_1.OAuth2Client.fromAPIResponse(resp.data.client), oidcContext: resp.data.oidc_context, requestUrl: resp.data.request_url, requestedAccessTokenAudience: resp.data.requested_access_token_audience, @@ -61,7 +62,13 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + querier_1.hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectLoginRequest: async function (input) { const resp = await querier.sendPutRequest( @@ -78,7 +85,13 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + querier_1.hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, getConsentRequest: async function (input) { const resp = await querier.sendGetRequest( @@ -90,7 +103,7 @@ function getRecipeInterface(querier, _config, _appInfo) { acr: resp.data.acr, amr: resp.data.amr, challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client_1.OAuth2Client.fromAPIResponse(resp.data.client), context: resp.data.context, loginChallenge: resp.data.login_challenge, loginSessionId: resp.data.login_session_id, @@ -119,7 +132,13 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + querier_1.hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectConsentRequest: async function (input) { const resp = await querier.sendPutRequest( @@ -136,7 +155,13 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + querier_1.hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, getLogoutRequest: async function (input) { const resp = await querier.sendGetRequest( @@ -146,7 +171,7 @@ function getRecipeInterface(querier, _config, _appInfo) { ); return { challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client_1.OAuth2Client.fromAPIResponse(resp.data.client), requestUrl: resp.data.request_url, rpInitiated: resp.data.rp_initiated, sid: resp.data.sid, @@ -162,7 +187,13 @@ function getRecipeInterface(querier, _config, _appInfo) { }, input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + querier_1.hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectLogoutRequest: async function (input) { await querier.sendPutRequest( @@ -175,16 +206,47 @@ function getRecipeInterface(querier, _config, _appInfo) { ); }, authorization: async function (input) { + var _a, _b, _c; const resp = await querier.sendGetRequestWithResponseHeaders( new normalisedURLPath_1.default(`/recipe/oauth2/pub/auth`), input.params, + { + Cookie: `${input.cookies}`, + }, input.userContext ); const redirectTo = resp.headers.get("Location"); if (redirectTo === undefined) { throw new Error(resp.body); } - return { redirectTo }; + const redirectToURL = new URL(redirectTo); + const consentChallenge = redirectToURL.searchParams.get("consent_challenge"); + if (consentChallenge !== null) { + const consentRequest = await this.getConsentRequest({ + challenge: consentChallenge, + userContext: input.userContext, + }); + if ( + consentRequest.skip || + ((_a = consentRequest.client) === null || _a === void 0 ? void 0 : _a.skipConsent) + ) { + const consentRes = await this.acceptConsentRequest( + Object.assign(Object.assign({}, input), { + challenge: consentRequest.challenge, + grantAccessTokenAudience: consentRequest.requestedAccessTokenAudience, + grantScope: consentRequest.requestedScope, + }) + ); + return { + redirectTo: consentRes.redirectTo, + setCookie: (_b = resp.headers.get("set-cookie")) !== null && _b !== void 0 ? _b : undefined, + }; + } + } + return { + redirectTo, + setCookie: (_c = resp.headers.get("set-cookie")) !== null && _c !== void 0 ? _c : undefined, + }; }, token: async function (input) { // TODO: Untested and suspicios diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index 848ba8875..c6173b500 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -82,9 +82,11 @@ export declare type TokenInfo = { export declare type RecipeInterface = { authorization(input: { params: any; + cookies: string | undefined; userContext: UserContext; }): Promise<{ redirectTo: string; + setCookie: string | undefined; }>; token(input: { body: any; userContext: UserContext }): Promise; getConsentRequest(input: { challenge: string; userContext: UserContext }): Promise; @@ -280,11 +282,13 @@ export declare type APIInterface = { | undefined | ((input: { params: any; + cookie: string | undefined; options: APIOptions; userContext: UserContext; }) => Promise< | { redirectTo: string; + setCookie: string | undefined; } | ErrorOAuth2 | GeneralErrorResponse diff --git a/lib/ts/querier.ts b/lib/ts/querier.ts index ae12c5362..da0d059e0 100644 --- a/lib/ts/querier.ts +++ b/lib/ts/querier.ts @@ -22,9 +22,9 @@ import { logDebugMessage } from "./logger"; import { UserContext } from "./types"; import { NetworkInterceptor } from "./types"; -const hydraPubDomain = process.env.HYDRA_PUB ?? "http://localhost:4444"; // This will be used as a domain for paths starting with hydraPubPathPrefix +export const hydraPubDomain = process.env.HYDRA_PUB ?? "http://localhost:4444"; // This will be used as a domain for paths starting with hydraPubPathPrefix const hydraAdmDomain = process.env.HYDRA_ADM ?? "http://localhost:4445"; // This will be used as a domain for paths starting with hydraAdmPathPrefix -const hydraPubPathPrefix = "/recipe/oauth2/pub"; // Replaced with "/oauth2" when sending the request (/recipe/oauth2/pub/token -> /oauth2/token) +export const hydraPubPathPrefix = "/recipe/oauth2/pub"; // Replaced with "/oauth2" when sending the request (/recipe/oauth2/pub/token -> /oauth2/token) const hydraAdmPathPrefix = "/recipe/oauth2/admin"; // Replaced with "/admin" when sending the request (/recipe/oauth2/admin/clients -> /admin/clients) export class Querier { @@ -352,6 +352,7 @@ export class Querier { sendGetRequestWithResponseHeaders = async ( path: NormalisedURLPath, params: Record, + inpHeaders: Record | undefined, userContext: UserContext ): Promise<{ body: any; headers: Headers }> => { return await this.sendRequestHelper( @@ -359,7 +360,9 @@ export class Querier { "GET", async (url: string) => { let apiVersion = await this.getAPIVersion(); - let headers: any = { "cdi-version": apiVersion }; + let headers: any = inpHeaders ?? {}; + headers["cdi-version"] = apiVersion; + if (Querier.apiKey !== undefined) { headers = { ...headers, @@ -650,6 +653,7 @@ export class Querier { } async function handleHydraAPICall(response: Response) { + console.log({ hydraResponse: response, text: await response.clone().text() }); const contentType = response.headers.get("Content-Type"); if (contentType?.startsWith("application/json")) { diff --git a/lib/ts/recipe/jwt/recipeImplementation.ts b/lib/ts/recipe/jwt/recipeImplementation.ts index dc8656124..fa937c881 100644 --- a/lib/ts/recipe/jwt/recipeImplementation.ts +++ b/lib/ts/recipe/jwt/recipeImplementation.ts @@ -78,6 +78,7 @@ export default function getRecipeInterface( const { body, headers } = await querier.sendGetRequestWithResponseHeaders( new NormalisedURLPath("/.well-known/jwks.json"), {}, + undefined, userContext ); let validityInSeconds = defaultJWKSMaxAge; diff --git a/lib/ts/recipe/oauth2/api/auth.ts b/lib/ts/recipe/oauth2/api/auth.ts index cdcb6fe37..802d408ef 100644 --- a/lib/ts/recipe/oauth2/api/auth.ts +++ b/lib/ts/recipe/oauth2/api/auth.ts @@ -32,9 +32,16 @@ export default async function authGET( let response = await apiImplementation.authGET({ options, params: Object.fromEntries(params.entries()), + cookie: options.req.getHeaderValue("cookie"), userContext, }); if ("redirectTo" in response) { + // TODO: + if (response.setCookie) { + for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) { + options.res.setHeader("set-cookie", c, true); + } + } options.res.original.redirect(response.redirectTo); } else { send200Response(options.res, response); diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index e1cb66252..4b3f28d8c 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -161,6 +161,7 @@ export default function getAPIImplementation(): APIInterface { authGET: async (input) => { const res = await input.options.recipeImplementation.authorization({ params: input.params, + cookies: input.cookie, userContext: input.userContext, }); return res; diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index 3a84a1128..207ab1a02 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -14,7 +14,7 @@ */ import NormalisedURLPath from "../../normalisedURLPath"; -import { Querier } from "../../querier"; +import { Querier, hydraPubDomain } from "../../querier"; import { NormalisedAppinfo } from "../../types"; import { RecipeInterface, TypeNormalisedInput, ConsentRequest, LoginRequest, LogoutRequest } from "./types"; import { toSnakeCase, transformObjectKeys } from "../../utils"; @@ -35,7 +35,7 @@ export default function getRecipeInterface( return { challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client.fromAPIResponse(resp.data.client), oidcContext: resp.data.oidc_context, requestUrl: resp.data.request_url, requestedAccessTokenAudience: resp.data.requested_access_token_audience, @@ -65,7 +65,13 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectLoginRequest: async function (this: RecipeInterface, input): Promise<{ redirectTo: string }> { const resp = await querier.sendPutRequest( @@ -83,7 +89,13 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, getConsentRequest: async function (this: RecipeInterface, input): Promise { const resp = await querier.sendGetRequest( @@ -96,7 +108,7 @@ export default function getRecipeInterface( acr: resp.data.acr, amr: resp.data.amr, challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client.fromAPIResponse(resp.data.client), context: resp.data.context, loginChallenge: resp.data.login_challenge, loginSessionId: resp.data.login_session_id, @@ -126,7 +138,13 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectConsentRequest: async function (this: RecipeInterface, input) { @@ -145,7 +163,13 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, getLogoutRequest: async function (this: RecipeInterface, input): Promise { @@ -157,7 +181,7 @@ export default function getRecipeInterface( return { challenge: resp.data.challenge, - client: resp.data.client, + client: OAuth2Client.fromAPIResponse(resp.data.client), requestUrl: resp.data.request_url, rpInitiated: resp.data.rp_initiated, sid: resp.data.sid, @@ -174,7 +198,13 @@ export default function getRecipeInterface( input.userContext ); - return { redirectTo: resp.data.redirect_to }; + return { + // TODO: FIXME!!! + redirectTo: resp.data.redirect_to.replace( + hydraPubDomain, + _appInfo.apiDomain.getAsStringDangerous() + _appInfo.apiBasePath.getAsStringDangerous() + ), + }; }, rejectLogoutRequest: async function (this: RecipeInterface, input): Promise { await querier.sendPutRequest( @@ -190,6 +220,9 @@ export default function getRecipeInterface( const resp = await querier.sendGetRequestWithResponseHeaders( new NormalisedURLPath(`/recipe/oauth2/pub/auth`), input.params, + { + Cookie: `${input.cookies}`, + }, input.userContext ); @@ -197,7 +230,29 @@ export default function getRecipeInterface( if (redirectTo === undefined) { throw new Error(resp.body); } - return { redirectTo }; + const redirectToURL = new URL(redirectTo); + const consentChallenge = redirectToURL.searchParams.get("consent_challenge"); + if (consentChallenge !== null) { + const consentRequest = await this.getConsentRequest({ + challenge: consentChallenge, + userContext: input.userContext, + }); + + if (consentRequest.skip || consentRequest.client?.skipConsent) { + const consentRes = await this.acceptConsentRequest({ + ...input, + challenge: consentRequest.challenge, + grantAccessTokenAudience: consentRequest.requestedAccessTokenAudience, + grantScope: consentRequest.requestedScope, + }); + + return { + redirectTo: consentRes.redirectTo, + setCookie: resp.headers.get("set-cookie") ?? undefined, + }; + } + } + return { redirectTo, setCookie: resp.headers.get("set-cookie") ?? undefined }; }, token: async function (this: RecipeInterface, input) { diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 30533239c..f825b2081 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -175,7 +175,11 @@ export type TokenInfo = { }; export type RecipeInterface = { - authorization(input: { params: any; userContext: UserContext }): Promise<{ redirectTo: string }>; + authorization(input: { + params: any; + cookies: string | undefined; + userContext: UserContext; + }): Promise<{ redirectTo: string; setCookie: string | undefined }>; token(input: { body: any; userContext: UserContext }): Promise; getConsentRequest(input: { challenge: string; userContext: UserContext }): Promise; acceptConsentRequest(input: { @@ -377,9 +381,10 @@ export type APIInterface = { | undefined | ((input: { params: any; + cookie: string | undefined; options: APIOptions; userContext: UserContext; - }) => Promise<{ redirectTo: string } | ErrorOAuth2 | GeneralErrorResponse>); + }) => Promise<{ redirectTo: string; setCookie: string | undefined } | ErrorOAuth2 | GeneralErrorResponse>); tokenPOST: | undefined | ((input: { From deb0392c167e64d4d6366e60de32196bb0dee845 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 27 Jun 2024 21:39:11 +0000 Subject: [PATCH 05/16] fix: OAuth2 fixes and test-server updates (#871) --- lib/build/querier.js | 3 +- lib/build/recipe/oauth2/api/auth.js | 21 ++++++++- .../recipe/oauth2/recipeImplementation.js | 14 +++--- lib/ts/querier.ts | 3 +- lib/ts/recipe/oauth2/api/auth.ts | 16 ++++++- lib/ts/recipe/oauth2/recipeImplementation.ts | 13 +++--- package-lock.json | 26 +++++++++-- package.json | 2 + test/test-server/src/index.ts | 24 ++++++++++ test/test-server/src/oauth2.ts | 46 +++++++++++++++++++ 10 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 test/test-server/src/oauth2.ts diff --git a/lib/build/querier.js b/lib/build/querier.js index 8f1a41977..038181ada 100644 --- a/lib/build/querier.js +++ b/lib/build/querier.js @@ -435,10 +435,12 @@ class Querier { strPath.startsWith(hydraAdmPathPrefix) || strPath.startsWith(exports.hydraPubPathPrefix); if (strPath.startsWith(exports.hydraPubPathPrefix)) { currentDomain = exports.hydraPubDomain; + currentBasePath = ""; strPath = strPath.replace(exports.hydraPubPathPrefix, "/oauth2"); } if (strPath.startsWith(hydraAdmPathPrefix)) { currentDomain = hydraAdmDomain; + currentBasePath = ""; strPath = strPath.replace(hydraAdmPathPrefix, "/admin"); } const url = currentDomain + currentBasePath + strPath; @@ -561,7 +563,6 @@ Querier.networkInterceptor = undefined; Querier.globalCacheTag = Date.now(); Querier.disableCache = false; async function handleHydraAPICall(response) { - console.log({ hydraResponse: response, text: await response.clone().text() }); const contentType = response.headers.get("Content-Type"); if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith("application/json")) { return { diff --git a/lib/build/recipe/oauth2/api/auth.js b/lib/build/recipe/oauth2/api/auth.js index 90103ef66..e9ffab92a 100644 --- a/lib/build/recipe/oauth2/api/auth.js +++ b/lib/build/recipe/oauth2/api/auth.js @@ -13,8 +13,14 @@ * 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 utils_1 = require("../../../utils"); +const set_cookie_parser_1 = __importDefault(require("set-cookie-parser")); async function authGET(apiImplementation, options, userContext) { if (apiImplementation.authGET === undefined) { return false; @@ -31,8 +37,19 @@ async function authGET(apiImplementation, options, userContext) { if ("redirectTo" in response) { // TODO: if (response.setCookie) { - for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) { - options.res.setHeader("set-cookie", c, true); + const cookieStr = set_cookie_parser_1.default.splitCookiesString(response.setCookie); + const cookies = set_cookie_parser_1.default.parse(cookieStr); + for (const cookie of cookies) { + options.res.setCookie( + cookie.name, + cookie.value, + cookie.domain, + !!cookie.secure, + !!cookie.httpOnly, + new Date(cookie.expires).getTime(), + cookie.path || "/", + cookie.sameSite + ); } } options.res.original.redirect(response.redirectTo); diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index f4559d5de..d1f29aa71 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -257,20 +257,22 @@ function getRecipeInterface(querier, _config, _appInfo) { ); }, getOAuth2Clients: async function (input, userContext) { - let response = await querier.sendGetRequest( + var _a; + let response = await querier.sendGetRequestWithResponseHeaders( new normalisedURLPath_1.default(`/recipe/oauth2/admin/clients`), Object.assign(Object.assign({}, utils_1.transformObjectKeys(input, "snake-case")), { page_token: input.paginationToken, }), + {}, userContext ); - if (response.status === "OK") { + if (response.body.status === "OK") { // Pagination info is in the Link header, containing comma-separated links: // "first", "next" (if applicable). // Example: Link: ; rel="first", ; rel="next" // We parse the nextPaginationToken from the Link header using RegExp let nextPaginationToken; - const linkHeader = response.headers.get("link"); + const linkHeader = (_a = response.headers.get("link")) !== null && _a !== void 0 ? _a : ""; const nextLinkMatch = linkHeader.match(/<([^>]+)>;\s*rel="next"/); if (nextLinkMatch) { const url = nextLinkMatch[1]; @@ -279,14 +281,14 @@ function getRecipeInterface(querier, _config, _appInfo) { } return { status: "OK", - clients: response.data.map((client) => OAuth2Client_1.OAuth2Client.fromAPIResponse(client)), + clients: response.body.data.map((client) => OAuth2Client_1.OAuth2Client.fromAPIResponse(client)), nextPaginationToken, }; } else { return { status: "ERROR", - error: response.data.error, - errorHint: response.data.errorHint, + error: response.body.data.error, + errorHint: response.body.data.errorHint, }; } }, diff --git a/lib/ts/querier.ts b/lib/ts/querier.ts index da0d059e0..380eb3d40 100644 --- a/lib/ts/querier.ts +++ b/lib/ts/querier.ts @@ -567,11 +567,13 @@ export class Querier { if (strPath.startsWith(hydraPubPathPrefix)) { currentDomain = hydraPubDomain; + currentBasePath = ""; strPath = strPath.replace(hydraPubPathPrefix, "/oauth2"); } if (strPath.startsWith(hydraAdmPathPrefix)) { currentDomain = hydraAdmDomain; + currentBasePath = ""; strPath = strPath.replace(hydraAdmPathPrefix, "/admin"); } @@ -653,7 +655,6 @@ export class Querier { } async function handleHydraAPICall(response: Response) { - console.log({ hydraResponse: response, text: await response.clone().text() }); const contentType = response.headers.get("Content-Type"); if (contentType?.startsWith("application/json")) { diff --git a/lib/ts/recipe/oauth2/api/auth.ts b/lib/ts/recipe/oauth2/api/auth.ts index 802d408ef..b1d133b12 100644 --- a/lib/ts/recipe/oauth2/api/auth.ts +++ b/lib/ts/recipe/oauth2/api/auth.ts @@ -16,6 +16,7 @@ import { send200Response } from "../../../utils"; import { APIInterface, APIOptions } from ".."; import { UserContext } from "../../../types"; +import setCookieParser from "set-cookie-parser"; export default async function authGET( apiImplementation: APIInterface, @@ -38,8 +39,19 @@ export default async function authGET( if ("redirectTo" in response) { // TODO: if (response.setCookie) { - for (const c of response.setCookie.replace(/, (\w+=)/, "\n$1").split("\n")) { - options.res.setHeader("set-cookie", c, true); + const cookieStr = setCookieParser.splitCookiesString(response.setCookie); + const cookies = setCookieParser.parse(cookieStr); + for (const cookie of cookies) { + options.res.setCookie( + cookie.name, + cookie.value, + cookie.domain, + !!cookie.secure, + !!cookie.httpOnly, + new Date(cookie.expires!).getTime(), + cookie.path || "/", + cookie.sameSite as any + ); } } options.res.original.redirect(response.redirectTo); diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index 207ab1a02..4317e442d 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -265,23 +265,24 @@ export default function getRecipeInterface( }, getOAuth2Clients: async function (input, userContext) { - let response = await querier.sendGetRequest( + let response = await querier.sendGetRequestWithResponseHeaders( new NormalisedURLPath(`/recipe/oauth2/admin/clients`), { ...transformObjectKeys(input, "snake-case"), page_token: input.paginationToken, }, + {}, userContext ); - if (response.status === "OK") { + if (response.body.status === "OK") { // Pagination info is in the Link header, containing comma-separated links: // "first", "next" (if applicable). // Example: Link: ; rel="first", ; rel="next" // We parse the nextPaginationToken from the Link header using RegExp let nextPaginationToken: string | undefined; - const linkHeader = response.headers.get("link"); + const linkHeader = response.headers.get("link") ?? ""; const nextLinkMatch = linkHeader.match(/<([^>]+)>;\s*rel="next"/); if (nextLinkMatch) { @@ -292,14 +293,14 @@ export default function getRecipeInterface( return { status: "OK", - clients: response.data.map((client: any) => OAuth2Client.fromAPIResponse(client)), + clients: response.body.data.map((client: any) => OAuth2Client.fromAPIResponse(client)), nextPaginationToken, }; } else { return { status: "ERROR", - error: response.data.error, - errorHint: response.data.errorHint, + error: response.body.data.error, + errorHint: response.body.data.errorHint, }; } }, diff --git a/package-lock.json b/package-lock.json index d4f1b4611..d439ee1f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "nodemailer": "^6.7.2", "pkce-challenge": "^3.0.0", "psl": "1.8.0", + "set-cookie-parser": "^2.6.0", "supertokens-js-override": "^0.0.4", "twilio": "^4.19.3" }, @@ -39,6 +40,7 @@ "@types/koa-bodyparser": "^4.3.3", "@types/nodemailer": "^6.4.4", "@types/psl": "1.1.0", + "@types/set-cookie-parser": "^2.4.9", "@types/validator": "10.11.0", "aws-sdk-mock": "^5.4.0", "body-parser": "1.20.1", @@ -1713,6 +1715,15 @@ "@types/node": "*" } }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.9.tgz", + "integrity": "sha512-bCorlULvl0xTdjj4BPUHX4cqs9I+go2TfW/7Do1nnFYWS0CPP429Qr1AY42kiFhCwLpvAkWFr1XIBHd8j6/MCQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/type-is": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.6.tgz", @@ -7010,8 +7021,7 @@ "node_modules/set-cookie-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", - "dev": true + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" }, "node_modules/set-function-length": { "version": "1.2.2", @@ -9568,6 +9578,15 @@ "@types/node": "*" } }, + "@types/set-cookie-parser": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.9.tgz", + "integrity": "sha512-bCorlULvl0xTdjj4BPUHX4cqs9I+go2TfW/7Do1nnFYWS0CPP429Qr1AY42kiFhCwLpvAkWFr1XIBHd8j6/MCQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/type-is": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.6.tgz", @@ -13642,8 +13661,7 @@ "set-cookie-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", - "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", - "dev": true + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" }, "set-function-length": { "version": "1.2.2", diff --git a/package.json b/package.json index 8f5377af5..2022a0fc6 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "nodemailer": "^6.7.2", "pkce-challenge": "^3.0.0", "psl": "1.8.0", + "set-cookie-parser": "^2.6.0", "supertokens-js-override": "^0.0.4", "twilio": "^4.19.3" }, @@ -143,6 +144,7 @@ "@types/koa-bodyparser": "^4.3.3", "@types/nodemailer": "^6.4.4", "@types/psl": "1.1.0", + "@types/set-cookie-parser": "^2.4.9", "@types/validator": "10.11.0", "aws-sdk-mock": "^5.4.0", "body-parser": "1.20.1", diff --git a/test/test-server/src/index.ts b/test/test-server/src/index.ts index 2856ffa26..0fdb993cb 100644 --- a/test/test-server/src/index.ts +++ b/test/test-server/src/index.ts @@ -19,6 +19,8 @@ import ThirdPartyRecipe from "../../../lib/build/recipe/thirdparty/recipe"; import { TypeInput as ThirdPartyTypeInput } from "../../../lib/build/recipe/thirdparty/types"; import { TypeInput as MFATypeInput } from "../../../lib/build/recipe/multifactorauth/types"; import TOTPRecipe from "../../../lib/build/recipe/totp/recipe"; +import OAuth2Recipe from "../../../lib/build/recipe/oauth2/recipe"; +import { TypeInput as OAuth2TypeInput } from "../../../lib/build/recipe/oauth2/types"; import UserMetadataRecipe from "../../../lib/build/recipe/usermetadata/recipe"; import SuperTokensRecipe from "../../../lib/build/supertokens"; import { RecipeListFunction } from "../../../lib/build/types"; @@ -32,6 +34,7 @@ import Session from "../../../recipe/session"; import { verifySession } from "../../../recipe/session/framework/express"; import ThirdParty from "../../../recipe/thirdparty"; import TOTP from "../../../recipe/totp"; +import OAuth2 from "../../../recipe/oauth2"; import accountlinkingRoutes from "./accountlinking"; import emailpasswordRoutes from "./emailpassword"; import emailverificationRoutes from "./emailverification"; @@ -39,6 +42,7 @@ import { logger } from "./logger"; import multiFactorAuthRoutes from "./multifactorauth"; import multitenancyRoutes from "./multitenancy"; import passwordlessRoutes from "./passwordless"; +import oAuth2Routes from "./oauth2"; import sessionRoutes from "./session"; import supertokensRoutes from "./supertokens"; import thirdPartyRoutes from "./thirdparty"; @@ -81,6 +85,7 @@ function STReset() { ProcessState.getInstance().reset(); MultiFactorAuthRecipe.reset(); TOTPRecipe.reset(); + OAuth2Recipe.reset(); SuperTokensRecipe.reset(); } @@ -237,6 +242,24 @@ function initST(config: any) { if (recipe.recipeId === "totp") { recipeList.push(TOTP.init(config)); } + if (recipe.recipeId === "oauth2") { + let initConfig: OAuth2TypeInput = { + ...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(OAuth2.init(initConfig)); + } }); settings.recipeList = recipeList; @@ -318,6 +341,7 @@ app.use("/test/multifactorauth", multiFactorAuthRoutes); app.use("/test/thirdparty", thirdPartyRoutes); app.use("/test/totp", TOTPRoutes); app.use("/test/usermetadata", userMetadataRoutes); +app.use("/test/oauth2", oAuth2Routes); // *** Custom routes to help with session tests *** app.post("/create", async (req, res, next) => { diff --git a/test/test-server/src/oauth2.ts b/test/test-server/src/oauth2.ts new file mode 100644 index 000000000..d54dff6fe --- /dev/null +++ b/test/test-server/src/oauth2.ts @@ -0,0 +1,46 @@ +import { Router } from "express"; +import OAuth2 from "../../../recipe/oauth2"; +import { logger } from "./logger"; + +const namespace = "com.supertokens:node-test-server:oauth2"; +const { logDebugMessage } = logger(namespace); + +const router = Router() + .post("/getoauth2clients", async (req, res, next) => { + try { + logDebugMessage("OAuth2:getOAuth2Clients %j", req.body); + const response = await OAuth2.getOAuth2Clients(req.body.input, req.body.userContext); + res.json(response); + } catch (e) { + next(e); + } + }) + .post("/createoauth2client", async (req, res, next) => { + try { + logDebugMessage("OAuth2:createOAuth2Client %j", req.body); + const response = await OAuth2.createOAuth2Client(req.body.input, req.body.userContext); + res.json(response); + } catch (e) { + next(e); + } + }) + .post("/updateoauth2client", async (req, res, next) => { + try { + logDebugMessage("OAuth2:updateOAuth2Client %j", req.body); + const response = await OAuth2.updateOAuth2Client(req.body.input, req.body.userContext); + res.json(response); + } catch (e) { + next(e); + } + }) + .post("/deleteoauth2client", async (req, res, next) => { + try { + logDebugMessage("OAuth2:deleteOAuth2Client %j", req.body); + const response = await OAuth2.deleteOAuth2Client(req.body.input, req.body.userContext); + res.json(response); + } catch (e) { + next(e); + } + }); + +export default router; From 3af555ea14d82b3c33133339c9ecb6ec42274272 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 27 Jun 2024 23:54:04 +0200 Subject: [PATCH 06/16] feat: update oauth2 login info endpoint types to match our general patterns --- lib/build/recipe/oauth2/api/implementation.js | 13 ++++++++----- lib/build/recipe/oauth2/types.d.ts | 8 +++++++- lib/ts/recipe/oauth2/api/implementation.ts | 13 ++++++++----- lib/ts/recipe/oauth2/types.ts | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index a5b5ecf92..015c28f19 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -178,11 +178,14 @@ function getAPIImplementation() { userContext, }); return { - clientName: client.clientName, - tosUri: client.tosUri, - policyUri: client.policyUri, - logoUri: client.logoUri, - metadata: client.metadata, + status: "OK", + info: { + clientName: client.clientName, + tosUri: client.tosUri, + policyUri: client.policyUri, + logoUri: client.logoUri, + metadata: client.metadata, + }, }; }, }; diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index 1fcdecd4b..68455859e 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -313,7 +313,13 @@ export declare type APIInterface = { loginChallenge: string; options: APIOptions; userContext: UserContext; - }) => Promise); + }) => Promise< + | { + status: "OK"; + info: LoginInfo; + } + | GeneralErrorResponse + >); }; export declare type OAuth2ClientOptions = { clientId: string; diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index dc56f711f..c8cd1d94a 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -176,11 +176,14 @@ export default function getAPIImplementation(): APIInterface { }); return { - clientName: client.clientName, - tosUri: client.tosUri, - policyUri: client.policyUri, - logoUri: client.logoUri, - metadata: client.metadata, + status: "OK", + info: { + clientName: client.clientName, + tosUri: client.tosUri, + policyUri: client.policyUri, + logoUri: client.logoUri, + metadata: client.metadata, + }, }; }, }; diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 4a02ee63b..059d37b56 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -411,7 +411,7 @@ export type APIInterface = { loginChallenge: string; options: APIOptions; userContext: UserContext; - }) => Promise); + }) => Promise<{ status: "OK"; info: LoginInfo } | GeneralErrorResponse>); }; export type OAuth2ClientOptions = { From ff2135b542a37fec07af44e9d72467e1304cc147 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 28 Jun 2024 01:30:59 +0200 Subject: [PATCH 07/16] fix: make login flow work --- lib/build/querier.js | 8 +++- lib/build/recipe/jwt/api/implementation.js | 15 +++++++ lib/build/recipe/oauth2/api/auth.js | 9 ++++ lib/build/recipe/oauth2/api/implementation.js | 2 + lib/build/recipe/oauth2/api/token.js | 2 +- .../recipe/oauth2/recipeImplementation.js | 45 ++++++++++++++++--- lib/build/recipe/oauth2/types.d.ts | 12 ++--- lib/ts/querier.ts | 8 +++- lib/ts/recipe/jwt/api/implementation.ts | 11 +++++ lib/ts/recipe/oauth2/api/auth.ts | 9 ++++ lib/ts/recipe/oauth2/api/implementation.ts | 2 + lib/ts/recipe/oauth2/api/token.ts | 2 +- lib/ts/recipe/oauth2/recipeImplementation.ts | 34 ++++++++++++-- lib/ts/recipe/oauth2/types.ts | 12 ++--- 14 files changed, 144 insertions(+), 27 deletions(-) diff --git a/lib/build/querier.js b/lib/build/querier.js index 038181ada..dc289267f 100644 --- a/lib/build/querier.js +++ b/lib/build/querier.js @@ -84,6 +84,8 @@ class Querier { this.sendPostRequest = async (path, body, userContext) => { var _a; this.invalidateCoreCallCache(userContext); + // TODO: remove FormData + const isForm = body !== undefined && body instanceof FormData; const { body: respBody } = await this.sendRequestHelper( path, "POST", @@ -91,8 +93,10 @@ class Querier { let apiVersion = await this.getAPIVersion(); let headers = { "cdi-version": apiVersion, - "content-type": "application/json; charset=utf-8", }; + if (!isForm) { + headers["content-type"] = "application/json; charset=utf-8"; + } if (Querier.apiKey !== undefined) { headers = Object.assign(Object.assign({}, headers), { "api-key": Querier.apiKey }); } @@ -117,7 +121,7 @@ class Querier { } return utils_1.doFetch(url, { method: "POST", - body: body !== undefined ? JSON.stringify(body) : undefined, + body: isForm ? body : body !== undefined ? JSON.stringify(body) : undefined, headers, }); }, diff --git a/lib/build/recipe/jwt/api/implementation.js b/lib/build/recipe/jwt/api/implementation.js index e52174f38..d81fc72b0 100644 --- a/lib/build/recipe/jwt/api/implementation.js +++ b/lib/build/recipe/jwt/api/implementation.js @@ -13,7 +13,13 @@ * 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 recipe_1 = __importDefault(require("../../oauth2/recipe")); function getAPIImplementation() { return { getJWKSGET: async function ({ options, userContext }) { @@ -21,6 +27,15 @@ function getAPIImplementation() { if (resp.validityInSeconds !== undefined) { options.res.setHeader("Cache-Control", `max-age=${resp.validityInSeconds}, must-revalidate`, false); } + const oauth2 = recipe_1.default.getInstance(); + // TODO: dirty hack until we get core support + if (oauth2 !== undefined) { + const oauth2JWKSRes = await fetch("http://localhost:4444/.well-known/jwks.json"); + if (oauth2JWKSRes.ok) { + const oauth2RespBody = await oauth2JWKSRes.json(); + resp.keys = resp.keys.concat(oauth2RespBody.keys); + } + } return { keys: resp.keys, }; diff --git a/lib/build/recipe/oauth2/api/auth.js b/lib/build/recipe/oauth2/api/auth.js index e9ffab92a..30135c712 100644 --- a/lib/build/recipe/oauth2/api/auth.js +++ b/lib/build/recipe/oauth2/api/auth.js @@ -21,6 +21,7 @@ var __importDefault = Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../../../utils"); const set_cookie_parser_1 = __importDefault(require("set-cookie-parser")); +const session_1 = __importDefault(require("../../session")); async function authGET(apiImplementation, options, userContext) { if (apiImplementation.authGET === undefined) { return false; @@ -28,10 +29,18 @@ async function authGET(apiImplementation, options, userContext) { const origURL = options.req.getOriginalURL(); const splitURL = origURL.split("?"); const params = new URLSearchParams(splitURL[1]); + let session; + try { + session = await session_1.default.getSession(options.req, options.res, { sessionRequired: false }, userContext); + } catch (_a) { + // TODO: explain + // ignore + } let response = await apiImplementation.authGET({ options, params: Object.fromEntries(params.entries()), cookie: options.req.getHeaderValue("cookie"), + session, userContext, }); if ("redirectTo" in response) { diff --git a/lib/build/recipe/oauth2/api/implementation.js b/lib/build/recipe/oauth2/api/implementation.js index 015c28f19..b4bdc6cad 100644 --- a/lib/build/recipe/oauth2/api/implementation.js +++ b/lib/build/recipe/oauth2/api/implementation.js @@ -42,6 +42,7 @@ function getAPIImplementation() { const accept = await options.recipeImplementation.acceptLoginRequest({ challenge: loginChallenge, subject: session.getUserId(), + identityProviderSessionId: session.getHandle(), userContext, }); return { redirectTo: accept.redirectTo }; @@ -165,6 +166,7 @@ function getAPIImplementation() { const res = await input.options.recipeImplementation.authorization({ params: input.params, cookies: input.cookie, + session: input.session, userContext: input.userContext, }); return res; diff --git a/lib/build/recipe/oauth2/api/token.js b/lib/build/recipe/oauth2/api/token.js index 2a7c8d03d..f8d076426 100644 --- a/lib/build/recipe/oauth2/api/token.js +++ b/lib/build/recipe/oauth2/api/token.js @@ -21,7 +21,7 @@ async function tokenPOST(apiImplementation, options, userContext) { } let response = await apiImplementation.tokenPOST({ options, - body: options.req.getFormData(), + body: await options.req.getFormData(), userContext, }); utils_1.send200Response(options.res, response); diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index d1f29aa71..28c889ec9 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -23,6 +23,7 @@ const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); const querier_1 = require("../../querier"); const utils_1 = require("../../utils"); const OAuth2Client_1 = require("./OAuth2Client"); +const __1 = require("../.."); function getRecipeInterface(querier, _config, _appInfo) { return { getLoginRequest: async function (input) { @@ -206,7 +207,7 @@ function getRecipeInterface(querier, _config, _appInfo) { ); }, authorization: async function (input) { - var _a, _b, _c; + var _a, _b, _c, _d, _e; const resp = await querier.sendGetRequestWithResponseHeaders( new normalisedURLPath_1.default(`/recipe/oauth2/pub/auth`), input.params, @@ -221,40 +222,70 @@ function getRecipeInterface(querier, _config, _appInfo) { } const redirectToURL = new URL(redirectTo); const consentChallenge = redirectToURL.searchParams.get("consent_challenge"); - if (consentChallenge !== null) { + if (consentChallenge !== null && input.session !== undefined) { const consentRequest = await this.getConsentRequest({ challenge: consentChallenge, userContext: input.userContext, }); + const user = await __1.getUser(input.session.getUserId()); + if (!user) { + throw new Error("Should not happen"); + } if ( consentRequest.skip || ((_a = consentRequest.client) === null || _a === void 0 ? void 0 : _a.skipConsent) ) { + const idToken = {}; + if ( + (_b = consentRequest.requestedScope) === null || _b === void 0 ? void 0 : _b.includes("email") + ) { + idToken.email = user === null || user === void 0 ? void 0 : user.emails[0]; + idToken.email_verified = user.loginMethods.some( + (lm) => lm.hasSameEmailAs(idToken.email) && lm.verified + ); + } + if ( + (_c = consentRequest.requestedScope) === null || _c === void 0 + ? void 0 + : _c.includes("phoneNumber") + ) { + idToken.phoneNumber = user === null || user === void 0 ? void 0 : user.phoneNumbers[0]; + idToken.phoneNumber_verified = user.loginMethods.some( + (lm) => lm.hasSamePhoneNumberAs(idToken.phoneNumber) && lm.verified + ); + } const consentRes = await this.acceptConsentRequest( Object.assign(Object.assign({}, input), { challenge: consentRequest.challenge, grantAccessTokenAudience: consentRequest.requestedAccessTokenAudience, grantScope: consentRequest.requestedScope, + session: { + id_token: idToken, + }, }) ); return { redirectTo: consentRes.redirectTo, - setCookie: (_b = resp.headers.get("set-cookie")) !== null && _b !== void 0 ? _b : undefined, + setCookie: (_d = resp.headers.get("set-cookie")) !== null && _d !== void 0 ? _d : undefined, }; } } return { redirectTo, - setCookie: (_c = resp.headers.get("set-cookie")) !== null && _c !== void 0 ? _c : undefined, + setCookie: (_e = resp.headers.get("set-cookie")) !== null && _e !== void 0 ? _e : undefined, }; }, token: async function (input) { - // TODO: Untested and suspicios - return querier.sendGetRequest( + const body = new FormData(); // TODO: we ideally want to avoid using formdata, the core can do the translation + for (const key in input.body) { + body.append(key, input.body[key]); + } + const res = await querier.sendPostRequest( new normalisedURLPath_1.default(`/recipe/oauth2/pub/token`), - input.body, + body, input.userContext ); + return res.data; }, getOAuth2Clients: async function (input, userContext) { var _a; diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index 68455859e..6430b4d35 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -72,12 +72,12 @@ export declare type LogoutRequest = { subject: string; }; export declare type TokenInfo = { - accessToken: string; - expiresIn: number; - idToken: string; - refreshToken: string; + access_token: string; + expires_in: number; + id_token: string; + refresh_token: string; scope: string; - tokenType: string; + token_type: string; }; export declare type LoginInfo = { clientName: string; @@ -90,6 +90,7 @@ export declare type RecipeInterface = { authorization(input: { params: any; cookies: string | undefined; + session: SessionContainerInterface | undefined; userContext: UserContext; }): Promise<{ redirectTo: string; @@ -290,6 +291,7 @@ export declare type APIInterface = { | ((input: { params: any; cookie: string | undefined; + session: SessionContainerInterface | undefined; options: APIOptions; userContext: UserContext; }) => Promise< diff --git a/lib/ts/querier.ts b/lib/ts/querier.ts index 380eb3d40..828a4cd6b 100644 --- a/lib/ts/querier.ts +++ b/lib/ts/querier.ts @@ -130,6 +130,8 @@ export class Querier { // path should start with "/" sendPostRequest = async (path: NormalisedURLPath, body: any, userContext: UserContext): Promise => { this.invalidateCoreCallCache(userContext); + // TODO: remove FormData + const isForm = body !== undefined && body instanceof FormData; const { body: respBody } = await this.sendRequestHelper( path, @@ -138,8 +140,10 @@ export class Querier { let apiVersion = await this.getAPIVersion(); let headers: any = { "cdi-version": apiVersion, - "content-type": "application/json; charset=utf-8", }; + if (!isForm) { + headers["content-type"] = "application/json; charset=utf-8"; + } if (Querier.apiKey !== undefined) { headers = { ...headers, @@ -170,7 +174,7 @@ export class Querier { } return doFetch(url, { method: "POST", - body: body !== undefined ? JSON.stringify(body) : undefined, + body: isForm ? body : body !== undefined ? JSON.stringify(body) : undefined, headers, }); }, diff --git a/lib/ts/recipe/jwt/api/implementation.ts b/lib/ts/recipe/jwt/api/implementation.ts index 4308c7cf1..e3418a969 100644 --- a/lib/ts/recipe/jwt/api/implementation.ts +++ b/lib/ts/recipe/jwt/api/implementation.ts @@ -13,6 +13,7 @@ * under the License. */ +import OAuth2 from "../../oauth2/recipe"; import { APIInterface, APIOptions, JsonWebKey } from "../types"; import { GeneralErrorResponse, UserContext } from "../../../types"; @@ -31,6 +32,16 @@ export default function getAPIImplementation(): APIInterface { options.res.setHeader("Cache-Control", `max-age=${resp.validityInSeconds}, must-revalidate`, false); } + const oauth2 = OAuth2.getInstance(); + // TODO: dirty hack until we get core support + if (oauth2 !== undefined) { + const oauth2JWKSRes = await fetch("http://localhost:4444/.well-known/jwks.json"); + if (oauth2JWKSRes.ok) { + const oauth2RespBody = await oauth2JWKSRes.json(); + resp.keys = resp.keys.concat(oauth2RespBody.keys); + } + } + return { keys: resp.keys, }; diff --git a/lib/ts/recipe/oauth2/api/auth.ts b/lib/ts/recipe/oauth2/api/auth.ts index b1d133b12..50eba1b00 100644 --- a/lib/ts/recipe/oauth2/api/auth.ts +++ b/lib/ts/recipe/oauth2/api/auth.ts @@ -17,6 +17,7 @@ import { send200Response } from "../../../utils"; import { APIInterface, APIOptions } from ".."; import { UserContext } from "../../../types"; import setCookieParser from "set-cookie-parser"; +import Session from "../../session"; export default async function authGET( apiImplementation: APIInterface, @@ -29,11 +30,19 @@ export default async function authGET( const origURL = options.req.getOriginalURL(); const splitURL = origURL.split("?"); const params = new URLSearchParams(splitURL[1]); + let session; + try { + session = await Session.getSession(options.req, options.res, { sessionRequired: false }, userContext); + } catch { + // TODO: explain + // ignore + } let response = await apiImplementation.authGET({ options, params: Object.fromEntries(params.entries()), cookie: options.req.getHeaderValue("cookie"), + session, userContext, }); if ("redirectTo" in response) { diff --git a/lib/ts/recipe/oauth2/api/implementation.ts b/lib/ts/recipe/oauth2/api/implementation.ts index c8cd1d94a..69c865874 100644 --- a/lib/ts/recipe/oauth2/api/implementation.ts +++ b/lib/ts/recipe/oauth2/api/implementation.ts @@ -38,6 +38,7 @@ export default function getAPIImplementation(): APIInterface { const accept = await options.recipeImplementation.acceptLoginRequest({ challenge: loginChallenge, subject: session.getUserId(), + identityProviderSessionId: session.getHandle(), userContext, }); return { redirectTo: accept.redirectTo }; @@ -162,6 +163,7 @@ export default function getAPIImplementation(): APIInterface { const res = await input.options.recipeImplementation.authorization({ params: input.params, cookies: input.cookie, + session: input.session, userContext: input.userContext, }); return res; diff --git a/lib/ts/recipe/oauth2/api/token.ts b/lib/ts/recipe/oauth2/api/token.ts index 1f4b85b60..91f2d3cc6 100644 --- a/lib/ts/recipe/oauth2/api/token.ts +++ b/lib/ts/recipe/oauth2/api/token.ts @@ -28,7 +28,7 @@ export default async function tokenPOST( let response = await apiImplementation.tokenPOST({ options, - body: options.req.getFormData(), + body: await options.req.getFormData(), userContext, }); diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index 4317e442d..19371d370 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -19,6 +19,7 @@ import { NormalisedAppinfo } from "../../types"; import { RecipeInterface, TypeNormalisedInput, ConsentRequest, LoginRequest, LogoutRequest } from "./types"; import { toSnakeCase, transformObjectKeys } from "../../utils"; import { OAuth2Client } from "./OAuth2Client"; +import { getUser } from "../.."; export default function getRecipeInterface( querier: Querier, @@ -232,18 +233,38 @@ export default function getRecipeInterface( } const redirectToURL = new URL(redirectTo); const consentChallenge = redirectToURL.searchParams.get("consent_challenge"); - if (consentChallenge !== null) { + if (consentChallenge !== null && input.session !== undefined) { const consentRequest = await this.getConsentRequest({ challenge: consentChallenge, userContext: input.userContext, }); + const user = await getUser(input.session.getUserId()); + if (!user) { + throw new Error("Should not happen"); + } if (consentRequest.skip || consentRequest.client?.skipConsent) { + const idToken: any = {}; + if (consentRequest.requestedScope?.includes("email")) { + idToken.email = user?.emails[0]; + idToken.email_verified = user.loginMethods.some( + (lm) => lm.hasSameEmailAs(idToken.email) && lm.verified + ); + } + if (consentRequest.requestedScope?.includes("phoneNumber")) { + idToken.phoneNumber = user?.phoneNumbers[0]; + idToken.phoneNumber_verified = user.loginMethods.some( + (lm) => lm.hasSamePhoneNumberAs(idToken.phoneNumber) && lm.verified + ); + } const consentRes = await this.acceptConsentRequest({ ...input, challenge: consentRequest.challenge, grantAccessTokenAudience: consentRequest.requestedAccessTokenAudience, grantScope: consentRequest.requestedScope, + session: { + id_token: idToken, + }, }); return { @@ -256,12 +277,17 @@ export default function getRecipeInterface( }, token: async function (this: RecipeInterface, input) { - // TODO: Untested and suspicios - return querier.sendGetRequest( + const body = new FormData(); // TODO: we ideally want to avoid using formdata, the core can do the translation + for (const key in input.body) { + body.append(key, input.body[key]); + } + const res = await querier.sendPostRequest( new NormalisedURLPath(`/recipe/oauth2/pub/token`), - input.body, + body, input.userContext ); + + return res.data; }, getOAuth2Clients: async function (input, userContext) { diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 059d37b56..9e77e8679 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -160,18 +160,18 @@ export type LogoutRequest = { export type TokenInfo = { // The access token issued by the authorization server. - accessToken: string; + 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 - expiresIn: number; + expires_in: number; // To retrieve a refresh token request the id_token scope. - idToken: string; + id_token: string; // The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request. - refreshToken: string; + refresh_token: string; // The scope of the access token scope: string; // The type of the token issued - tokenType: string; + token_type: string; }; export type LoginInfo = { @@ -191,6 +191,7 @@ export type RecipeInterface = { authorization(input: { params: any; cookies: string | undefined; + session: SessionContainerInterface | undefined; userContext: UserContext; }): Promise<{ redirectTo: string; setCookie: string | undefined }>; token(input: { body: any; userContext: UserContext }): Promise; @@ -395,6 +396,7 @@ export type APIInterface = { | ((input: { params: any; cookie: string | undefined; + session: SessionContainerInterface | undefined; options: APIOptions; userContext: UserContext; }) => Promise<{ redirectTo: string; setCookie: string | undefined } | ErrorOAuth2 | GeneralErrorResponse>); From 1621b7ac064cd16a9d3da05ede3a37d84cd41feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Mon, 1 Jul 2024 11:32:35 +0200 Subject: [PATCH 08/16] feat!: improve how we handle changing email addresses and users becoming unverified when account linking requires verification (#869) * feat: update email and pw change logic and add more security checks * feat: update error messages * refactor: improve debug logs and clarify conditions * chore: update changelog * chore: empty line from changelog * refactor: remove duplicated check and bypass mapping for already mapped errcodes * chore: update changelog --- CHANGELOG.md | 7 +- lib/build/recipe/accountlinking/index.js | 3 +- lib/build/recipe/accountlinking/recipe.d.ts | 10 +- lib/build/recipe/accountlinking/recipe.js | 89 ++++++++++++++---- .../emailpassword/api/implementation.js | 74 ++++++--------- .../emailpassword/recipeImplementation.js | 33 +++++++ .../recipe/thirdparty/api/implementation.js | 82 ++--------------- .../recipe/thirdparty/recipeImplementation.js | 30 +++++- lib/ts/recipe/accountlinking/index.ts | 3 +- lib/ts/recipe/accountlinking/recipe.ts | 92 ++++++++++++++----- .../emailpassword/api/implementation.ts | 80 +++++++--------- .../emailpassword/recipeImplementation.ts | 36 ++++++++ .../recipe/thirdparty/api/implementation.ts | 82 ++--------------- .../recipe/thirdparty/recipeImplementation.ts | 33 ++++++- test/emailpassword/updateEmailPass.test.js | 4 +- 15 files changed, 370 insertions(+), 288 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 660dbd83b..bbbeaad9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking changes - Defined the entry points of the library using the "exports" field in package.json to make ESM imports more comfortable. This can cause some issues for applications using directory imports from the `lib/build` directory. In those cases we recommend adding `index.js` to the import path. - +- `isEmailChangeAllowed` now returns false for unverified addresses if input user is a primary user and there exists another user with the same email address and linking requires verification +- Generating a password reset token is now denied if all of the following is true: + - a linked email password user exists + - the email address is not verified + - the user has another email address or phone number associated with it +- Account linking based on emails now require the email to be verified in both users if `shouldRequireVerification` is set to `true` instead of only requiring it for the recipe user. - The access token cookie expiry has been changed from 100 years to 1 year due to some browsers capping the maximum expiry at 400 days. No action is needed on your part. ### Changes diff --git a/lib/build/recipe/accountlinking/index.js b/lib/build/recipe/accountlinking/index.js index ad00a94fb..68e7fbbb5 100644 --- a/lib/build/recipe/accountlinking/index.js +++ b/lib/build/recipe/accountlinking/index.js @@ -129,13 +129,14 @@ class Wrapper { } static async isEmailChangeAllowed(recipeUserId, newEmail, isVerified, session, userContext) { const user = await __1.getUser(recipeUserId.getAsString(), userContext); - return await recipe_1.default.getInstance().isEmailChangeAllowed({ + const res = await recipe_1.default.getInstance().isEmailChangeAllowed({ user, newEmail, isVerified, session, userContext: utils_1.getUserContext(userContext), }); + return res.allowed; } } exports.default = Wrapper; diff --git a/lib/build/recipe/accountlinking/recipe.d.ts b/lib/build/recipe/accountlinking/recipe.d.ts index 48970b36d..f6050bce1 100644 --- a/lib/build/recipe/accountlinking/recipe.d.ts +++ b/lib/build/recipe/accountlinking/recipe.d.ts @@ -104,7 +104,15 @@ export default class Recipe extends RecipeModule { isVerified: boolean; session: SessionContainerInterface | undefined; userContext: UserContext; - }) => Promise; + }) => Promise< + | { + allowed: true; + } + | { + allowed: false; + reason: "PRIMARY_USER_CONFLICT" | "ACCOUNT_TAKEOVER_RISK"; + } + >; verifyEmailForRecipeUserIfLinkedAccountsAreVerified: (input: { user: User; recipeUserId: RecipeUserId; diff --git a/lib/build/recipe/accountlinking/recipe.js b/lib/build/recipe/accountlinking/recipe.js index ac9e83d6e..656686bc6 100644 --- a/lib/build/recipe/accountlinking/recipe.js +++ b/lib/build/recipe/accountlinking/recipe.js @@ -384,35 +384,73 @@ class Recipe extends recipeModule_1.default { * on the link by mistake, causing account linking to happen which can result * in account take over if this recipe user is malicious. */ - let user = input.user; - if (user === undefined) { + let inputUser = input.user; + if (inputUser === undefined) { throw new Error("Passed in recipe user id does not exist"); } - for (const tenantId of user.tenantIds) { + for (const tenantId of inputUser.tenantIds) { let existingUsersWithNewEmail = await this.recipeInterfaceImpl.listUsersByAccountInfo({ - tenantId: user.tenantIds[0], + tenantId: inputUser.tenantIds[0], accountInfo: { email: input.newEmail, }, doUnionOfAccountInfo: false, userContext: input.userContext, }); - let primaryUserForNewEmail = existingUsersWithNewEmail.filter((u) => u.isPrimaryUser); - if (primaryUserForNewEmail.length > 1) { + let otherUsersWithNewEmail = existingUsersWithNewEmail.filter((u) => u.id !== inputUser.id); + let otherPrimaryUserForNewEmail = otherUsersWithNewEmail.filter((u) => u.isPrimaryUser); + if (otherPrimaryUserForNewEmail.length > 1) { throw new Error("You found a bug. Please report it on github.com/supertokens/supertokens-node"); } - if (user.isPrimaryUser) { + if (inputUser.isPrimaryUser) { // this is condition one from the above comment. - if (primaryUserForNewEmail.length === 1 && primaryUserForNewEmail[0].id !== user.id) { + if (otherPrimaryUserForNewEmail.length !== 0) { logger_1.logDebugMessage( - "isEmailChangeAllowed: returning false cause email change will lead to two primary users having same email" + `isEmailChangeAllowed: returning false cause email change will lead to two primary users having same email on ${tenantId}` ); - return false; + return { allowed: false, reason: "PRIMARY_USER_CONFLICT" }; + } + if (input.isVerified) { + logger_1.logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary, new email is verified and doesn't belong to any other primary user` + ); + continue; + } + if (inputUser.loginMethods.some((lm) => lm.hasSameEmailAs(input.newEmail) && lm.verified)) { + logger_1.logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary, new email is verified in another login method and doesn't belong to any other primary user` + ); + continue; + } + if (otherUsersWithNewEmail.length === 0) { + logger_1.logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary and the new email doesn't belong to any other user (primary or non-primary)` + ); + continue; + } + const shouldDoAccountLinking = await this.config.shouldDoAutomaticAccountLinking( + otherUsersWithNewEmail[0].loginMethods[0], + inputUser, + input.session, + tenantId, + input.userContext + ); + if (!shouldDoAccountLinking.shouldAutomaticallyLink) { + logger_1.logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause linking is disabled` + ); + continue; + } + if (!shouldDoAccountLinking.shouldRequireVerification) { + logger_1.logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause linking is doesn't require email verification` + ); + continue; } logger_1.logDebugMessage( - `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary and new email doesn't belong to any other primary user` + `isEmailChangeAllowed: returning false because the user hasn't verified the new email address and there exists another user with it on ${tenantId} and linking requires verification` ); - continue; + return { allowed: false, reason: "ACCOUNT_TAKEOVER_RISK" }; } else { if (input.isVerified) { logger_1.logDebugMessage( @@ -420,16 +458,16 @@ class Recipe extends recipeModule_1.default { ); continue; } - if (user.loginMethods[0].hasSameEmailAs(input.newEmail)) { + if (inputUser.loginMethods[0].hasSameEmailAs(input.newEmail)) { logger_1.logDebugMessage( `isEmailChangeAllowed: can change on ${tenantId} cause input user is not a primary and new email is same as the older one` ); continue; } - if (primaryUserForNewEmail.length === 1) { + if (otherPrimaryUserForNewEmail.length === 1) { let shouldDoAccountLinking = await this.config.shouldDoAutomaticAccountLinking( - user.loginMethods[0], - primaryUserForNewEmail[0], + inputUser.loginMethods[0], + otherPrimaryUserForNewEmail[0], input.session, tenantId, input.userContext @@ -449,7 +487,7 @@ class Recipe extends recipeModule_1.default { logger_1.logDebugMessage( "isEmailChangeAllowed: returning false cause input user is not a primary there exists a primary user exists with the new email." ); - return false; + return { allowed: false, reason: "ACCOUNT_TAKEOVER_RISK" }; } logger_1.logDebugMessage( `isEmailChangeAllowed: can change on ${tenantId} cause input user is not a primary no primary user exists with the new email` @@ -460,7 +498,7 @@ class Recipe extends recipeModule_1.default { logger_1.logDebugMessage( "isEmailChangeAllowed: returning true cause email change can happen on all tenants the user is part of" ); - return true; + return { allowed: true }; }; this.verifyEmailForRecipeUserIfLinkedAccountsAreVerified = async (input) => { try { @@ -651,9 +689,20 @@ class Recipe extends recipeModule_1.default { ); return { status: "NO_LINK" }; } - if (shouldDoAccountLinking.shouldRequireVerification && !inputUser.loginMethods[0].verified) { + const accountInfoVerifiedInPrimUser = primaryUserThatCanBeLinkedToTheInputUser.loginMethods.some( + (lm) => + (inputUser.loginMethods[0].email !== undefined && + lm.hasSameEmailAs(inputUser.loginMethods[0].email)) || + (inputUser.loginMethods[0].phoneNumber !== undefined && + lm.hasSamePhoneNumberAs(inputUser.loginMethods[0].phoneNumber) && + lm.verified) + ); + if ( + shouldDoAccountLinking.shouldRequireVerification && + (!inputUser.loginMethods[0].verified || !accountInfoVerifiedInPrimUser) + ) { logger_1.logDebugMessage( - "tryLinkingByAccountInfoOrCreatePrimaryUser: not linking because shouldRequireVerification is true but the login method is not verified" + "tryLinkingByAccountInfoOrCreatePrimaryUser: not linking because shouldRequireVerification is true but the login method is not verified in the new or the primary user" ); return { status: "NO_LINK" }; } diff --git a/lib/build/recipe/emailpassword/api/implementation.js b/lib/build/recipe/emailpassword/api/implementation.js index 4e8045c98..6ed81771b 100644 --- a/lib/build/recipe/emailpassword/api/implementation.js +++ b/lib/build/recipe/emailpassword/api/implementation.js @@ -122,6 +122,31 @@ function getAPIImplementation() { emailPasswordAccount.recipeUserId ); } + // Next we check if there is any login method in which the input email is verified. + // If that is the case, then it's proven that the user owns the email and we can + // trust linking of the email password account. + let emailVerified = + primaryUserAssociatedWithEmail.loginMethods.find((lm) => { + return lm.hasSameEmailAs(email) && lm.verified; + }) !== undefined; + // finally, we check if the primary user has any other email / phone number + // associated with this account - and if it does, then it means that + // there is a risk of account takeover, so we do not allow the token to be generated + let hasOtherEmailOrPhone = + primaryUserAssociatedWithEmail.loginMethods.find((lm) => { + // we do the extra undefined check below cause + // hasSameEmailAs returns false if the lm.email is undefined, and + // we want to check that the email is different as opposed to email + // not existing in lm. + return (lm.email !== undefined && !lm.hasSameEmailAs(email)) || lm.phoneNumber !== undefined; + }) !== undefined; + if (!emailVerified && hasOtherEmailOrPhone) { + return { + status: "PASSWORD_RESET_NOT_ALLOWED", + reason: + "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)", + }; + } let shouldDoAccountLinkingResponse = await recipe_1.default .getInstance() .config.shouldDoAutomaticAccountLinking( @@ -228,51 +253,10 @@ function getAPIImplementation() { emailPasswordAccount.recipeUserId ); } - // Now we start the required security checks. First we check if the primary user - // it has just one linked account. And if that's true, then we continue - // cause then there is no scope for account takeover - if (primaryUserAssociatedWithEmail.loginMethods.length === 1) { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } - // Next we check if there is any login method in which the input email is verified. - // If that is the case, then it's proven that the user owns the email and we can - // trust linking of the email password account. - let emailVerified = - primaryUserAssociatedWithEmail.loginMethods.find((lm) => { - return lm.hasSameEmailAs(email) && lm.verified; - }) !== undefined; - if (emailVerified) { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } - // finally, we check if the primary user has any other email / phone number - // associated with this account - and if it does, then it means that - // there is a risk of account takeover, so we do not allow the token to be generated - let hasOtherEmailOrPhone = - primaryUserAssociatedWithEmail.loginMethods.find((lm) => { - // we do the extra undefined check below cause - // hasSameEmailAs returns false if the lm.email is undefined, and - // we want to check that the email is different as opposed to email - // not existing in lm. - return (lm.email !== undefined && !lm.hasSameEmailAs(email)) || lm.phoneNumber !== undefined; - }) !== undefined; - if (hasOtherEmailOrPhone) { - return { - status: "PASSWORD_RESET_NOT_ALLOWED", - reason: - "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)", - }; - } else { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } + return await generateAndSendPasswordResetToken( + primaryUserAssociatedWithEmail.id, + emailPasswordAccount.recipeUserId + ); }, passwordResetPOST: async function ({ formFields, token, tenantId, options, userContext }) { async function markEmailAsVerified(recipeUserId, email) { diff --git a/lib/build/recipe/emailpassword/recipeImplementation.js b/lib/build/recipe/emailpassword/recipeImplementation.js index 937dca20e..8c09609c1 100644 --- a/lib/build/recipe/emailpassword/recipeImplementation.js +++ b/lib/build/recipe/emailpassword/recipeImplementation.js @@ -6,6 +6,7 @@ var __importDefault = }; Object.defineProperty(exports, "__esModule", { value: true }); const recipe_1 = __importDefault(require("../accountlinking/recipe")); +const recipe_2 = __importDefault(require("../emailverification/recipe")); const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); const __1 = require("../.."); const constants_1 = require("./constants"); @@ -160,6 +161,38 @@ function getRecipeInterface(querier, getEmailPasswordConfig) { ); }, updateEmailOrPassword: async function (input) { + const accountLinking = recipe_1.default.getInstance(); + if (input.email) { + const user = await __1.getUser(input.recipeUserId.getAsString(), input.userContext); + if (user === undefined) { + return { status: "UNKNOWN_USER_ID_ERROR" }; + } + const evInstance = recipe_2.default.getInstance(); + let isEmailVerified = false; + if (evInstance) { + isEmailVerified = await evInstance.recipeInterfaceImpl.isEmailVerified({ + recipeUserId: input.recipeUserId, + email: input.email, + userContext: input.userContext, + }); + } + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isEmailVerified, + newEmail: input.email, + session: undefined, + userContext: input.userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "ACCOUNT_TAKEOVER_RISK" + ? "New email cannot be applied to existing account because of account takeover risks." + : "New email cannot be applied to existing account because of there is another primary user with the same email address.", + }; + } + } if (input.applyPasswordPolicy || input.applyPasswordPolicy === undefined) { let formFields = getEmailPasswordConfig().signUpFeature.formFields; if (input.password !== undefined) { diff --git a/lib/build/recipe/thirdparty/api/implementation.js b/lib/build/recipe/thirdparty/api/implementation.js index 860c226d2..e688b95ae 100644 --- a/lib/build/recipe/thirdparty/api/implementation.js +++ b/lib/build/recipe/thirdparty/api/implementation.js @@ -5,9 +5,8 @@ var __importDefault = return mod && mod.__esModule ? mod : { default: mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const recipe_1 = __importDefault(require("../../accountlinking/recipe")); const emailverification_1 = __importDefault(require("../../emailverification")); -const recipe_2 = __importDefault(require("../../emailverification/recipe")); +const recipe_1 = __importDefault(require("../../emailverification/recipe")); const authUtils_1 = require("../../../authUtils"); const logger_1 = require("../../../logger"); function getAPIInterface() { @@ -36,7 +35,7 @@ function getAPIInterface() { "Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_023)", }, }; - const { provider, tenantId, options, session, userContext } = input; + const { provider, tenantId, options, userContext } = input; let oAuthTokensToUse = {}; if ("redirectURIInfo" in input && input.redirectURIInfo !== undefined) { oAuthTokensToUse = await provider.exchangeAuthCodeForOAuthTokens({ @@ -92,84 +91,19 @@ function getAPIInterface() { // We do this check here and not in the recipe function cause we want to keep the // recipe function checks to a minimum so that the dev has complete control of // what they can do. - // The isEmailChangeAllowed function takes in a isVerified boolean. Now, even though - // we already have that from the input, that's just what the provider says. If the - // provider says that the email is NOT verified, it could have been that the email + // The isEmailChangeAllowed and preAuthChecks functions take an isVerified boolean. + // Now, even though we already have that from the input, that's just what the provider says. + // If the provider says that the email is NOT verified, it could have been that the email // is verified on the user's account via supertokens on a previous sign in / up. // So we just check that as well before calling isEmailChangeAllowed const recipeUserId = authenticatingUser.loginMethod.recipeUserId; - if (!emailInfo.isVerified && recipe_2.default.getInstance() !== undefined) { + if (!emailInfo.isVerified && recipe_1.default.getInstance() !== undefined) { emailInfo.isVerified = await emailverification_1.default.isEmailVerified( recipeUserId, emailInfo.id, userContext ); } - /** - * In this API, during only a sign in, we check for isEmailChangeAllowed first, then - * change the email by calling the recipe function, then check if is sign in allowed. - * This may result in a few states where email change is allowed, but still, sign in - * is not allowed: - * - * Various outcomes of isSignInAllowed vs isEmailChangeAllowed - * isSignInAllowed result: - * - is primary user -> TRUE - * - is recipe user - * - other recipe user exists - * - no -> TRUE - * - yes - * - email verified -> TRUE - * - email unverified -> FALSE - * - other primary user exists - * - no -> TRUE - * - yes - * - email verification status - * - this && primary -> TRUE - * - !this && !primary -> FALSE - * - this && !primary -> FALSE - * - !this && primary -> FALSE - * - * isEmailChangeAllowed result: - * - is primary user -> TRUE - * - is recipe user - * - other recipe user exists - * - no -> TRUE - * - yes - * - email verified -> TRUE - * - email unverified -> TRUE - * - other primary user exists - * - no -> TRUE - * - yes - * - email verification status - * - this && primary -> TRUE - * - !this && !primary -> FALSE - * - this && !primary -> TRUE - * - !this && primary -> FALSE - * - * Based on the above, isEmailChangeAllowed can return true, but isSignInAllowed will return false - * in the following situations: - * - If a recipe user is signing in with a new email, other recipe users with the same email exist, - * and one of them is unverfied. In this case, the email change will happen in the social login - * recipe, but the user will not be able to login anyway. - * - * - If the recipe user is signing in with a new email, there exists a primary user with the same - * email, but this new email is verified for the recipe user already, but the primary user's email - * is not verified. - */ - let isEmailChangeAllowed = await recipe_1.default.getInstance().isEmailChangeAllowed({ - user: authenticatingUser.user, - isVerified: emailInfo.isVerified, - newEmail: emailInfo.id, - session, - userContext, - }); - if (!isEmailChangeAllowed) { - return { - status: "SIGN_IN_UP_NOT_ALLOWED", - reason: - "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)", - }; - } } const preAuthChecks = await authUtils_1.AuthUtils.preAuthChecks({ authenticatingAccountInfo: { @@ -218,6 +152,10 @@ function getAPIInterface() { tenantId, userContext, }); + if (response.status === "SIGN_IN_UP_NOT_ALLOWED") { + // In this case we do not need to do mapping, since the recipe function already has the right response shape. + return response; + } if (response.status !== "OK") { logger_1.logDebugMessage("signInUpPOST: erroring out because signInUp returned " + response.status); return authUtils_1.AuthUtils.getErrorStatusResponseWithReason( diff --git a/lib/build/recipe/thirdparty/recipeImplementation.js b/lib/build/recipe/thirdparty/recipeImplementation.js index 90f1b2dbf..df6887fc9 100644 --- a/lib/build/recipe/thirdparty/recipeImplementation.js +++ b/lib/build/recipe/thirdparty/recipeImplementation.js @@ -24,6 +24,32 @@ function getRecipeImplementation(querier, providers) { session, userContext, }) { + const accountLinking = recipe_1.default.getInstance(); + const users = await __1.listUsersByAccountInfo( + tenantId, + { thirdParty: { id: thirdPartyId, userId: thirdPartyUserId } }, + false, + userContext + ); + const user = users[0]; + if (user !== undefined) { + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isVerified, + newEmail: email, + session, + userContext: userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "PRIMARY_USER_CONFLICT" + ? "Email already associated with another primary user." + : "New email cannot be applied to existing account because of account takeover risks.", + }; + } + } let response = await querier.sendPostRequest( new normalisedURLPath_1.default(`/${tenantId}/recipe/signinup`), { @@ -89,7 +115,9 @@ function getRecipeImplementation(querier, providers) { return { status: "SIGN_IN_UP_NOT_ALLOWED", reason: - "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)", + response.reason === "Email already associated with another primary user." + ? "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)" + : "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_024)", }; } if (response.status === "OK") { diff --git a/lib/ts/recipe/accountlinking/index.ts b/lib/ts/recipe/accountlinking/index.ts index fb82f3138..995a341e0 100644 --- a/lib/ts/recipe/accountlinking/index.ts +++ b/lib/ts/recipe/accountlinking/index.ts @@ -165,13 +165,14 @@ export default class Wrapper { ) { const user = await getUser(recipeUserId.getAsString(), userContext); - return await Recipe.getInstance().isEmailChangeAllowed({ + const res = await Recipe.getInstance().isEmailChangeAllowed({ user, newEmail, isVerified, session, userContext: getUserContext(userContext), }); + return res.allowed; } } diff --git a/lib/ts/recipe/accountlinking/recipe.ts b/lib/ts/recipe/accountlinking/recipe.ts index 2b40199e7..5f7ae50c4 100644 --- a/lib/ts/recipe/accountlinking/recipe.ts +++ b/lib/ts/recipe/accountlinking/recipe.ts @@ -528,7 +528,7 @@ export default class Recipe extends RecipeModule { isVerified: boolean; session: SessionContainerInterface | undefined; userContext: UserContext; - }): Promise => { + }): Promise<{ allowed: true } | { allowed: false; reason: "PRIMARY_USER_CONFLICT" | "ACCOUNT_TAKEOVER_RISK" }> => { /** * The purpose of this function is to check that if a recipe user ID's email * can be changed or not. There are two conditions for when it can't be changed: @@ -544,15 +544,15 @@ export default class Recipe extends RecipeModule { * in account take over if this recipe user is malicious. */ - let user = input.user; + let inputUser = input.user; - if (user === undefined) { + if (inputUser === undefined) { throw new Error("Passed in recipe user id does not exist"); } - for (const tenantId of user.tenantIds) { + for (const tenantId of inputUser.tenantIds) { let existingUsersWithNewEmail = await this.recipeInterfaceImpl.listUsersByAccountInfo({ - tenantId: user.tenantIds[0], + tenantId: inputUser.tenantIds[0], accountInfo: { email: input.newEmail, }, @@ -560,23 +560,62 @@ export default class Recipe extends RecipeModule { userContext: input.userContext, }); - let primaryUserForNewEmail = existingUsersWithNewEmail.filter((u) => u.isPrimaryUser); - if (primaryUserForNewEmail.length > 1) { + let otherUsersWithNewEmail = existingUsersWithNewEmail.filter((u) => u.id !== inputUser!.id); + + let otherPrimaryUserForNewEmail = otherUsersWithNewEmail.filter((u) => u.isPrimaryUser); + if (otherPrimaryUserForNewEmail.length > 1) { throw new Error("You found a bug. Please report it on github.com/supertokens/supertokens-node"); } - if (user.isPrimaryUser) { + if (inputUser.isPrimaryUser) { // this is condition one from the above comment. - if (primaryUserForNewEmail.length === 1 && primaryUserForNewEmail[0].id !== user.id) { + if (otherPrimaryUserForNewEmail.length !== 0) { logDebugMessage( - "isEmailChangeAllowed: returning false cause email change will lead to two primary users having same email" + `isEmailChangeAllowed: returning false cause email change will lead to two primary users having same email on ${tenantId}` ); - return false; + return { allowed: false, reason: "PRIMARY_USER_CONFLICT" }; + } + if (input.isVerified) { + logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary, new email is verified and doesn't belong to any other primary user` + ); + continue; + } + if (inputUser.loginMethods.some((lm) => lm.hasSameEmailAs(input.newEmail) && lm.verified)) { + logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary, new email is verified in another login method and doesn't belong to any other primary user` + ); + continue; + } + if (otherUsersWithNewEmail.length === 0) { + logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary and the new email doesn't belong to any other user (primary or non-primary)` + ); + continue; + } + + const shouldDoAccountLinking = await this.config.shouldDoAutomaticAccountLinking( + otherUsersWithNewEmail[0].loginMethods[0], + inputUser, + input.session, + tenantId, + input.userContext + ); + if (!shouldDoAccountLinking.shouldAutomaticallyLink) { + logDebugMessage(`isEmailChangeAllowed: can change on ${tenantId} cause linking is disabled`); + continue; + } + + if (!shouldDoAccountLinking.shouldRequireVerification) { + logDebugMessage( + `isEmailChangeAllowed: can change on ${tenantId} cause linking is doesn't require email verification` + ); + continue; } logDebugMessage( - `isEmailChangeAllowed: can change on ${tenantId} cause input user is primary and new email doesn't belong to any other primary user` + `isEmailChangeAllowed: returning false because the user hasn't verified the new email address and there exists another user with it on ${tenantId} and linking requires verification` ); - continue; + return { allowed: false, reason: "ACCOUNT_TAKEOVER_RISK" }; } else { if (input.isVerified) { logDebugMessage( @@ -585,17 +624,17 @@ export default class Recipe extends RecipeModule { continue; } - if (user.loginMethods[0].hasSameEmailAs(input.newEmail)) { + if (inputUser.loginMethods[0].hasSameEmailAs(input.newEmail)) { logDebugMessage( `isEmailChangeAllowed: can change on ${tenantId} cause input user is not a primary and new email is same as the older one` ); continue; } - if (primaryUserForNewEmail.length === 1) { + if (otherPrimaryUserForNewEmail.length === 1) { let shouldDoAccountLinking = await this.config.shouldDoAutomaticAccountLinking( - user.loginMethods[0], - primaryUserForNewEmail[0], + inputUser.loginMethods[0], + otherPrimaryUserForNewEmail[0], input.session, tenantId, input.userContext @@ -618,7 +657,7 @@ export default class Recipe extends RecipeModule { logDebugMessage( "isEmailChangeAllowed: returning false cause input user is not a primary there exists a primary user exists with the new email." ); - return false; + return { allowed: false, reason: "ACCOUNT_TAKEOVER_RISK" }; } logDebugMessage( @@ -630,7 +669,7 @@ export default class Recipe extends RecipeModule { logDebugMessage( "isEmailChangeAllowed: returning true cause email change can happen on all tenants the user is part of" ); - return true; + return { allowed: true }; }; verifyEmailForRecipeUserIfLinkedAccountsAreVerified = async (input: { @@ -780,9 +819,20 @@ export default class Recipe extends RecipeModule { return { status: "NO_LINK" }; } - if (shouldDoAccountLinking.shouldRequireVerification && !inputUser.loginMethods[0].verified) { + const accountInfoVerifiedInPrimUser = primaryUserThatCanBeLinkedToTheInputUser.loginMethods.some( + (lm) => + (inputUser.loginMethods[0].email !== undefined && + lm.hasSameEmailAs(inputUser.loginMethods[0].email)) || + (inputUser.loginMethods[0].phoneNumber !== undefined && + lm.hasSamePhoneNumberAs(inputUser.loginMethods[0].phoneNumber) && + lm.verified) + ); + if ( + shouldDoAccountLinking.shouldRequireVerification && + (!inputUser.loginMethods[0].verified || !accountInfoVerifiedInPrimUser) + ) { logDebugMessage( - "tryLinkingByAccountInfoOrCreatePrimaryUser: not linking because shouldRequireVerification is true but the login method is not verified" + "tryLinkingByAccountInfoOrCreatePrimaryUser: not linking because shouldRequireVerification is true but the login method is not verified in the new or the primary user" ); return { status: "NO_LINK" }; } diff --git a/lib/ts/recipe/emailpassword/api/implementation.ts b/lib/ts/recipe/emailpassword/api/implementation.ts index b17c2cb2c..843e270cd 100644 --- a/lib/ts/recipe/emailpassword/api/implementation.ts +++ b/lib/ts/recipe/emailpassword/api/implementation.ts @@ -165,6 +165,34 @@ export default function getAPIImplementation(): APIInterface { ); } + // Next we check if there is any login method in which the input email is verified. + // If that is the case, then it's proven that the user owns the email and we can + // trust linking of the email password account. + let emailVerified = + primaryUserAssociatedWithEmail.loginMethods.find((lm) => { + return lm.hasSameEmailAs(email) && lm.verified; + }) !== undefined; + + // finally, we check if the primary user has any other email / phone number + // associated with this account - and if it does, then it means that + // there is a risk of account takeover, so we do not allow the token to be generated + let hasOtherEmailOrPhone = + primaryUserAssociatedWithEmail.loginMethods.find((lm) => { + // we do the extra undefined check below cause + // hasSameEmailAs returns false if the lm.email is undefined, and + // we want to check that the email is different as opposed to email + // not existing in lm. + return (lm.email !== undefined && !lm.hasSameEmailAs(email)) || lm.phoneNumber !== undefined; + }) !== undefined; + + if (!emailVerified && hasOtherEmailOrPhone) { + return { + status: "PASSWORD_RESET_NOT_ALLOWED", + reason: + "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)", + }; + } + let shouldDoAccountLinkingResponse = await AccountLinking.getInstance().config.shouldDoAutomaticAccountLinking( emailPasswordAccount !== undefined ? emailPasswordAccount @@ -280,54 +308,10 @@ export default function getAPIImplementation(): APIInterface { ); } - // Now we start the required security checks. First we check if the primary user - // it has just one linked account. And if that's true, then we continue - // cause then there is no scope for account takeover - if (primaryUserAssociatedWithEmail.loginMethods.length === 1) { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } - - // Next we check if there is any login method in which the input email is verified. - // If that is the case, then it's proven that the user owns the email and we can - // trust linking of the email password account. - let emailVerified = - primaryUserAssociatedWithEmail.loginMethods.find((lm) => { - return lm.hasSameEmailAs(email) && lm.verified; - }) !== undefined; - - if (emailVerified) { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } - - // finally, we check if the primary user has any other email / phone number - // associated with this account - and if it does, then it means that - // there is a risk of account takeover, so we do not allow the token to be generated - let hasOtherEmailOrPhone = - primaryUserAssociatedWithEmail.loginMethods.find((lm) => { - // we do the extra undefined check below cause - // hasSameEmailAs returns false if the lm.email is undefined, and - // we want to check that the email is different as opposed to email - // not existing in lm. - return (lm.email !== undefined && !lm.hasSameEmailAs(email)) || lm.phoneNumber !== undefined; - }) !== undefined; - if (hasOtherEmailOrPhone) { - return { - status: "PASSWORD_RESET_NOT_ALLOWED", - reason: - "Reset password link was not created because of account take over risk. Please contact support. (ERR_CODE_001)", - }; - } else { - return await generateAndSendPasswordResetToken( - primaryUserAssociatedWithEmail.id, - emailPasswordAccount.recipeUserId - ); - } + return await generateAndSendPasswordResetToken( + primaryUserAssociatedWithEmail.id, + emailPasswordAccount.recipeUserId + ); }, passwordResetPOST: async function ({ formFields, diff --git a/lib/ts/recipe/emailpassword/recipeImplementation.ts b/lib/ts/recipe/emailpassword/recipeImplementation.ts index e1f44b073..4c2e39d70 100644 --- a/lib/ts/recipe/emailpassword/recipeImplementation.ts +++ b/lib/ts/recipe/emailpassword/recipeImplementation.ts @@ -1,5 +1,6 @@ import { RecipeInterface, TypeNormalisedInput } from "./types"; import AccountLinking from "../accountlinking/recipe"; +import EmailVerification from "../emailverification/recipe"; import { Querier } from "../../querier"; import NormalisedURLPath from "../../normalisedURLPath"; import { getUser } from "../.."; @@ -252,6 +253,41 @@ export default function getRecipeInterface( } | { status: "PASSWORD_POLICY_VIOLATED_ERROR"; failureReason: string } > { + const accountLinking = AccountLinking.getInstance(); + if (input.email) { + const user = await getUser(input.recipeUserId.getAsString(), input.userContext); + + if (user === undefined) { + return { status: "UNKNOWN_USER_ID_ERROR" }; + } + + const evInstance = EmailVerification.getInstance(); + + let isEmailVerified = false; + if (evInstance) { + isEmailVerified = await evInstance.recipeInterfaceImpl.isEmailVerified({ + recipeUserId: input.recipeUserId, + email: input.email, + userContext: input.userContext, + }); + } + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isEmailVerified, + newEmail: input.email, + session: undefined, + userContext: input.userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "ACCOUNT_TAKEOVER_RISK" + ? "New email cannot be applied to existing account because of account takeover risks." + : "New email cannot be applied to existing account because of there is another primary user with the same email address.", + }; + } + } if (input.applyPasswordPolicy || input.applyPasswordPolicy === undefined) { let formFields = getEmailPasswordConfig().signUpFeature.formFields; if (input.password !== undefined) { diff --git a/lib/ts/recipe/thirdparty/api/implementation.ts b/lib/ts/recipe/thirdparty/api/implementation.ts index f9ea8884c..e91bb2dc3 100644 --- a/lib/ts/recipe/thirdparty/api/implementation.ts +++ b/lib/ts/recipe/thirdparty/api/implementation.ts @@ -1,5 +1,4 @@ import { APIInterface } from "../"; -import AccountLinking from "../../accountlinking/recipe"; import EmailVerification from "../../emailverification"; import EmailVerificationRecipe from "../../emailverification/recipe"; import { AuthUtils } from "../../../authUtils"; @@ -35,7 +34,7 @@ export default function getAPIInterface(): APIInterface { "Cannot sign in / up due to security reasons. Please contact support. (ERR_CODE_023)", }, }; - const { provider, tenantId, options, session, userContext } = input; + const { provider, tenantId, options, userContext } = input; let oAuthTokensToUse: any = {}; @@ -99,9 +98,9 @@ export default function getAPIInterface(): APIInterface { // recipe function checks to a minimum so that the dev has complete control of // what they can do. - // The isEmailChangeAllowed function takes in a isVerified boolean. Now, even though - // we already have that from the input, that's just what the provider says. If the - // provider says that the email is NOT verified, it could have been that the email + // The isEmailChangeAllowed and preAuthChecks functions take an isVerified boolean. + // Now, even though we already have that from the input, that's just what the provider says. + // If the provider says that the email is NOT verified, it could have been that the email // is verified on the user's account via supertokens on a previous sign in / up. // So we just check that as well before calling isEmailChangeAllowed @@ -114,74 +113,6 @@ export default function getAPIInterface(): APIInterface { userContext ); } - - /** - * In this API, during only a sign in, we check for isEmailChangeAllowed first, then - * change the email by calling the recipe function, then check if is sign in allowed. - * This may result in a few states where email change is allowed, but still, sign in - * is not allowed: - * - * Various outcomes of isSignInAllowed vs isEmailChangeAllowed - * isSignInAllowed result: - * - is primary user -> TRUE - * - is recipe user - * - other recipe user exists - * - no -> TRUE - * - yes - * - email verified -> TRUE - * - email unverified -> FALSE - * - other primary user exists - * - no -> TRUE - * - yes - * - email verification status - * - this && primary -> TRUE - * - !this && !primary -> FALSE - * - this && !primary -> FALSE - * - !this && primary -> FALSE - * - * isEmailChangeAllowed result: - * - is primary user -> TRUE - * - is recipe user - * - other recipe user exists - * - no -> TRUE - * - yes - * - email verified -> TRUE - * - email unverified -> TRUE - * - other primary user exists - * - no -> TRUE - * - yes - * - email verification status - * - this && primary -> TRUE - * - !this && !primary -> FALSE - * - this && !primary -> TRUE - * - !this && primary -> FALSE - * - * Based on the above, isEmailChangeAllowed can return true, but isSignInAllowed will return false - * in the following situations: - * - If a recipe user is signing in with a new email, other recipe users with the same email exist, - * and one of them is unverfied. In this case, the email change will happen in the social login - * recipe, but the user will not be able to login anyway. - * - * - If the recipe user is signing in with a new email, there exists a primary user with the same - * email, but this new email is verified for the recipe user already, but the primary user's email - * is not verified. - */ - - let isEmailChangeAllowed = await AccountLinking.getInstance().isEmailChangeAllowed({ - user: authenticatingUser.user, - isVerified: emailInfo.isVerified, - newEmail: emailInfo.id, - session, - userContext, - }); - - if (!isEmailChangeAllowed) { - return { - status: "SIGN_IN_UP_NOT_ALLOWED", - reason: - "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)", - }; - } } const preAuthChecks = await AuthUtils.preAuthChecks({ @@ -232,6 +163,11 @@ export default function getAPIInterface(): APIInterface { userContext, }); + if (response.status === "SIGN_IN_UP_NOT_ALLOWED") { + // In this case we do not need to do mapping, since the recipe function already has the right response shape. + return response; + } + if (response.status !== "OK") { logDebugMessage("signInUpPOST: erroring out because signInUp returned " + response.status); return AuthUtils.getErrorStatusResponseWithReason(response, errorCodeMap, "SIGN_IN_UP_NOT_ALLOWED"); diff --git a/lib/ts/recipe/thirdparty/recipeImplementation.ts b/lib/ts/recipe/thirdparty/recipeImplementation.ts index 58dbbc248..f730537a3 100644 --- a/lib/ts/recipe/thirdparty/recipeImplementation.ts +++ b/lib/ts/recipe/thirdparty/recipeImplementation.ts @@ -5,7 +5,7 @@ import { findAndCreateProviderInstance, mergeProvidersFromCoreAndStatic } from " import AccountLinking from "../accountlinking/recipe"; import MultitenancyRecipe from "../multitenancy/recipe"; import RecipeUserId from "../../recipeUserId"; -import { getUser } from "../.."; +import { getUser, listUsersByAccountInfo } from "../.."; import { User as UserType } from "../../types"; import { User } from "../../user"; import { AuthUtils } from "../../authUtils"; @@ -16,6 +16,33 @@ export default function getRecipeImplementation(querier: Querier, providers: Pro this: RecipeInterface, { thirdPartyId, thirdPartyUserId, email, isVerified, tenantId, session, userContext } ) { + const accountLinking = AccountLinking.getInstance(); + const users = await listUsersByAccountInfo( + tenantId, + { thirdParty: { id: thirdPartyId, userId: thirdPartyUserId } }, + false, + userContext + ); + + const user = users[0]; + if (user !== undefined) { + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isVerified, + newEmail: email, + session, + userContext: userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "PRIMARY_USER_CONFLICT" + ? "Email already associated with another primary user." + : "New email cannot be applied to existing account because of account takeover risks.", + }; + } + } let response = await querier.sendPostRequest( new NormalisedURLPath(`/${tenantId}/recipe/signinup`), { @@ -115,7 +142,9 @@ export default function getRecipeImplementation(querier: Querier, providers: Pro return { status: "SIGN_IN_UP_NOT_ALLOWED", reason: - "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)", + response.reason === "Email already associated with another primary user." + ? "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_005)" + : "Cannot sign in / up because new email cannot be applied to existing account. Please contact support. (ERR_CODE_024)", }; } diff --git a/test/emailpassword/updateEmailPass.test.js b/test/emailpassword/updateEmailPass.test.js index 55c0ac765..11b350187 100644 --- a/test/emailpassword/updateEmailPass.test.js +++ b/test/emailpassword/updateEmailPass.test.js @@ -127,7 +127,7 @@ describe(`updateEmailPassTest: ${printPath("[test/emailpassword/updateEmailPass. let res = await signIn("public", "test@gmail.com", "testPass123"); const res2 = await updateEmailOrPassword({ - userId: STExpress.convertToRecipeUserId(res.user.id), + recipeUserId: STExpress.convertToRecipeUserId(res.user.id), email: "test2@gmail.com", password: "test", }); @@ -224,7 +224,7 @@ describe(`updateEmailPassTest: ${printPath("[test/emailpassword/updateEmailPass. let res = await signIn("public", "test@gmail.com", "testPass123"); const res2 = await updateEmailOrPassword({ - userId: STExpress.convertToRecipeUserId(res.user.id), + recipeUserId: STExpress.convertToRecipeUserId(res.user.id), email: "test2@gmail.com", password: "1", }); From 257f2c9625a8bf250f77da07c769ab141ff36fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Tue, 2 Jul 2024 15:23:26 +0200 Subject: [PATCH 09/16] feat: call isEmailChangeAllowed in pwless updateUser (#875) * feat: call isEmailChangeAllowed in pwless updateUser * test: add updateUser to test-server * chore: remove unnecessary item from changelog * chore: extend changelog to mention exact function names --- CHANGELOG.md | 7 ++++ .../passwordless/recipeImplementation.js | 33 +++++++++++++++++ .../passwordless/recipeImplementation.ts | 36 +++++++++++++++++++ test/test-server/src/passwordless.ts | 19 ++++++++++ 4 files changed, 95 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbbeaad9c..29190143a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - the user has another email address or phone number associated with it - Account linking based on emails now require the email to be verified in both users if `shouldRequireVerification` is set to `true` instead of only requiring it for the recipe user. - The access token cookie expiry has been changed from 100 years to 1 year due to some browsers capping the maximum expiry at 400 days. No action is needed on your part. +- Recipe functions that update the email address of users now call `isEmailChangeAllowed` to check if the email update should be allowed or not. + - This only has an effect if account linking is turned on. + - This is aimed to help you avoid security issues. + - `isEmailChangeAllowed` is now called in functions: + - `updateUser` (Passwordless recipe) + - `updateEmailOrPassword` (EmailPassword recipe) + - `manuallyCreateOrUpdateUser` (ThirdParty recipe) ### Changes diff --git a/lib/build/recipe/passwordless/recipeImplementation.js b/lib/build/recipe/passwordless/recipeImplementation.js index 8a3782d64..15e4e9238 100644 --- a/lib/build/recipe/passwordless/recipeImplementation.js +++ b/lib/build/recipe/passwordless/recipeImplementation.js @@ -6,6 +6,7 @@ var __importDefault = }; Object.defineProperty(exports, "__esModule", { value: true }); const recipe_1 = __importDefault(require("../accountlinking/recipe")); +const recipe_2 = __importDefault(require("../emailverification/recipe")); const normalisedURLPath_1 = __importDefault(require("../../normalisedURLPath")); const logger_1 = require("../../logger"); const user_1 = require("../../user"); @@ -147,6 +148,38 @@ function getRecipeInterface(querier) { return { status: "OK" }; }, updateUser: async function (input) { + const accountLinking = recipe_1.default.getInstance(); + if (input.email) { + const user = await __1.getUser(input.recipeUserId.getAsString(), input.userContext); + if (user === undefined) { + return { status: "UNKNOWN_USER_ID_ERROR" }; + } + const evInstance = recipe_2.default.getInstance(); + let isEmailVerified = false; + if (evInstance) { + isEmailVerified = await evInstance.recipeInterfaceImpl.isEmailVerified({ + recipeUserId: input.recipeUserId, + email: input.email, + userContext: input.userContext, + }); + } + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isEmailVerified, + newEmail: input.email, + session: undefined, + userContext: input.userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "ACCOUNT_TAKEOVER_RISK" + ? "New email cannot be applied to existing account because of account takeover risks." + : "New email cannot be applied to existing account because of there is another primary user with the same email address.", + }; + } + } let response = await querier.sendPutRequest( new normalisedURLPath_1.default(`/recipe/user`), copyAndRemoveUserContextAndTenantId(input), diff --git a/lib/ts/recipe/passwordless/recipeImplementation.ts b/lib/ts/recipe/passwordless/recipeImplementation.ts index 71ab078b0..d157ce3b0 100644 --- a/lib/ts/recipe/passwordless/recipeImplementation.ts +++ b/lib/ts/recipe/passwordless/recipeImplementation.ts @@ -1,6 +1,7 @@ import { RecipeInterface } from "./types"; import { Querier } from "../../querier"; import AccountLinking from "../accountlinking/recipe"; +import EmailVerification from "../emailverification/recipe"; import NormalisedURLPath from "../../normalisedURLPath"; import { logDebugMessage } from "../../logger"; import { User } from "../../user"; @@ -161,6 +162,41 @@ export default function getRecipeInterface(querier: Querier): RecipeInterface { return { status: "OK" }; }, updateUser: async function (input) { + const accountLinking = AccountLinking.getInstance(); + if (input.email) { + const user = await getUser(input.recipeUserId.getAsString(), input.userContext); + + if (user === undefined) { + return { status: "UNKNOWN_USER_ID_ERROR" }; + } + + const evInstance = EmailVerification.getInstance(); + + let isEmailVerified = false; + if (evInstance) { + isEmailVerified = await evInstance.recipeInterfaceImpl.isEmailVerified({ + recipeUserId: input.recipeUserId, + email: input.email, + userContext: input.userContext, + }); + } + const isEmailChangeAllowed = await accountLinking.isEmailChangeAllowed({ + user, + isVerified: isEmailVerified, + newEmail: input.email, + session: undefined, + userContext: input.userContext, + }); + if (!isEmailChangeAllowed.allowed) { + return { + status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR", + reason: + isEmailChangeAllowed.reason === "ACCOUNT_TAKEOVER_RISK" + ? "New email cannot be applied to existing account because of account takeover risks." + : "New email cannot be applied to existing account because of there is another primary user with the same email address.", + }; + } + } let response = await querier.sendPutRequest( new NormalisedURLPath(`/recipe/user`), copyAndRemoveUserContextAndTenantId(input), diff --git a/test/test-server/src/passwordless.ts b/test/test-server/src/passwordless.ts index 35a8381d3..cc8c7fd99 100644 --- a/test/test-server/src/passwordless.ts +++ b/test/test-server/src/passwordless.ts @@ -1,4 +1,5 @@ import { Router } from "express"; +import SuperTokens from "../../.."; import Passwordless from "../../../recipe/passwordless"; import { convertRequestSessionToSessionObject, serializeRecipeUserId, serializeUser } from "./utils"; import { logger } from "./logger"; @@ -67,6 +68,24 @@ const router = Router() } catch (e) { next(e); } + }) + .post("/updateuser", async (req, res, next) => { + try { + logDebugMessage("Passwordless:updateUser %j", req.body); + const response = await Passwordless.updateUser({ + recipeUserId: SuperTokens.convertToRecipeUserId(req.body.recipeUserId), + email: req.body.email, + phoneNumber: req.body.phoneNumber, + userContext: req.body.userContext, + }); + res.json({ + ...response, + ...serializeUser(response), + ...serializeRecipeUserId(response), + }); + } catch (e) { + next(e); + } }); export default router; From 58da933b24c92a232e55629c6d5c44ff4e6a184c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Tue, 2 Jul 2024 15:24:00 +0200 Subject: [PATCH 10/16] test: add logging to default overrides in test-server (#876) --- test/test-server/src/index.ts | 346 ++++++++++++++++-------- test/test-server/src/overrideLogging.ts | 68 +++++ 2 files changed, 299 insertions(+), 115 deletions(-) create mode 100644 test/test-server/src/overrideLogging.ts diff --git a/test/test-server/src/index.ts b/test/test-server/src/index.ts index 2856ffa26..7787e0d15 100644 --- a/test/test-server/src/index.ts +++ b/test/test-server/src/index.ts @@ -45,6 +45,8 @@ import thirdPartyRoutes from "./thirdparty"; import userMetadataRoutes from "./usermetadata"; import TOTPRoutes from "./totp"; import { getFunc, resetOverrideParams, getOverrideParams } from "./testFunctionMapper"; +import OverrideableBuilder from "supertokens-js-override"; +import { resetOverrideLogs, logOverrideEvent, getOverrideLogs } from "./overrideLogging"; const { logDebugMessage } = logger("com.supertokens:node-test-server"); @@ -69,6 +71,7 @@ defaultSTInit(); function STReset() { resetOverrideParams(); + resetOverrideLogs(); EmailPasswordRecipe.reset(); SessionRecipe.reset(); @@ -89,55 +92,73 @@ function initST(config: any) { const recipeList: RecipeListFunction[] = []; - const settings = JSON.parse(config); - logDebugMessage("initST %j", settings); + const parsedConfig = JSON.parse(config); + const init = { + ...parsedConfig, + }; + logDebugMessage("initST %j", init); - settings.recipeList.forEach((recipe) => { + init.recipeList.forEach((recipe) => { const config = recipe.config ? JSON.parse(recipe.config) : undefined; if (recipe.recipeId === "emailpassword") { let init: EmailPasswordTypeInput = { ...config, }; - if (config?.override?.apis) { - init.override = { - ...init.override, - apis: getFunc(`${config?.override.apis}`), - }; - } - - if (config?.emailDelivery?.override) { - init.emailDelivery = { - ...config?.emailDelivery, - override: getFunc(`${config?.emailDelivery.override}`), - }; - } - - recipeList.push(EmailPassword.init(init)); + recipeList.push( + EmailPassword.init({ + ...init, + emailDelivery: { + ...config?.emailDelivery, + override: overrideBuilderWithLogging( + "EmailPassword.emailDelivery.override", + config?.emailDelivery?.override + ), + }, + override: { + apis: overrideBuilderWithLogging("EmailPassword.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "EmailPassword.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "session") { - let init: SessionTypeInput = { - ...config, - }; - if (config?.override?.functions) { - init.override = { - ...init.override, - functions: getFunc(`${config?.override.functions}`), - }; - } - recipeList.push(Session.init(init)); + recipeList.push( + Session.init({ + ...config, + override: { + apis: overrideBuilderWithLogging("Session.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "Session.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "accountlinking") { - let init: AccountLinkingTypeInput = { - ...config, - }; - if (config?.shouldDoAutomaticAccountLinking) { - init.shouldDoAutomaticAccountLinking = getFunc(`${config.shouldDoAutomaticAccountLinking}`); - } - if (config?.onAccountLinked) { - init.onAccountLinked = getFunc(`${config.onAccountLinked}`); - } - recipeList.push(AccountLinking.init(init)); + recipeList.push( + AccountLinking.init({ + ...config, + shouldDoAutomaticAccountLinking: callbackWithLog( + "AccountLinking.shouldDoAutomaticAccountLinking", + config.shouldDoAutomaticAccountLinking, + { + shouldAutomaticallyLink: false, + } + ), + onAccountLinked: callbackWithLog("AccountLinking.onAccountLinked", config.onAccountLinked), + override: { + functions: overrideBuilderWithLogging( + "AccountLinking.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "thirdparty") { let init: ThirdPartyTypeInput = { @@ -148,104 +169,139 @@ function initST(config: any) { ...config.signInAndUpFeature, providers: config.signInAndUpFeature.providers.map((p) => ({ ...p, - ...(p.override ? { override: getFunc(`${p.override}`) } : {}), + override: p.override && getFunc(p.override), })), }; } - if (config?.override?.apis) { - init.override = { - ...init.override, - ...(config?.override.apis ? { apis: getFunc(`${config?.override.apis}`) } : {}), - }; - } - recipeList.push(ThirdParty.init(init)); + recipeList.push( + ThirdParty.init({ + ...init, + override: { + apis: overrideBuilderWithLogging("ThirdParty.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "ThirdParty.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "emailverification") { - let init: EmailVerificationTypeInput = { - ...config, - }; - if (config?.emailDelivery?.override) { - init.emailDelivery = { - ...config?.emailDelivery, - override: getFunc(`${config?.emailDelivery.override}`), - }; - } - if (config?.getEmailForRecipeUserId) { - init.getEmailForRecipeUserId = getFunc(`${config?.getEmailForRecipeUserId}`); - } - if (config?.override?.functions) { - init.override = { - ...init.override, - functions: getFunc(`${config?.override.functions}`), - }; - } - recipeList.push(EmailVerification.init(init)); + recipeList.push( + EmailVerification.init({ + ...config, + emailDelivery: { + ...config?.emailDelivery, + override: overrideBuilderWithLogging( + "EmailVerification.emailDelivery", + config?.emailDelivery?.override + ), + }, + getEmailForRecipeUserId: callbackWithLog( + "EmailVerification.getEmailForRecipeUserId", + config?.getEmailForRecipeUserId, + { status: "UNKNOWN_USER_ID_ERROR" } + ), + override: { + apis: overrideBuilderWithLogging("EmailVerification.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "EmailVerification.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "multitenancy") { - recipeList.push(Multitenancy.init(config)); + recipeList.push( + Multitenancy.init({ + ...config, + override: { + apis: overrideBuilderWithLogging("Multitenancy.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "Multitenancy.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "passwordless") { - let init: PasswordlessTypeInput = { - ...config, - }; - - if (config?.emailDelivery?.service?.sendEmail) { - init.emailDelivery = { - ...config?.emailDelivery, - service: { - ...config?.emailDelivery?.service, - sendEmail: getFunc(`${config?.emailDelivery?.service?.sendEmail}`), + recipeList.push( + Passwordless.init({ + ...config, + emailDelivery: { + ...config?.emailDelivery, + override: overrideBuilderWithLogging("Passwordless.emailDelivery", undefined), + service: { + ...config?.emailDelivery?.service, + sendEmail: + config?.emailDelivery?.service?.sendEmail && + getFunc(config?.emailDelivery?.service?.sendEmail), + }, }, - }; - } - if (config?.smsDelivery?.service?.sendSms) { - init.smsDelivery = { - ...config?.smsDelivery, - service: { - ...config?.smsDelivery?.service, - sendSms: getFunc(`${config?.smsDelivery?.service?.sendSms}`), + smsDelivery: { + ...config?.smsDelivery, + override: overrideBuilderWithLogging("Passwordless.smsDelivery", undefined), + service: { + ...config?.smsDelivery?.service, + sendSms: + config?.smsDelivery?.service?.sendSms && getFunc(config?.smsDelivery?.service?.sendSms), + }, }, - }; - } - if (config?.override?.apis) { - init.override = { - ...init.override, - ...(config?.override.apis ? { apis: getFunc(`${config?.override.apis}`) } : {}), - }; - } - recipeList.push(Passwordless.init(init)); + override: { + apis: overrideBuilderWithLogging("Passwordless.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "Passwordless.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "multifactorauth") { - let initConfig: MFATypeInput = { - ...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(MultiFactorAuth.init(initConfig)); + recipeList.push( + MultiFactorAuth.init({ + ...config, + override: { + apis: overrideBuilderWithLogging("MultiFactorAuth.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "MultiFactorAuth.override.functions", + config?.override?.functions + ), + }, + }) + ); } if (recipe.recipeId === "totp") { - recipeList.push(TOTP.init(config)); + recipeList.push( + TOTP.init({ + ...config, + override: { + apis: overrideBuilderWithLogging("Multitenancy.override.apis", config?.override?.apis), + functions: overrideBuilderWithLogging( + "Multitenancy.override.functions", + config?.override?.functions + ), + }, + }) + ); } }); - settings.recipeList = recipeList; + init.recipeList = recipeList; - if (settings.supertokens?.networkInterceptor) { - settings.supertokens.networkInterceptor = getFunc(`${settings.supertokens.networkInterceptor}`); - } + const interceptor = + parsedConfig?.supertokens?.networkInterceptor && getFunc(parsedConfig.supertokens.networkInterceptor); + init.supertokens.networkInterceptor = loggingOverrideFuncSync("networkInterceptor", (req, userContext) => { + if (interceptor) { + return interceptor(req, userContext); + } + return req; + }); - supertokens.init(settings); + supertokens.init(init); } const app = express(); @@ -289,6 +345,10 @@ app.post("/test/mockexternalapi", async (req, res, next) => { res.json({ ok: true }); }); +app.get("/test/getoverridelogs", async (req, res, next) => { + res.json({ logs: getOverrideLogs() }); +}); + app.get("/test/waitforevent", async (req, res, next) => { try { logDebugMessage("ProcessState:waitForEvent %j", req.query); @@ -371,3 +431,59 @@ app.use((err, req, res, next) => { app.listen(API_PORT, "localhost", () => { logDebugMessage(`node-test-server-server started on localhost:${API_PORT}`); }); + +function loggingOverrideFuncSync(name: string, originalImpl: (...args: any[]) => Promise) { + return function (...args: any[]) { + logOverrideEvent(name, "CALL", args); + try { + const res = originalImpl(...args); + logOverrideEvent(name, "RES", res); + return res; + } catch (err) { + logOverrideEvent(name, "REJ", err); + throw err; + } + }; +} +function loggingOverrideFunc(name: string, originalImpl: (...args: any[]) => Promise) { + return async function (...args: any[]) { + logOverrideEvent(name, "CALL", args); + try { + const res = await originalImpl(...args); + logOverrideEvent(name, "RES", res); + return res; + } catch (err) { + logOverrideEvent(name, "REJ", err); + throw err; + } + }; +} + +function callbackWithLog(name: string, overrideName: string, defaultValue?: T) { + const impl = overrideName ? getFunc(overrideName) : () => defaultValue; + return loggingOverrideFunc(name, impl); +} + +function overrideBuilderWithLogging any) | undefined>>( + name: string, + overrideName?: string +) { + return (originalImpl: T, builder: OverrideableBuilder) => { + builder.override((oI) => { + const keys = Object.keys(oI); + // this would be hard to type (and ultimately not worth it) because it is a generic type + // and writing to those through an index is not allowed by default + const ret: any = {}; // Partial + + for (const k of keys) { + ret[k] = loggingOverrideFunc(`${name}.${k}`, oI[k]!.bind(oI)); + } + return ret; + }); + + if (overrideName !== undefined) { + builder.override(getFunc(overrideName)); + } + return originalImpl; + }; +} diff --git a/test/test-server/src/overrideLogging.ts b/test/test-server/src/overrideLogging.ts new file mode 100644 index 000000000..f8dd686e4 --- /dev/null +++ b/test/test-server/src/overrideLogging.ts @@ -0,0 +1,68 @@ +import { IncomingMessage } from "http"; + +let overrideLogs: { t: number; name: string; type: "RES" | "REJ" | "CALL"; data: unknown }[] = []; + +export function resetOverrideLogs() { + overrideLogs = []; +} + +export function getOverrideLogs() { + return overrideLogs; +} + +export function logOverrideEvent(name: string, type: "RES" | "REJ" | "CALL", data: unknown) { + overrideLogs.push({ + t: Date.now(), + type, + name, + data: transformLoggedData(data), + }); +} + +function transformLoggedData(data: any, visited: Set = new Set()) { + if (typeof data !== "object") { + return data; + } + if (data === null) { + return data; + } + + if (data instanceof IncomingMessage) { + // We do not actually want these in the logs because: + // 1. They are not useful + // 2. They are very big objects + // 3. They contain circular references + return "IncomingMessage obj"; + } + + if (visited.has(data)) { + return "VISITED"; + } + visited.add(data); + + if (data instanceof Array) { + return data.map((i) => transformLoggedData(i, visited)); + } + if ("toJson" in data) { + return data.toJson(); + } + if ("getAsString" in data) { + return data.getAsString(); + } + if ("getAsStringDangerous" in data) { + return data.getAsStringDangerous(); + } + if ("getAllSessionTokensDangerously" in data) { + return data.getAllSessionTokensDangerously(); + } + + return Object.fromEntries( + Array.from(Object.keys(data)).map((k) => { + if (Object.getOwnPropertyDescriptor(data, k)?.get === undefined) { + return [k, transformLoggedData(data[k], visited)]; + } else { + return [k, "prop is a getter for lazy loading"]; + } + }) + ); +} From 8c977f47ac7711e438bf4905b0b8c5dc78dab7a2 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Thu, 4 Jul 2024 11:59:19 +0200 Subject: [PATCH 11/16] fix: circular dependency --- lib/build/recipe/jwt/api/implementation.js | 8 +------- lib/ts/recipe/jwt/api/implementation.ts | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/build/recipe/jwt/api/implementation.js b/lib/build/recipe/jwt/api/implementation.js index d81fc72b0..4e8bc342f 100644 --- a/lib/build/recipe/jwt/api/implementation.js +++ b/lib/build/recipe/jwt/api/implementation.js @@ -13,13 +13,7 @@ * 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 recipe_1 = __importDefault(require("../../oauth2/recipe")); function getAPIImplementation() { return { getJWKSGET: async function ({ options, userContext }) { @@ -27,7 +21,7 @@ function getAPIImplementation() { if (resp.validityInSeconds !== undefined) { options.res.setHeader("Cache-Control", `max-age=${resp.validityInSeconds}, must-revalidate`, false); } - const oauth2 = recipe_1.default.getInstance(); + const oauth2 = require("../../oauth2").getInstance(); // TODO: dirty hack until we get core support if (oauth2 !== undefined) { const oauth2JWKSRes = await fetch("http://localhost:4444/.well-known/jwks.json"); diff --git a/lib/ts/recipe/jwt/api/implementation.ts b/lib/ts/recipe/jwt/api/implementation.ts index e3418a969..5a029d4a1 100644 --- a/lib/ts/recipe/jwt/api/implementation.ts +++ b/lib/ts/recipe/jwt/api/implementation.ts @@ -13,7 +13,6 @@ * under the License. */ -import OAuth2 from "../../oauth2/recipe"; import { APIInterface, APIOptions, JsonWebKey } from "../types"; import { GeneralErrorResponse, UserContext } from "../../../types"; @@ -32,7 +31,7 @@ export default function getAPIImplementation(): APIInterface { options.res.setHeader("Cache-Control", `max-age=${resp.validityInSeconds}, must-revalidate`, false); } - const oauth2 = OAuth2.getInstance(); + const oauth2 = require("../../oauth2").getInstance(); // TODO: dirty hack until we get core support if (oauth2 !== undefined) { const oauth2JWKSRes = await fetch("http://localhost:4444/.well-known/jwks.json"); From aef8781fb2cfc6e7530f71594b85fd929e42b72e Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Fri, 5 Jul 2024 13:29:53 +0200 Subject: [PATCH 12/16] fix: fix types in oauth2 index exposed functions --- lib/build/recipe/oauth2/index.d.ts | 9 ++++----- lib/build/recipe/oauth2/index.js | 9 +++++---- lib/ts/recipe/oauth2/index.ts | 30 +++++++++++++++++++++--------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/build/recipe/oauth2/index.d.ts b/lib/build/recipe/oauth2/index.d.ts index 6df42ad6c..f0e29f04c 100644 --- a/lib/build/recipe/oauth2/index.d.ts +++ b/lib/build/recipe/oauth2/index.d.ts @@ -1,5 +1,4 @@ // @ts-nocheck -import { UserContext } from "../../types"; import Recipe from "./recipe"; import { APIInterface, @@ -14,7 +13,7 @@ export default class Wrapper { static init: typeof Recipe.init; static getOAuth2Clients( input: GetOAuth2ClientsInput, - userContext: UserContext + userContext?: Record ): Promise< | { status: "OK"; @@ -29,7 +28,7 @@ export default class Wrapper { >; static createOAuth2Client( input: CreateOAuth2ClientInput, - userContext: UserContext + userContext?: Record ): Promise< | { status: "OK"; @@ -43,7 +42,7 @@ export default class Wrapper { >; static updateOAuth2Client( input: UpdateOAuth2ClientInput, - userContext: UserContext + userContext?: Record ): Promise< | { status: "OK"; @@ -57,7 +56,7 @@ export default class Wrapper { >; static deleteOAuth2Client( input: DeleteOAuth2ClientInput, - userContext: UserContext + userContext?: Record ): Promise< | { status: "OK"; diff --git a/lib/build/recipe/oauth2/index.js b/lib/build/recipe/oauth2/index.js index 7769bc796..3a0f72db4 100644 --- a/lib/build/recipe/oauth2/index.js +++ b/lib/build/recipe/oauth2/index.js @@ -20,27 +20,28 @@ var __importDefault = }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deleteOAuth2Client = exports.updateOAuth2Client = exports.createOAuth2Client = exports.getOAuth2Clients = exports.init = void 0; +const utils_1 = require("../../utils"); const recipe_1 = __importDefault(require("./recipe")); class Wrapper { static async getOAuth2Clients(input, userContext) { return await recipe_1.default .getInstanceOrThrowError() - .recipeInterfaceImpl.getOAuth2Clients(input, userContext); + .recipeInterfaceImpl.getOAuth2Clients(input, utils_1.getUserContext(userContext)); } static async createOAuth2Client(input, userContext) { return await recipe_1.default .getInstanceOrThrowError() - .recipeInterfaceImpl.createOAuth2Client(input, userContext); + .recipeInterfaceImpl.createOAuth2Client(input, utils_1.getUserContext(userContext)); } static async updateOAuth2Client(input, userContext) { return await recipe_1.default .getInstanceOrThrowError() - .recipeInterfaceImpl.updateOAuth2Client(input, userContext); + .recipeInterfaceImpl.updateOAuth2Client(input, utils_1.getUserContext(userContext)); } static async deleteOAuth2Client(input, userContext) { return await recipe_1.default .getInstanceOrThrowError() - .recipeInterfaceImpl.deleteOAuth2Client(input, userContext); + .recipeInterfaceImpl.deleteOAuth2Client(input, utils_1.getUserContext(userContext)); } } exports.default = Wrapper; diff --git a/lib/ts/recipe/oauth2/index.ts b/lib/ts/recipe/oauth2/index.ts index 6962a0e09..c1bf5cef3 100644 --- a/lib/ts/recipe/oauth2/index.ts +++ b/lib/ts/recipe/oauth2/index.ts @@ -13,7 +13,7 @@ * under the License. */ -import { UserContext } from "../../types"; +import { getUserContext } from "../../utils"; import Recipe from "./recipe"; import { APIInterface, @@ -28,17 +28,29 @@ import { export default class Wrapper { static init = Recipe.init; - static async getOAuth2Clients(input: GetOAuth2ClientsInput, userContext: UserContext) { - return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.getOAuth2Clients(input, userContext); + static async getOAuth2Clients(input: GetOAuth2ClientsInput, userContext?: Record) { + return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.getOAuth2Clients( + input, + getUserContext(userContext) + ); } - static async createOAuth2Client(input: CreateOAuth2ClientInput, userContext: UserContext) { - return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.createOAuth2Client(input, userContext); + static async createOAuth2Client(input: CreateOAuth2ClientInput, userContext?: Record) { + return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.createOAuth2Client( + input, + getUserContext(userContext) + ); } - static async updateOAuth2Client(input: UpdateOAuth2ClientInput, userContext: UserContext) { - return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.updateOAuth2Client(input, userContext); + static async updateOAuth2Client(input: UpdateOAuth2ClientInput, userContext?: Record) { + return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.updateOAuth2Client( + input, + getUserContext(userContext) + ); } - static async deleteOAuth2Client(input: DeleteOAuth2ClientInput, userContext: UserContext) { - return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.deleteOAuth2Client(input, userContext); + static async deleteOAuth2Client(input: DeleteOAuth2ClientInput, userContext?: Record) { + return await Recipe.getInstanceOrThrowError().recipeInterfaceImpl.deleteOAuth2Client( + input, + getUserContext(userContext) + ); } } From c600d2620cbc8d87fd9b4c2756db202a08451701 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Mon, 8 Jul 2024 12:37:52 +0200 Subject: [PATCH 13/16] feat: add token building callbacks --- lib/build/recipe/oauth2/recipe.d.ts | 5 +- lib/build/recipe/oauth2/recipe.js | 25 ++++++++- .../recipe/oauth2/recipeImplementation.d.ts | 5 +- .../recipe/oauth2/recipeImplementation.js | 55 +++++++++++-------- lib/build/recipe/oauth2/types.d.ts | 27 +++++++++ lib/ts/recipe/oauth2/recipe.ts | 36 +++++++++++- lib/ts/recipe/oauth2/recipeImplementation.ts | 55 ++++++++++++++----- lib/ts/recipe/oauth2/types.ts | 35 ++++++++++++ 8 files changed, 198 insertions(+), 45 deletions(-) diff --git a/lib/build/recipe/oauth2/recipe.d.ts b/lib/build/recipe/oauth2/recipe.d.ts index 2bab64401..8efd16f20 100644 --- a/lib/build/recipe/oauth2/recipe.d.ts +++ b/lib/build/recipe/oauth2/recipe.d.ts @@ -3,11 +3,13 @@ import error from "../../error"; import type { BaseRequest, BaseResponse } from "../../framework"; import NormalisedURLPath from "../../normalisedURLPath"; import RecipeModule from "../../recipeModule"; -import { APIHandled, HTTPMethod, NormalisedAppinfo, RecipeListFunction, UserContext } from "../../types"; +import { APIHandled, HTTPMethod, JSONObject, NormalisedAppinfo, RecipeListFunction, UserContext } from "../../types"; import { APIInterface, RecipeInterface, TypeInput, TypeNormalisedInput } from "./types"; +import { User } from "../../user"; export default class Recipe extends RecipeModule { static RECIPE_ID: string; private static instance; + private idTokenBuilders; config: TypeNormalisedInput; recipeInterfaceImpl: RecipeInterface; apiImpl: APIInterface; @@ -30,4 +32,5 @@ export default class Recipe extends RecipeModule { handleError(error: error, _: BaseRequest, __: BaseResponse, _userContext: UserContext): Promise; getAllCORSHeaders(): string[]; isErrorFromThisRecipe(err: any): err is error; + getDefaultIdTokenPayload(user: User, scopes: string[], userContext: UserContext): Promise; } diff --git a/lib/build/recipe/oauth2/recipe.js b/lib/build/recipe/oauth2/recipe.js index 8b031b748..9a7a01277 100644 --- a/lib/build/recipe/oauth2/recipe.js +++ b/lib/build/recipe/oauth2/recipe.js @@ -37,6 +37,7 @@ const supertokens_js_override_1 = __importDefault(require("supertokens-js-overri class Recipe extends recipeModule_1.default { constructor(recipeId, appInfo, isInServerlessEnv, config) { super(recipeId, appInfo); + this.idTokenBuilders = []; this.handleAPIRequest = async (id, _tenantId, req, res, _path, _method, userContext) => { let options = { config: this.config, @@ -73,7 +74,8 @@ class Recipe extends recipeModule_1.default { recipeImplementation_1.default( querier_1.Querier.getNewInstanceOrThrowError(recipeId), this.config, - appInfo + appInfo, + this.getDefaultIdTokenPayload ) ); this.recipeInterfaceImpl = builder.override(this.config.override.functions).build(); @@ -177,6 +179,27 @@ class Recipe extends recipeModule_1.default { isErrorFromThisRecipe(err) { return error_1.default.isErrorFromSuperTokens(err) && err.fromRecipe === Recipe.RECIPE_ID; } + async getDefaultIdTokenPayload(user, scopes, userContext) { + let payload = {}; + if (scopes.includes("email")) { + payload.email = user === null || user === void 0 ? void 0 : user.emails[0]; + payload.email_verified = user.loginMethods.some( + (lm) => lm.hasSameEmailAs(user === null || user === void 0 ? void 0 : user.emails[0]) && lm.verified + ); + } + if (scopes.includes("phoneNumber")) { + payload.phoneNumber = user === null || user === void 0 ? void 0 : user.phoneNumbers[0]; + payload.phoneNumber_verified = user.loginMethods.some( + (lm) => + lm.hasSamePhoneNumberAs(user === null || user === void 0 ? void 0 : user.phoneNumbers[0]) && + lm.verified + ); + } + for (const fn of this.idTokenBuilders) { + payload = Object.assign(Object.assign({}, payload), await fn(user, scopes, userContext)); + } + return payload; + } } exports.default = Recipe; Recipe.RECIPE_ID = "oauth2"; diff --git a/lib/build/recipe/oauth2/recipeImplementation.d.ts b/lib/build/recipe/oauth2/recipeImplementation.d.ts index d2a1542a2..0029a9016 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.d.ts +++ b/lib/build/recipe/oauth2/recipeImplementation.d.ts @@ -1,9 +1,10 @@ // @ts-nocheck import { Querier } from "../../querier"; import { NormalisedAppinfo } from "../../types"; -import { RecipeInterface, TypeNormalisedInput } from "./types"; +import { RecipeInterface, TypeNormalisedInput, PayloadBuilderFunction } from "./types"; export default function getRecipeInterface( querier: Querier, _config: TypeNormalisedInput, - _appInfo: NormalisedAppinfo + _appInfo: NormalisedAppinfo, + getDefaultIdTokenPayload: PayloadBuilderFunction ): RecipeInterface; diff --git a/lib/build/recipe/oauth2/recipeImplementation.js b/lib/build/recipe/oauth2/recipeImplementation.js index 28c889ec9..ac5e17e13 100644 --- a/lib/build/recipe/oauth2/recipeImplementation.js +++ b/lib/build/recipe/oauth2/recipeImplementation.js @@ -24,7 +24,7 @@ const querier_1 = require("../../querier"); const utils_1 = require("../../utils"); const OAuth2Client_1 = require("./OAuth2Client"); const __1 = require("../.."); -function getRecipeInterface(querier, _config, _appInfo) { +function getRecipeInterface(querier, _config, _appInfo, getDefaultIdTokenPayload) { return { getLoginRequest: async function (input) { const resp = await querier.sendGetRequest( @@ -207,7 +207,7 @@ function getRecipeInterface(querier, _config, _appInfo) { ); }, authorization: async function (input) { - var _a, _b, _c, _d, _e; + var _a, _b, _c, _d; const resp = await querier.sendGetRequestWithResponseHeaders( new normalisedURLPath_1.default(`/recipe/oauth2/pub/auth`), input.params, @@ -235,25 +235,24 @@ function getRecipeInterface(querier, _config, _appInfo) { consentRequest.skip || ((_a = consentRequest.client) === null || _a === void 0 ? void 0 : _a.skipConsent) ) { - const idToken = {}; - if ( - (_b = consentRequest.requestedScope) === null || _b === void 0 ? void 0 : _b.includes("email") - ) { - idToken.email = user === null || user === void 0 ? void 0 : user.emails[0]; - idToken.email_verified = user.loginMethods.some( - (lm) => lm.hasSameEmailAs(idToken.email) && lm.verified - ); - } - if ( - (_c = consentRequest.requestedScope) === null || _c === void 0 - ? void 0 - : _c.includes("phoneNumber") - ) { - idToken.phoneNumber = user === null || user === void 0 ? void 0 : user.phoneNumbers[0]; - idToken.phoneNumber_verified = user.loginMethods.some( - (lm) => lm.hasSamePhoneNumberAs(idToken.phoneNumber) && lm.verified - ); - } + const idToken = this.buildIdTokenPayload({ + user, + session: input.session, + defaultPayload: await getDefaultIdTokenPayload( + user, + (_b = consentRequest.requestedScope) !== null && _b !== void 0 ? _b : [], + input.userContext + ), + scopes: consentRequest.requestedScope || [], + userContext: input.userContext, + }); + const accessTokenPayload = this.buildAccessTokenPayload({ + user, + session: input.session, + defaultPayload: input.session.getAccessTokenPayload(input.userContext), + userContext: input.userContext, + scopes: consentRequest.requestedScope || [], + }); const consentRes = await this.acceptConsentRequest( Object.assign(Object.assign({}, input), { challenge: consentRequest.challenge, @@ -261,18 +260,19 @@ function getRecipeInterface(querier, _config, _appInfo) { grantScope: consentRequest.requestedScope, session: { id_token: idToken, + access_token: accessTokenPayload, }, }) ); return { redirectTo: consentRes.redirectTo, - setCookie: (_d = resp.headers.get("set-cookie")) !== null && _d !== void 0 ? _d : undefined, + setCookie: (_c = resp.headers.get("set-cookie")) !== null && _c !== void 0 ? _c : undefined, }; } } return { redirectTo, - setCookie: (_e = resp.headers.get("set-cookie")) !== null && _e !== void 0 ? _e : undefined, + setCookie: (_d = resp.headers.get("set-cookie")) !== null && _d !== void 0 ? _d : undefined, }; }, token: async function (input) { @@ -388,6 +388,15 @@ function getRecipeInterface(querier, _config, _appInfo) { }; } }, + buildAccessTokenPayload: async function (input) { + return input.defaultPayload; + }, + buildIdTokenPayload: async function (input) { + return input.defaultPayload; + }, + buildUserInfo: async function (input) { + return input.user.toJson(); // Proper impl + }, }; } exports.default = getRecipeInterface; diff --git a/lib/build/recipe/oauth2/types.d.ts b/lib/build/recipe/oauth2/types.d.ts index 6430b4d35..b95d8f50e 100644 --- a/lib/build/recipe/oauth2/types.d.ts +++ b/lib/build/recipe/oauth2/types.d.ts @@ -4,6 +4,7 @@ import OverrideableBuilder from "supertokens-js-override"; import { GeneralErrorResponse, JSONObject, NonNullableProperties, UserContext } from "../../types"; import { SessionContainerInterface } from "../session/types"; import { OAuth2Client } from "./OAuth2Client"; +import { User } from "../../user"; export declare type TypeInput = { override?: { functions?: ( @@ -205,6 +206,27 @@ export declare type RecipeInterface = { errorHint: string; } >; + buildAccessTokenPayload(input: { + user: User; + session: SessionContainerInterface; + scopes: string[]; + defaultPayload: JSONObject; + userContext: UserContext; + }): Promise; + buildIdTokenPayload(input: { + user: User; + session: SessionContainerInterface; + scopes: string[]; + defaultPayload: JSONObject; + userContext: UserContext; + }): Promise; + buildUserInfo(input: { + user: User; + accessTokenPayload: JSONObject; + scopes: string[]; + defaultInfo: JSONObject; + userContext: UserContext; + }): Promise; }; export declare type APIInterface = { loginGET: @@ -418,3 +440,8 @@ export declare type UpdateOAuth2ClientInput = NonNullableProperties< export declare type DeleteOAuth2ClientInput = { clientId: string; }; +export declare type PayloadBuilderFunction = ( + user: User, + scopes: string[], + userContext: UserContext +) => Promise; diff --git a/lib/ts/recipe/oauth2/recipe.ts b/lib/ts/recipe/oauth2/recipe.ts index 0f6b66d26..5aa8a3888 100644 --- a/lib/ts/recipe/oauth2/recipe.ts +++ b/lib/ts/recipe/oauth2/recipe.ts @@ -19,7 +19,7 @@ import type { BaseRequest, BaseResponse } from "../../framework"; import NormalisedURLPath from "../../normalisedURLPath"; import { Querier } from "../../querier"; import RecipeModule from "../../recipeModule"; -import { APIHandled, HTTPMethod, NormalisedAppinfo, RecipeListFunction, UserContext } from "../../types"; +import { APIHandled, HTTPMethod, JSONObject, NormalisedAppinfo, RecipeListFunction, UserContext } from "../../types"; import authGET from "./api/auth"; import consentAPI from "./api/consent"; import APIImplementation from "./api/implementation"; @@ -29,13 +29,15 @@ import tokenPOST from "./api/token"; import loginInfoGET from "./api/loginInfo"; import { AUTH_PATH, CONSENT_PATH, LOGIN_INFO_PATH, LOGIN_PATH, LOGOUT_PATH, TOKEN_PATH } from "./constants"; import RecipeImplementation from "./recipeImplementation"; -import { APIInterface, RecipeInterface, TypeInput, TypeNormalisedInput } from "./types"; +import { APIInterface, PayloadBuilderFunction, RecipeInterface, TypeInput, TypeNormalisedInput } from "./types"; import { validateAndNormaliseUserInput } from "./utils"; import OverrideableBuilder from "supertokens-js-override"; +import { User } from "../../user"; export default class Recipe extends RecipeModule { static RECIPE_ID = "oauth2"; private static instance: Recipe | undefined = undefined; + private idTokenBuilders: PayloadBuilderFunction[] = []; config: TypeNormalisedInput; recipeInterfaceImpl: RecipeInterface; @@ -49,7 +51,12 @@ export default class Recipe extends RecipeModule { { let builder = new OverrideableBuilder( - RecipeImplementation(Querier.getNewInstanceOrThrowError(recipeId), this.config, appInfo) + RecipeImplementation( + Querier.getNewInstanceOrThrowError(recipeId), + this.config, + appInfo, + this.getDefaultIdTokenPayload + ) ); this.recipeInterfaceImpl = builder.override(this.config.override.functions).build(); } @@ -200,4 +207,27 @@ export default class Recipe extends RecipeModule { isErrorFromThisRecipe(err: any): err is error { return SuperTokensError.isErrorFromSuperTokens(err) && err.fromRecipe === Recipe.RECIPE_ID; } + + async getDefaultIdTokenPayload(user: User, scopes: string[], userContext: UserContext) { + let payload: JSONObject = {}; + if (scopes.includes("email")) { + payload.email = user?.emails[0]; + payload.email_verified = user.loginMethods.some((lm) => lm.hasSameEmailAs(user?.emails[0]) && lm.verified); + } + if (scopes.includes("phoneNumber")) { + payload.phoneNumber = user?.phoneNumbers[0]; + payload.phoneNumber_verified = user.loginMethods.some( + (lm) => lm.hasSamePhoneNumberAs(user?.phoneNumbers[0]) && lm.verified + ); + } + + for (const fn of this.idTokenBuilders) { + payload = { + ...payload, + ...(await fn(user, scopes, userContext)), + }; + } + + return payload; + } } diff --git a/lib/ts/recipe/oauth2/recipeImplementation.ts b/lib/ts/recipe/oauth2/recipeImplementation.ts index 19371d370..0f5e62274 100644 --- a/lib/ts/recipe/oauth2/recipeImplementation.ts +++ b/lib/ts/recipe/oauth2/recipeImplementation.ts @@ -16,7 +16,14 @@ import NormalisedURLPath from "../../normalisedURLPath"; import { Querier, hydraPubDomain } from "../../querier"; import { NormalisedAppinfo } from "../../types"; -import { RecipeInterface, TypeNormalisedInput, ConsentRequest, LoginRequest, LogoutRequest } from "./types"; +import { + RecipeInterface, + TypeNormalisedInput, + ConsentRequest, + LoginRequest, + LogoutRequest, + PayloadBuilderFunction, +} from "./types"; import { toSnakeCase, transformObjectKeys } from "../../utils"; import { OAuth2Client } from "./OAuth2Client"; import { getUser } from "../.."; @@ -24,7 +31,8 @@ import { getUser } from "../.."; export default function getRecipeInterface( querier: Querier, _config: TypeNormalisedInput, - _appInfo: NormalisedAppinfo + _appInfo: NormalisedAppinfo, + getDefaultIdTokenPayload: PayloadBuilderFunction ): RecipeInterface { return { getLoginRequest: async function (this: RecipeInterface, input): Promise { @@ -244,19 +252,26 @@ export default function getRecipeInterface( throw new Error("Should not happen"); } if (consentRequest.skip || consentRequest.client?.skipConsent) { - const idToken: any = {}; - if (consentRequest.requestedScope?.includes("email")) { - idToken.email = user?.emails[0]; - idToken.email_verified = user.loginMethods.some( - (lm) => lm.hasSameEmailAs(idToken.email) && lm.verified - ); - } - if (consentRequest.requestedScope?.includes("phoneNumber")) { - idToken.phoneNumber = user?.phoneNumbers[0]; - idToken.phoneNumber_verified = user.loginMethods.some( - (lm) => lm.hasSamePhoneNumberAs(idToken.phoneNumber) && lm.verified - ); - } + const idToken = this.buildIdTokenPayload({ + user, + session: input.session, + defaultPayload: await getDefaultIdTokenPayload( + user, + consentRequest.requestedScope ?? [], + input.userContext + ), + scopes: consentRequest.requestedScope || [], + userContext: input.userContext, + }); + + const accessTokenPayload = this.buildAccessTokenPayload({ + user, + session: input.session, + defaultPayload: input.session.getAccessTokenPayload(input.userContext), // TODO: validate + userContext: input.userContext, + scopes: consentRequest.requestedScope || [], + }); + const consentRes = await this.acceptConsentRequest({ ...input, challenge: consentRequest.challenge, @@ -264,6 +279,7 @@ export default function getRecipeInterface( grantScope: consentRequest.requestedScope, session: { id_token: idToken, + access_token: accessTokenPayload, }, }); @@ -401,5 +417,14 @@ export default function getRecipeInterface( }; } }, + buildAccessTokenPayload: async function (input) { + return input.defaultPayload; + }, + buildIdTokenPayload: async function (input) { + return input.defaultPayload; + }, + buildUserInfo: async function (input) { + return input.user.toJson(); // Proper impl + }, }; } diff --git a/lib/ts/recipe/oauth2/types.ts b/lib/ts/recipe/oauth2/types.ts index 9e77e8679..f88435f43 100644 --- a/lib/ts/recipe/oauth2/types.ts +++ b/lib/ts/recipe/oauth2/types.ts @@ -18,6 +18,7 @@ import OverrideableBuilder from "supertokens-js-override"; import { GeneralErrorResponse, JSONObject, NonNullableProperties, UserContext } from "../../types"; import { SessionContainerInterface } from "../session/types"; import { OAuth2Client } from "./OAuth2Client"; +import { User } from "../../user"; export type TypeInput = { override?: { @@ -333,6 +334,28 @@ export type RecipeInterface = { errorHint: string; } >; + + buildAccessTokenPayload(input: { + user: User; + session: SessionContainerInterface; + scopes: string[]; + defaultPayload: JSONObject; + userContext: UserContext; + }): Promise; + buildIdTokenPayload(input: { + user: User; + session: SessionContainerInterface; + scopes: string[]; + defaultPayload: JSONObject; + userContext: UserContext; + }): Promise; + buildUserInfo(input: { + user: User; + accessTokenPayload: JSONObject; + scopes: string[]; + defaultInfo: JSONObject; + userContext: UserContext; + }): Promise; }; export type APIInterface = { @@ -414,6 +437,16 @@ export type APIInterface = { options: APIOptions; userContext: UserContext; }) => Promise<{ status: "OK"; info: LoginInfo } | GeneralErrorResponse>); + // userInfoGET: + // | undefined + // | ((input: { + // accessTokenPayload: JSONObject; // after validating the access token passed to the endpoint + // user: User; // ge + // scopes: string[]; + // defaultInfo: JSONObject; + // options: APIOptions; + // userContext: UserContext; + // }) => Promise<{ status: "OK"; info: LoginInfo } | GeneralErrorResponse>); }; export type OAuth2ClientOptions = { @@ -522,3 +555,5 @@ export type UpdateOAuth2ClientInput = NonNullableProperties< export type DeleteOAuth2ClientInput = { clientId: string; }; + +export type PayloadBuilderFunction = (user: User, scopes: string[], userContext: UserContext) => Promise; From dd818d40c980a21491d528762f271ff5bd1a4bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mih=C3=A1ly=20Lengyel?= Date: Tue, 9 Jul 2024 13:29:23 +0200 Subject: [PATCH 14/16] test: move the session object and claims to the BE sdk server (#879) --- test/test-server/src/index.ts | 1 + test/test-server/src/session.ts | 330 +++++++++++++++++++++++++++++++- test/test-server/src/utils.ts | 149 +++++++++++++- 3 files changed, 466 insertions(+), 14 deletions(-) diff --git a/test/test-server/src/index.ts b/test/test-server/src/index.ts index 7787e0d15..0eaa50a56 100644 --- a/test/test-server/src/index.ts +++ b/test/test-server/src/index.ts @@ -336,6 +336,7 @@ app.get("/test/featureflag", async (req, res, next) => { app.post("/test/resetoverrideparams", async (req, res, next) => { resetOverrideParams(); + resetOverrideLogs(); res.json({ ok: true }); }); diff --git a/test/test-server/src/session.ts b/test/test-server/src/session.ts index 838f13038..a6f6ac9c3 100644 --- a/test/test-server/src/session.ts +++ b/test/test-server/src/session.ts @@ -1,10 +1,11 @@ import { Router } from "express"; import Session from "../../../recipe/session"; import * as supertokens from "../../../lib/build"; -import { PrimitiveClaim } from "../../../lib/build/recipe/session/claims"; import SessionRecipe from "../../../lib/build/recipe/session/recipe"; import { logger } from "./logger"; import { getFunc } from "./testFunctionMapper"; +import { convertRequestSessionToSessionObject, deserializeClaim, deserializeValidator } from "./utils"; +import { logOverrideEvent } from "./overrideLogging"; const namespace = "com.supertokens:node-test-server:session"; const { logDebugMessage } = logger(namespace); @@ -110,10 +111,7 @@ const router = Router() .post("/fetchandsetclaim", async (req, res, next) => { try { logDebugMessage("Session.fetchAndSetClaim %j", req.body); - let claim = new PrimitiveClaim({ - key: req.body.claim.key, - fetchValue: getFunc(`${req.body.claim.fetchValue}`), - }); + let claim = deserializeClaim(req.body.claim); const response = await Session.fetchAndSetClaim(req.body.sessionHandle, claim, req.body.userContext); res.json(response); } catch (e) { @@ -147,6 +145,328 @@ const router = Router() } catch (e) { next(e); } + }) + .post("/sessionobject/revokesession", async (req, res, next) => { + logDebugMessage("Session.sessionobject.revokesession %j", req.body); + logOverrideEvent("sessionobject.revokesession", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.revokeSession(req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.revokesession", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.revokesession", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getsessiondatafromdatabase", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getsessiondatafromdatabase %j", req.body); + logOverrideEvent("sessionobject.getsessiondatafromdatabase", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getSessionDataFromDatabase(req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getsessiondatafromdatabase", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getsessiondatafromdatabase", "REJ", e); + next(e); + } + }) + .post("/sessionobject/updatesessiondataindatabase", async (req, res, next) => { + logDebugMessage("Session.sessionobject.updatesessiondataindatabase %j", req.body); + logOverrideEvent("sessionobject.updatesessiondataindatabase", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.updateSessionDataInDatabase(req.body.newSessionData, req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.updatesessiondataindatabase", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.updatesessiondataindatabase", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getuserid", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getuserid %j", req.body); + logOverrideEvent("sessionobject.getuserid", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getUserId(req.body.userContext); // : string; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getuserid", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getuserid", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getrecipeuserid", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getrecipeuserid %j", req.body); + logOverrideEvent("sessionobject.getrecipeuserid", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getRecipeUserId(req.body.userContext); // : RecipeUserId; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getrecipeuserid", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getrecipeuserid", "REJ", e); + next(e); + } + }) + .post("/sessionobject/gettenantid", async (req, res, next) => { + logDebugMessage("Session.sessionobject.gettenantid %j", req.body); + logOverrideEvent("sessionobject.gettenantid", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getTenantId(req.body.userContext); // : string; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.gettenantid", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.gettenantid", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getaccesstokenpayload", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getaccesstokenpayload %j", req.body); + logOverrideEvent("sessionobject.getaccesstokenpayload", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getAccessTokenPayload(req.body.userContext); // : any; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getaccesstokenpayload", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getaccesstokenpayload", "REJ", e); + next(e); + } + }) + .post("/sessionobject/gethandle", async (req, res, next) => { + logDebugMessage("Session.sessionobject.gethandle %j", req.body); + logOverrideEvent("sessionobject.gethandle", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getHandle(req.body.userContext); // : string; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.gethandle", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.gethandle", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getallsessiontokensdangerously", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getallsessiontokensdangerously %j", req.body); + logOverrideEvent("sessionobject.getallsessiontokensdangerously", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getAllSessionTokensDangerously(); // : Promise<{}>; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getallsessiontokensdangerously", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getallsessiontokensdangerously", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getaccesstoken", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getaccesstoken %j", req.body); + logOverrideEvent("sessionobject.getaccesstoken", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getAccessToken(req.body.userContext); // : string; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getaccesstoken", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getaccesstoken", "REJ", e); + next(e); + } + }) + .post("/sessionobject/mergeintoaccesstokenpayload", async (req, res, next) => { + logDebugMessage("Session.sessionobject.mergeintoaccesstokenpayload %j", req.body); + logOverrideEvent("sessionobject.mergeintoaccesstokenpayload", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.mergeIntoAccessTokenPayload( + req.body.accessTokenPayloadUpdate, + req.body.userContext + ); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.mergeintoaccesstokenpayload", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.mergeintoaccesstokenpayload", "REJ", e); + next(e); + } + }) + .post("/sessionobject/gettimecreated", async (req, res, next) => { + logDebugMessage("Session.sessionobject.gettimecreated %j", req.body); + logOverrideEvent("sessionobject.gettimecreated", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getTimeCreated(req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.gettimecreated", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.gettimecreated", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getexpiry", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getexpiry %j", req.body); + logOverrideEvent("sessionobject.getexpiry", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getExpiry(req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getexpiry", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getexpiry", "REJ", e); + next(e); + } + }) + .post("/sessionobject/assertclaims", async (req, res, next) => { + logDebugMessage("Session.sessionobject.assertclaims %j", req.body); + logOverrideEvent("sessionobject.assertclaims", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.assertClaims( + req.body.claimValidators.map(deserializeValidator), + req.body.userContext + ); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.assertclaims", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.assertclaims", "REJ", e); + next(e); + } + }) + .post("/sessionobject/fetchandsetclaim", async (req, res, next) => { + logDebugMessage("Session.sessionobject.fetchandsetclaim %j", req.body); + logOverrideEvent("sessionobject.fetchandsetclaim", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + + const retVal = await session.fetchAndSetClaim(deserializeClaim(req.body.claim), req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.fetchandsetclaim", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.fetchandsetclaim", "REJ", e); + next(e); + } + }) + .post("/sessionobject/setclaimvalue", async (req, res, next) => { + logDebugMessage("Session.sessionobject.setclaimvalue %j", req.body); + logOverrideEvent("sessionobject.setclaimvalue", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.setClaimValue( + deserializeClaim(req.body.claim), + req.body.value, + req.body.userContext + ); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.setclaimvalue", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.setclaimvalue", "REJ", e); + next(e); + } + }) + .post("/sessionobject/removeclaim", async (req, res, next) => { + logDebugMessage("Session.sessionobject.removeClaim %j", req.body); + logOverrideEvent("sessionobject.removeClaim", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + await session.removeClaim(deserializeClaim(req.body.claim), req.body.userContext); // : Promise; + res.json({ updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.removeClaim", "RES", undefined); + } catch (e) { + logOverrideEvent("sessionobject.removeClaim", "REJ", e); + next(e); + } + }) + .post("/sessionobject/getclaimvalue", async (req, res, next) => { + logDebugMessage("Session.sessionobject.getclaimvalue %j", req.body); + logOverrideEvent("sessionobject.getclaimvalue", "CALL", req.body); + try { + const session = await convertRequestSessionToSessionObject(req.body.session); + if (!session) { + throw new Error("This should never happen: failed to deserialize session"); + } + const retVal = await session.getClaimValue(deserializeClaim(req.body.claim), req.body.userContext); // : Promise; + res.json({ retVal, updatedSession: { ...session } }); + + logOverrideEvent("sessionobject.getclaimvalue", "RES", retVal); + } catch (e) { + logOverrideEvent("sessionobject.getclaimvalue", "REJ", e); + next(e); + } + }) + .post("/sessionobject/attachtorequestresponse", async (req, res, next) => { + logDebugMessage("Session.sessionobject.attachtorequestresponse %j", req.body); + logOverrideEvent("sessionobject.attachtorequestresponse", "CALL", req.body); + throw new Error("This should never happen: attachToRequestResponse called on remote-test session obj"); }); export default router; diff --git a/test/test-server/src/utils.ts b/test/test-server/src/utils.ts index 96c28f4e0..69ecb21ce 100644 --- a/test/test-server/src/utils.ts +++ b/test/test-server/src/utils.ts @@ -1,19 +1,140 @@ import Session from "../../../recipe/session"; import * as supertokens from "../../../lib/build"; +import { parseJWTWithoutSignatureVerification } from "../../../lib/build/recipe/session/jwt"; +import SessionClass from "../../../lib/build/recipe/session/sessionClass"; +import SessionRecipe from "../../../lib/build/recipe/session/recipe"; +import { SessionClaimValidator, TokenInfo } from "../../../lib/build/recipe/session/types"; +import { BooleanClaim, PrimitiveArrayClaim, PrimitiveClaim } from "../../../lib/build/recipe/session/claims"; +import { EmailVerificationClaim } from "../../../recipe/emailverification"; +import { MultiFactorAuthClaim } from "../../../recipe/multifactorauth"; +import { PermissionClaim, UserRoleClaim } from "../../../recipe/userroles"; +import { logOverrideEvent } from "./overrideLogging"; + +const testClaimSetups = { + "st-true": () => + new BooleanClaim({ + key: "st-true", + fetchValue: () => true, + }), + "st-undef": () => + new BooleanClaim({ + key: "st-undef", + fetchValue: () => undefined, + }), +}; + +// Add all built-in claims +for (const c of [EmailVerificationClaim, MultiFactorAuthClaim, UserRoleClaim, PermissionClaim]) { + testClaimSetups[c.key] = () => c; +} + +const mockClaimBuilder = ({ key, values }) => { + const claim = new PrimitiveClaim({ + key: key ?? "st-stub-primitive", + fetchValue: (userId, recipeUserId, tenantId, currentPayload, userContext) => { + logOverrideEvent(`claim-${key}.fetchValue`, "CALL", { + userId, + recipeUserId, + tenantId, + currentPayload, + userContext, + }); + + // Here we can't reuse popOrUseVal because the values are arrays themselves + const retVal = + userContext["st-stub-arr-value"] ?? + (values instanceof Array && values[0] instanceof Array ? values.pop() : values); + logOverrideEvent(`claim-${key}.fetchValue`, "RES", retVal); + + return retVal; + }, + }); + + return claim; +}; + +export function deserializeClaim(serializedClaim: { key: string; values: any }) { + if (serializedClaim.key.startsWith("st-stub-")) { + return mockClaimBuilder({ ...serializedClaim, key: serializedClaim.key.replace(/^st-stub-/, "") }); + } + return testClaimSetups[serializedClaim.key](serializedClaim); +} + +export function deserializeValidator( + serializedValidator: { key: string } & ( + | { validatorName: string; args: any[] } + | { id?: string; shouldRefetchRes: boolean | boolean[]; validateRes: any | any[] } + ) +): SessionClaimValidator { + const claim = testClaimSetups[serializedValidator.key](serializedValidator); + if ("validatorName" in serializedValidator) { + return claim.validators[serializedValidator.validatorName](...serializedValidator.args); + } + return { + id: serializedValidator.id ?? serializedValidator.key, + claim, + shouldRefetch: (payload, ctx) => { + logOverrideEvent(`${serializedValidator.key}-shouldRefetch`, "CALL", { payload, ctx }); + const retVal = + ctx[`${serializedValidator.key}-shouldRefetch-res`] ?? + popOrUseVal(serializedValidator.shouldRefetchRes); + logOverrideEvent(`${serializedValidator.key}-shouldRefetch`, "RES", { retVal }); + + return retVal; + }, + validate: (payload, ctx) => { + logOverrideEvent(`${serializedValidator.key}-validate`, "CALL", { payload, ctx }); + const retVal = + ctx[`${serializedValidator.key}-validate-res`] ?? popOrUseVal(serializedValidator.validateRes); + logOverrideEvent(`${serializedValidator.key}-validate`, "RES", { retVal }); + return retVal; + }, + }; +} export async function convertRequestSessionToSessionObject( - session: { [key: string]: any } | undefined + tokens: + | { + accessToken: string; + frontToken: string; + refreshToken: TokenInfo | undefined; + antiCsrfToken: string | undefined; + } + | undefined ): Promise { - if (session !== undefined) { - return await Session.getSessionWithoutRequestResponse( - session.accessToken, - session.userDataInAccessToken?.antiCsrfToken, - { - overrideGlobalClaimValidators: () => [], - } + if (tokens !== undefined) { + const helpers = { + config: SessionRecipe.getInstanceOrThrowError().config, + getRecipeImpl: () => SessionRecipe.getInstanceOrThrowError().recipeInterfaceImpl, + }; + + const jwtInfo = parseJWTWithoutSignatureVerification(tokens.accessToken); + const jwtPayload = jwtInfo.payload; + + let userId = jwtInfo.version === 2 ? jwtPayload.userId! : jwtPayload.sub!; + let sessionHandle = jwtPayload.sessionHandle!; + + let recipeUserId = new supertokens.RecipeUserId(jwtPayload.rsub ?? userId); + let antiCsrfToken = jwtPayload.antiCsrfToken; + let tenantId = jwtInfo.version >= 4 ? jwtPayload.tId! : "public"; + + const session = new SessionClass( + helpers as any, + tokens.accessToken, + tokens.frontToken, + tokens.refreshToken, + antiCsrfToken, + sessionHandle, + userId, + recipeUserId, + jwtPayload, + undefined, + false, + tenantId ); + return session; } - return session; + return tokens; } export function serializeUser(response) { @@ -35,3 +156,13 @@ export function serializeRecipeUserId(response) { : {}), }; } + +function popOrUseVal(arrOrValue: T | T[]): T { + if (arrOrValue instanceof Array) { + if (arrOrValue.length === 0) { + throw new Error("Ran out of values"); + } + return arrOrValue.pop()!; + } + return arrOrValue; +} From 090e29af65baf4995a5c141ac447d055f6080f1d Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 9 Jul 2024 22:56:29 +0530 Subject: [PATCH 15/16] fixes issue of refresh not clearing tokens --- CHANGELOG.md | 4 ++ .../recipe/session/sessionRequestFunctions.js | 4 +- lib/build/version.d.ts | 2 +- lib/build/version.js | 2 +- .../recipe/session/sessionRequestFunctions.ts | 4 +- lib/ts/version.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- test/auth-modes.test.js | 37 ++++++++++++------- 9 files changed, 37 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ca1cf00d..7a50aaa3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [18.0.2] - 2024-07-09 + +- `refreshPOST` and `refreshSession` now clears all user tokens upon CSRF failures and if no tokens are found. See the latest comment on https://github.com/supertokens/supertokens-node/issues/141 for more details. + ## [18.0.1] - 2024-06-19 ### Fixes diff --git a/lib/build/recipe/session/sessionRequestFunctions.js b/lib/build/recipe/session/sessionRequestFunctions.js index dac80fb8e..1d33897a7 100644 --- a/lib/build/recipe/session/sessionRequestFunctions.js +++ b/lib/build/recipe/session/sessionRequestFunctions.js @@ -257,7 +257,7 @@ async function refreshSessionInRequest({ res, req, userContext, config, recipeIn throw new error_1.default({ message: "Refresh token not found. Are you sending the refresh token in the request?", payload: { - clearTokens: false, + clearTokens: true, }, type: error_1.default.UNAUTHORISED, }); @@ -280,7 +280,7 @@ async function refreshSessionInRequest({ res, req, userContext, config, recipeIn message: "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.", type: error_1.default.UNAUTHORISED, payload: { - clearTokens: false, // see https://github.com/supertokens/supertokens-node/issues/141 + clearTokens: true, // see https://github.com/supertokens/supertokens-node/issues/141 }, }); } diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index eb88f6222..f474cb1c6 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1,4 +1,4 @@ // @ts-nocheck -export declare const version = "18.0.1"; +export declare const version = "18.0.2"; export declare const cdiSupported: string[]; export declare const dashboardVersion = "0.11"; diff --git a/lib/build/version.js b/lib/build/version.js index 9ac2936c1..6a18a166d 100644 --- a/lib/build/version.js +++ b/lib/build/version.js @@ -15,7 +15,7 @@ exports.dashboardVersion = exports.cdiSupported = exports.version = void 0; * License for the specific language governing permissions and limitations * under the License. */ -exports.version = "18.0.1"; +exports.version = "18.0.2"; exports.cdiSupported = ["5.0"]; // Note: The actual script import for dashboard uses v{DASHBOARD_VERSION} exports.dashboardVersion = "0.11"; diff --git a/lib/ts/recipe/session/sessionRequestFunctions.ts b/lib/ts/recipe/session/sessionRequestFunctions.ts index dbfe15051..de4c5b3bb 100644 --- a/lib/ts/recipe/session/sessionRequestFunctions.ts +++ b/lib/ts/recipe/session/sessionRequestFunctions.ts @@ -314,7 +314,7 @@ export async function refreshSessionInRequest({ throw new SessionError({ message: "Refresh token not found. Are you sending the refresh token in the request?", payload: { - clearTokens: false, + clearTokens: true, }, type: SessionError.UNAUTHORISED, }); @@ -338,7 +338,7 @@ export async function refreshSessionInRequest({ message: "anti-csrf check failed. Please pass 'rid: \"session\"' header in the request.", type: SessionError.UNAUTHORISED, payload: { - clearTokens: false, // see https://github.com/supertokens/supertokens-node/issues/141 + clearTokens: true, // see https://github.com/supertokens/supertokens-node/issues/141 }, }); } diff --git a/lib/ts/version.ts b/lib/ts/version.ts index fdbe1bd19..700fb6fdd 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,7 +12,7 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const version = "18.0.1"; +export const version = "18.0.2"; export const cdiSupported = ["5.0"]; diff --git a/package-lock.json b/package-lock.json index daf4f2610..a09dbd095 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-node", - "version": "18.0.1", + "version": "18.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-node", - "version": "18.0.1", + "version": "18.0.2", "license": "Apache-2.0", "dependencies": { "content-type": "^1.0.5", diff --git a/package.json b/package.json index 2aaf7792f..70a8e3d69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-node", - "version": "18.0.1", + "version": "18.0.2", "description": "NodeJS driver for SuperTokens core", "main": "index.js", "scripts": { diff --git a/test/auth-modes.test.js b/test/auth-modes.test.js index 067346d64..0ed01e580 100644 --- a/test/auth-modes.test.js +++ b/test/auth-modes.test.js @@ -893,15 +893,15 @@ describe(`auth-modes: ${printPath("[test/auth-modes.test.js]")}`, function () { describe("from behaviour table", () => { // prettier-ignore const behaviourTable = [ - { getTokenTransferMethodRes: "any", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "none" }, - { getTokenTransferMethodRes: "header", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "none" }, - { getTokenTransferMethodRes: "cookie", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "none" }, + { getTokenTransferMethodRes: "any", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "both" }, + { getTokenTransferMethodRes: "header", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "both" }, + { getTokenTransferMethodRes: "cookie", authHeader: false, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "both" }, { getTokenTransferMethodRes: "any", authHeader: false, authCookie: true, output: "validatecookie", setTokens: "cookies", clearedTokens: "none" }, - { getTokenTransferMethodRes: "header", authHeader: false, authCookie: true, output: "unauthorised", setTokens: "none", clearedTokens: "none" }, // 5 + { getTokenTransferMethodRes: "header", authHeader: false, authCookie: true, output: "unauthorised", setTokens: "none", clearedTokens: "both" }, // 5 { getTokenTransferMethodRes: "cookie", authHeader: false, authCookie: true, output: "validatecookie", setTokens: "cookies", clearedTokens: "none" }, { getTokenTransferMethodRes: "any", authHeader: true, authCookie: false, output: "validateheader", setTokens: "headers", clearedTokens: "none" }, { getTokenTransferMethodRes: "header", authHeader: true, authCookie: false, output: "validateheader", setTokens: "headers", clearedTokens: "none" }, - { getTokenTransferMethodRes: "cookie", authHeader: true, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "none" }, // 9 + { getTokenTransferMethodRes: "cookie", authHeader: true, authCookie: false, output: "unauthorised", setTokens: "none", clearedTokens: "both" }, // 9 { getTokenTransferMethodRes: "any", authHeader: true, authCookie: true, output: "validateheader", setTokens: "headers", clearedTokens: "cookies" }, { getTokenTransferMethodRes: "header", authHeader: true, authCookie: true, output: "validateheader", setTokens: "headers", clearedTokens: "cookies" }, { getTokenTransferMethodRes: "cookie", authHeader: true, authCookie: true, output: "validatecookie", setTokens: "cookies", clearedTokens: "headers" }, // 12 @@ -964,6 +964,13 @@ describe(`auth-modes: ${printPath("[test/auth-modes.test.js]")}`, function () { assert.strictEqual(refreshRes.accessTokenExpiry, "Thu, 01 Jan 1970 00:00:00 GMT"); assert.strictEqual(refreshRes.refreshToken, ""); assert.strictEqual(refreshRes.refreshTokenExpiry, "Thu, 01 Jan 1970 00:00:00 GMT"); + } else if (conf.clearedTokens === "both") { + assert.strictEqual(refreshRes.accessTokenFromHeader, ""); + assert.strictEqual(refreshRes.refreshTokenFromHeader, ""); + assert.strictEqual(refreshRes.accessToken, ""); + assert.strictEqual(refreshRes.accessTokenExpiry, "Thu, 01 Jan 1970 00:00:00 GMT"); + assert.strictEqual(refreshRes.refreshToken, ""); + assert.strictEqual(refreshRes.refreshTokenExpiry, "Thu, 01 Jan 1970 00:00:00 GMT"); } switch (conf.setTokens) { @@ -985,15 +992,17 @@ describe(`auth-modes: ${printPath("[test/auth-modes.test.js]")}`, function () { } break; } - if (conf.setTokens !== "cookies" && conf.clearedTokens !== "cookies") { - assert.strictEqual(refreshRes.accessToken, undefined); - assert.strictEqual(refreshRes.accessTokenExpiry, undefined); - assert.strictEqual(refreshRes.refreshToken, undefined); - assert.strictEqual(refreshRes.refreshTokenExpiry, undefined); - } - if (conf.setTokens !== "headers" && conf.clearedTokens !== "headers") { - assert.strictEqual(refreshRes.accessTokenFromHeader, undefined); - assert.strictEqual(refreshRes.refreshTokenFromHeader, undefined); + if (conf.clearedTokens !== "both") { + if (conf.setTokens !== "cookies" && conf.clearedTokens !== "cookies") { + assert.strictEqual(refreshRes.accessToken, undefined); + assert.strictEqual(refreshRes.accessTokenExpiry, undefined); + assert.strictEqual(refreshRes.refreshToken, undefined); + assert.strictEqual(refreshRes.refreshTokenExpiry, undefined); + } + if (conf.setTokens !== "headers" && conf.clearedTokens !== "headers") { + assert.strictEqual(refreshRes.accessTokenFromHeader, undefined); + assert.strictEqual(refreshRes.refreshTokenFromHeader, undefined); + } } }); } From cdfa03123ffc42a22eb7d8c327762b568e5c4b7a Mon Sep 17 00:00:00 2001 From: rishabhpoddar Date: Tue, 9 Jul 2024 22:57:04 +0530 Subject: [PATCH 16/16] adding dev-v18.0.2 tag to this commit to ensure building --- docs/classes/framework.BaseRequest.html | 2 +- docs/classes/framework.BaseResponse.html | 2 +- docs/classes/framework_custom.CollectingResponse.html | 2 +- docs/classes/framework_custom.PreParsedRequest.html | 2 +- docs/classes/index.RecipeUserId.html | 2 +- docs/classes/index.User.html | 2 +- docs/classes/index.default.html | 2 +- docs/classes/ingredients_emaildelivery.default.html | 2 +- docs/classes/ingredients_smsdelivery.default.html | 2 +- docs/classes/recipe_accountlinking.default.html | 6 +++--- docs/classes/recipe_dashboard.default.html | 2 +- docs/classes/recipe_emailpassword.default.html | 4 ++-- docs/classes/recipe_emailverification.default.html | 2 +- docs/classes/recipe_jwt.default.html | 2 +- docs/classes/recipe_multifactorauth.default.html | 2 +- docs/classes/recipe_multitenancy.default.html | 2 +- docs/classes/recipe_openid.default.html | 2 +- docs/classes/recipe_passwordless.default.html | 6 +++--- docs/classes/recipe_session.default.html | 4 ++-- docs/classes/recipe_thirdparty.default.html | 2 +- docs/classes/recipe_totp.default.html | 2 +- docs/classes/recipe_usermetadata.default.html | 2 +- docs/classes/recipe_userroles.default.html | 2 +- docs/interfaces/framework_awsLambda.SessionEvent.html | 2 +- docs/interfaces/framework_awsLambda.SessionEventV2.html | 2 +- docs/interfaces/framework_express.SessionRequest.html | 2 +- docs/interfaces/framework_hapi.SessionRequest.html | 2 +- docs/interfaces/framework_koa.SessionContext.html | 2 +- docs/interfaces/framework_loopback.SessionContext.html | 2 +- docs/interfaces/recipe_session.SessionContainer.html | 2 +- docs/interfaces/recipe_session.VerifySessionOptions.html | 2 +- docs/modules/framework.html | 2 +- docs/modules/framework_awsLambda.html | 2 +- docs/modules/framework_custom.html | 2 +- docs/modules/framework_express.html | 2 +- docs/modules/framework_fastify.html | 2 +- docs/modules/framework_hapi.html | 2 +- docs/modules/framework_koa.html | 2 +- docs/modules/framework_loopback.html | 2 +- docs/modules/index.html | 2 +- docs/modules/recipe_accountlinking.html | 2 +- docs/modules/recipe_dashboard.html | 2 +- docs/modules/recipe_emailpassword.html | 4 ++-- docs/modules/recipe_emailverification.html | 2 +- docs/modules/recipe_jwt.html | 2 +- docs/modules/recipe_multifactorauth.html | 2 +- docs/modules/recipe_multitenancy.html | 2 +- docs/modules/recipe_openid.html | 2 +- docs/modules/recipe_passwordless.html | 2 +- docs/modules/recipe_session.html | 8 ++++---- docs/modules/recipe_thirdparty.html | 2 +- docs/modules/recipe_totp.html | 2 +- docs/modules/recipe_usermetadata.html | 4 ++-- docs/modules/recipe_userroles.html | 2 +- 54 files changed, 65 insertions(+), 65 deletions(-) diff --git a/docs/classes/framework.BaseRequest.html b/docs/classes/framework.BaseRequest.html index 45471aed9..0909c657f 100644 --- a/docs/classes/framework.BaseRequest.html +++ b/docs/classes/framework.BaseRequest.html @@ -1 +1 @@ -BaseRequest | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class BaseRequest Abstract

Hierarchy

Index

Constructors

Properties

getCookieValue: ((key_: string) => undefined | string)

Type declaration

    • (key_: string): undefined | string
    • Parameters

      • key_: string

      Returns undefined | string

getHeaderValue: ((key: string) => undefined | string)

Type declaration

    • (key: string): undefined | string
    • Parameters

      • key: string

      Returns undefined | string

getKeyValueFromQuery: ((key: string) => undefined | string)

Type declaration

    • (key: string): undefined | string
    • Parameters

      • key: string

      Returns undefined | string

getMethod: (() => HTTPMethod)

Type declaration

    • (): HTTPMethod
    • Returns HTTPMethod

getOriginalURL: (() => string)

Type declaration

    • (): string
    • Returns string

original: any
parsedJSONBody: any
parsedUrlEncodedFormData: any
wrapperUsed: boolean

Methods

  • getFormData(): Promise<any>
  • getFormDataFromRequestBody(): Promise<any>
  • getJSONBody(): Promise<any>
  • getJSONFromRequestBody(): Promise<any>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Interface
  • Protected method
  • Private property

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +BaseRequest | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class BaseRequest Abstract

Hierarchy

Index

Constructors

Properties

getCookieValue: ((key_: string) => undefined | string)

Type declaration

    • (key_: string): undefined | string
    • Parameters

      • key_: string

      Returns undefined | string

getHeaderValue: ((key: string) => undefined | string)

Type declaration

    • (key: string): undefined | string
    • Parameters

      • key: string

      Returns undefined | string

getKeyValueFromQuery: ((key: string) => undefined | string)

Type declaration

    • (key: string): undefined | string
    • Parameters

      • key: string

      Returns undefined | string

getMethod: (() => HTTPMethod)

Type declaration

    • (): HTTPMethod
    • Returns HTTPMethod

getOriginalURL: (() => string)

Type declaration

    • (): string
    • Returns string

original: any
parsedJSONBody: any
parsedUrlEncodedFormData: any
wrapperUsed: boolean

Methods

  • getFormData(): Promise<any>
  • getFormDataFromRequestBody(): Promise<any>
  • getJSONBody(): Promise<any>
  • getJSONFromRequestBody(): Promise<any>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Interface
  • Protected method
  • Private property

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/framework.BaseResponse.html b/docs/classes/framework.BaseResponse.html index 63d3a613b..c493c2617 100644 --- a/docs/classes/framework.BaseResponse.html +++ b/docs/classes/framework.BaseResponse.html @@ -1 +1 @@ -BaseResponse | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class BaseResponse Abstract

Hierarchy

Index

Constructors

Properties

original: any
removeHeader: ((key: string) => void)

Type declaration

    • (key: string): void
    • Parameters

      • key: string

      Returns void

sendHTMLResponse: ((html: string) => void)

Type declaration

    • (html: string): void
    • Parameters

      • html: string

      Returns void

sendJSONResponse: ((content: any) => void)

Type declaration

    • (content: any): void
    • Parameters

      • content: any

      Returns void

setCookie: ((key: string, value: string, domain: undefined | string, secure: boolean, httpOnly: boolean, expires: number, path: string, sameSite: "strict" | "lax" | "none") => void)

Type declaration

    • (key: string, value: string, domain: undefined | string, secure: boolean, httpOnly: boolean, expires: number, path: string, sameSite: "strict" | "lax" | "none"): void
    • Parameters

      • key: string
      • value: string
      • domain: undefined | string
      • secure: boolean
      • httpOnly: boolean
      • expires: number
      • path: string
      • sameSite: "strict" | "lax" | "none"

      Returns void

setHeader: ((key: string, value: string, allowDuplicateKey: boolean) => void)

Type declaration

    • (key: string, value: string, allowDuplicateKey: boolean): void
    • Parameters

      • key: string
      • value: string
      • allowDuplicateKey: boolean

      Returns void

setStatusCode: ((statusCode: number) => void)

Type declaration

    • (statusCode: number): void
    • Parameters

      • statusCode: number

      Returns void

wrapperUsed: boolean

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +BaseResponse | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class BaseResponse Abstract

Hierarchy

Index

Constructors

Properties

original: any
removeHeader: ((key: string) => void)

Type declaration

    • (key: string): void
    • Parameters

      • key: string

      Returns void

sendHTMLResponse: ((html: string) => void)

Type declaration

    • (html: string): void
    • Parameters

      • html: string

      Returns void

sendJSONResponse: ((content: any) => void)

Type declaration

    • (content: any): void
    • Parameters

      • content: any

      Returns void

setCookie: ((key: string, value: string, domain: undefined | string, secure: boolean, httpOnly: boolean, expires: number, path: string, sameSite: "strict" | "lax" | "none") => void)

Type declaration

    • (key: string, value: string, domain: undefined | string, secure: boolean, httpOnly: boolean, expires: number, path: string, sameSite: "strict" | "lax" | "none"): void
    • Parameters

      • key: string
      • value: string
      • domain: undefined | string
      • secure: boolean
      • httpOnly: boolean
      • expires: number
      • path: string
      • sameSite: "strict" | "lax" | "none"

      Returns void

setHeader: ((key: string, value: string, allowDuplicateKey: boolean) => void)

Type declaration

    • (key: string, value: string, allowDuplicateKey: boolean): void
    • Parameters

      • key: string
      • value: string
      • allowDuplicateKey: boolean

      Returns void

setStatusCode: ((statusCode: number) => void)

Type declaration

    • (statusCode: number): void
    • Parameters

      • statusCode: number

      Returns void

wrapperUsed: boolean

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/framework_custom.CollectingResponse.html b/docs/classes/framework_custom.CollectingResponse.html index 4e0698cea..81010d1d6 100644 --- a/docs/classes/framework_custom.CollectingResponse.html +++ b/docs/classes/framework_custom.CollectingResponse.html @@ -1,2 +1,2 @@ -CollectingResponse | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

Index

Constructors

Properties

body?: string
cookies: CookieInfo[]
headers: Headers
original: any
statusCode: number
wrapperUsed: boolean

Methods

  • removeHeader(key: string): void
  • sendHTMLResponse(html: string): void
  • sendJSONResponse(content: any): void
  • setCookie(key: string, value: string, domain: undefined | string, secure: boolean, httpOnly: boolean, expires: number, path: string, sameSite: "strict" | "lax" | "none"): void
  • Parameters

    • key: string
    • value: string
    • domain: undefined | string
    • secure: boolean
    • httpOnly: boolean
    • expires: number
    • path: string
    • sameSite: "strict" | "lax" | "none"

    Returns void

  • setHeader(key: string, value: string, allowDuplicateKey: boolean): void
  • setStatusCode(statusCode: number): void
  • resetPasswordUsingToken(tenantId: string, token: string, newPassword: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
  • Parameters

    • tenantId: string
    • token: string
    • newPassword: string
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

  • sendEmail(input: TypeEmailPasswordPasswordResetEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
  • sendResetPasswordEmail(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>
  • Parameters

    • tenantId: string
    • userId: string
    • email: string
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>

  • signIn(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>
  • signIn(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
  • Parameters

    • tenantId: string
    • email: string
    • password: string
    • Optional session: undefined
    • Optional userContext: Record<string, any>

    Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>

  • Parameters

    • tenantId: string
    • email: string
    • password: string
    • session: SessionContainer
    • Optional userContext: Record<string, any>

    Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

  • signUp(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>
  • signUp(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
  • Parameters

    • tenantId: string
    • email: string
    • password: string
    • Optional session: undefined
    • Optional userContext: Record<string, any>

    Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>

  • Parameters

    • tenantId: string
    • email: string
    • password: string
    • session: SessionContainer
    • Optional userContext: Record<string, any>

    Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

  • updateEmailOrPassword(input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
  • Parameters

    • input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }
      • Optional applyPasswordPolicy?: boolean
      • Optional email?: string
      • Optional password?: string
      • recipeUserId: RecipeUserId
      • Optional tenantIdForPasswordPolicy?: string
      • Optional userContext?: Record<string, any>

    Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

  • verifyCredentials(tenantId: string, email: string, password: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>
  • Parameters

    • tenantId: string
    • email: string
    • password: string
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_emailverification.default.html b/docs/classes/recipe_emailverification.default.html index 948dcd52f..c03361803 100644 --- a/docs/classes/recipe_emailverification.default.html +++ b/docs/classes/recipe_emailverification.default.html @@ -1 +1 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

EmailVerificationClaim: EmailVerificationClaimClass = EmailVerificationClaim
Error: typeof default = SuperTokensError
init: ((config: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config: TypeInput): RecipeListFunction
    • Parameters

      • config: TypeInput

      Returns RecipeListFunction

Methods

  • createEmailVerificationLink(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • createEmailVerificationToken(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • isEmailVerified(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<boolean>
  • revokeEmailVerificationTokens(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
  • sendEmail(input: TypeEmailVerificationEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
  • sendEmailVerificationEmail(tenantId: string, userId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • unverifyEmail(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
  • verifyEmailUsingToken(tenantId: string, token: string, attemptAccountLinking?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

EmailVerificationClaim: EmailVerificationClaimClass = EmailVerificationClaim
Error: typeof default = SuperTokensError
init: ((config: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config: TypeInput): RecipeListFunction
    • Parameters

      • config: TypeInput

      Returns RecipeListFunction

Methods

  • createEmailVerificationLink(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • createEmailVerificationToken(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • isEmailVerified(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<boolean>
  • revokeEmailVerificationTokens(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
  • sendEmail(input: TypeEmailVerificationEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
  • sendEmailVerificationEmail(tenantId: string, userId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
  • unverifyEmail(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
  • verifyEmailUsingToken(tenantId: string, token: string, attemptAccountLinking?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_jwt.default.html b/docs/classes/recipe_jwt.default.html index ff9a0ce45..d1b7e548f 100644 --- a/docs/classes/recipe_jwt.default.html +++ b/docs/classes/recipe_jwt.default.html @@ -1 +1 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

Methods

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • createJWT(payload: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
  • Parameters

    • payload: any
    • Optional validitySeconds: number
    • Optional useStaticSigningKey: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

  • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

Methods

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • createJWT(payload: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
  • Parameters

    • payload: any
    • Optional validitySeconds: number
    • Optional useStaticSigningKey: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

  • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_multifactorauth.default.html b/docs/classes/recipe_multifactorauth.default.html index d02257e2a..29a9adcd0 100644 --- a/docs/classes/recipe_multifactorauth.default.html +++ b/docs/classes/recipe_multifactorauth.default.html @@ -1 +1 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

FactorIds: { EMAILPASSWORD: string; LINK_EMAIL: string; LINK_PHONE: string; OTP_EMAIL: string; OTP_PHONE: string; THIRDPARTY: string; TOTP: string } = FactorIds

Type declaration

  • EMAILPASSWORD: string
  • LINK_EMAIL: string
  • LINK_PHONE: string
  • OTP_EMAIL: string
  • OTP_PHONE: string
  • THIRDPARTY: string
  • TOTP: string
MultiFactorAuthClaim: MultiFactorAuthClaimClass = MultiFactorAuthClaim
init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • addToRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>
  • assertAllowedToSetupFactorElseThrowInvalidClaimError(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
  • getFactorsSetupForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
  • getMFARequirementsForAuth(session: SessionContainer, userContext?: Record<string, any>): Promise<MFARequirementList>
  • getRequiredSecondaryFactorsForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
  • markFactorAsCompleteInSession(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
  • removeFromRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

FactorIds: { EMAILPASSWORD: string; LINK_EMAIL: string; LINK_PHONE: string; OTP_EMAIL: string; OTP_PHONE: string; THIRDPARTY: string; TOTP: string } = FactorIds

Type declaration

  • EMAILPASSWORD: string
  • LINK_EMAIL: string
  • LINK_PHONE: string
  • OTP_EMAIL: string
  • OTP_PHONE: string
  • THIRDPARTY: string
  • TOTP: string
MultiFactorAuthClaim: MultiFactorAuthClaimClass = MultiFactorAuthClaim
init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • addToRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>
  • assertAllowedToSetupFactorElseThrowInvalidClaimError(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
  • getFactorsSetupForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
  • getMFARequirementsForAuth(session: SessionContainer, userContext?: Record<string, any>): Promise<MFARequirementList>
  • getRequiredSecondaryFactorsForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
  • markFactorAsCompleteInSession(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
  • removeFromRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_multitenancy.default.html b/docs/classes/recipe_multitenancy.default.html index e15676cd8..bd863959a 100644 --- a/docs/classes/recipe_multitenancy.default.html +++ b/docs/classes/recipe_multitenancy.default.html @@ -1 +1 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • associateUserToTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
  • Parameters

    • tenantId: string
    • recipeUserId: RecipeUserId
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

  • createOrUpdateTenant(tenantId: string, config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • Optional config: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
      • Optional coreConfig?: {}
        • [key: string]: any
      • Optional emailPasswordEnabled?: boolean
      • Optional firstFactors?: string[]
      • Optional passwordlessEnabled?: boolean
      • Optional requiredSecondaryFactors?: string[]
      • Optional thirdPartyEnabled?: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ createdNew: boolean; status: "OK" }>

  • createOrUpdateThirdPartyConfig(tenantId: string, config: ProviderConfig, skipValidation?: boolean, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • config: ProviderConfig
    • Optional skipValidation: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ createdNew: boolean; status: "OK" }>

  • deleteTenant(tenantId: string, userContext?: Record<string, any>): Promise<{ didExist: boolean; status: "OK" }>
  • deleteThirdPartyConfig(tenantId: string, thirdPartyId: string, userContext?: Record<string, any>): Promise<{ didConfigExist: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • thirdPartyId: string
    • Optional userContext: Record<string, any>

    Returns Promise<{ didConfigExist: boolean; status: "OK" }>

  • disassociateUserFromTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAssociated: boolean }>
  • getTenant(tenantId: string, userContext?: Record<string, any>): Promise<undefined | { status: "OK" } & TenantConfig>
  • listAllTenants(userContext?: Record<string, any>): Promise<{ status: "OK"; tenants: ({ tenantId: string } & TenantConfig)[] }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • associateUserToTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
  • Parameters

    • tenantId: string
    • recipeUserId: RecipeUserId
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

  • createOrUpdateTenant(tenantId: string, config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • Optional config: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
      • Optional coreConfig?: {}
        • [key: string]: any
      • Optional emailPasswordEnabled?: boolean
      • Optional firstFactors?: string[]
      • Optional passwordlessEnabled?: boolean
      • Optional requiredSecondaryFactors?: string[]
      • Optional thirdPartyEnabled?: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ createdNew: boolean; status: "OK" }>

  • createOrUpdateThirdPartyConfig(tenantId: string, config: ProviderConfig, skipValidation?: boolean, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • config: ProviderConfig
    • Optional skipValidation: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ createdNew: boolean; status: "OK" }>

  • deleteTenant(tenantId: string, userContext?: Record<string, any>): Promise<{ didExist: boolean; status: "OK" }>
  • deleteThirdPartyConfig(tenantId: string, thirdPartyId: string, userContext?: Record<string, any>): Promise<{ didConfigExist: boolean; status: "OK" }>
  • Parameters

    • tenantId: string
    • thirdPartyId: string
    • Optional userContext: Record<string, any>

    Returns Promise<{ didConfigExist: boolean; status: "OK" }>

  • disassociateUserFromTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAssociated: boolean }>
  • getTenant(tenantId: string, userContext?: Record<string, any>): Promise<undefined | { status: "OK" } & TenantConfig>
  • listAllTenants(userContext?: Record<string, any>): Promise<{ status: "OK"; tenants: ({ tenantId: string } & TenantConfig)[] }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_openid.default.html b/docs/classes/recipe_openid.default.html index 52eba9a25..43eba9587 100644 --- a/docs/classes/recipe_openid.default.html +++ b/docs/classes/recipe_openid.default.html @@ -1 +1 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = OpenIdRecipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
  • Parameters

    • Optional payload: any
    • Optional validitySeconds: number
    • Optional useStaticSigningKey: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

  • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
  • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
  • Parameters

    • Optional userContext: Record<string, any>

    Returns Promise<{ issuer: string; jwks_uri: string; status: "OK" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

init: ((config?: TypeInput) => RecipeListFunction) = OpenIdRecipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
  • Parameters

    • Optional payload: any
    • Optional validitySeconds: number
    • Optional useStaticSigningKey: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

  • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
  • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
  • Parameters

    • Optional userContext: Record<string, any>

    Returns Promise<{ issuer: string; jwks_uri: string; status: "OK" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_passwordless.default.html b/docs/classes/recipe_passwordless.default.html index c7f4dcbdf..9fa20c95f 100644 --- a/docs/classes/recipe_passwordless.default.html +++ b/docs/classes/recipe_passwordless.default.html @@ -1,14 +1,14 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

Error: typeof default = SuperTokensError
init: ((config: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config: TypeInput): RecipeListFunction
    • Parameters

      • config: TypeInput

      Returns RecipeListFunction

Methods

  • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
  • +default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    Error: typeof default = SuperTokensError
    init: ((config: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config: TypeInput): RecipeListFunction
      • Parameters

        • config: TypeInput

        Returns RecipeListFunction

    Methods

    • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • This function will only verify the code (not consume it), and: NOT create a new user if it doesn't exist NOT verify the user email if it exists NOT do any linking NOT delete the code unless it returned RESTART_FLOW_ERROR

      -

      Parameters

      • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • +

      Parameters

      • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      1. verifies the code
      2. creates the user if it doesn't exist
      3. tries to link it
      4. marks the email as verified
      -

      Parameters

      • input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • createCode(input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
    • Parameters

      • input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

    • createMagicLink(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<string>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<string>

    • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>
        • Optional userInputCode?: string

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

    • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • listCodesByEmail(input: { email: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> }
        • email: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }
        • phoneNumber: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }
        • preAuthSessionId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • revokeAllCodes(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • revokeCode(input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • sendEmail(input: TypePasswordlessEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendSms(input: TypePasswordlessSmsDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • signInUp(input: { email: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: string; user: User }>
    • updateUser(input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
    • Parameters

      • input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }
        • Optional email?: null | string
        • Optional phoneNumber?: null | string
        • recipeUserId: RecipeUserId
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +

    Parameters

    • input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }

    Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

  • Parameters

    • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }

    Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

  • createCode(input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
  • Parameters

    • input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }

    Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

  • createMagicLink(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<string>
  • Parameters

    • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

    Returns Promise<string>

  • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
  • Parameters

    • input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }
      • deviceId: string
      • tenantId: string
      • Optional userContext?: Record<string, any>
      • Optional userInputCode?: string

    Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

  • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
  • Parameters

    • input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }
      • deviceId: string
      • tenantId: string
      • Optional userContext?: Record<string, any>

    Returns Promise<undefined | DeviceType>

  • listCodesByEmail(input: { email: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
  • Parameters

    • input: { email: string; tenantId: string; userContext?: Record<string, any> }
      • email: string
      • tenantId: string
      • Optional userContext?: Record<string, any>

    Returns Promise<DeviceType[]>

  • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
  • Parameters

    • input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }
      • phoneNumber: string
      • tenantId: string
      • Optional userContext?: Record<string, any>

    Returns Promise<DeviceType[]>

  • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
  • Parameters

    • input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }
      • preAuthSessionId: string
      • tenantId: string
      • Optional userContext?: Record<string, any>

    Returns Promise<undefined | DeviceType>

  • revokeAllCodes(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
  • Parameters

    • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

    Returns Promise<{ status: "OK" }>

  • revokeCode(input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
  • Parameters

    • input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

    Returns Promise<{ status: "OK" }>

  • sendEmail(input: TypePasswordlessEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
  • sendSms(input: TypePasswordlessSmsDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
  • signInUp(input: { email: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: string; user: User }>
  • updateUser(input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
  • Parameters

    • input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }
      • Optional email?: null | string
      • Optional phoneNumber?: null | string
      • recipeUserId: RecipeUserId
      • Optional userContext?: Record<string, any>

    Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/recipe_session.default.html b/docs/classes/recipe_session.default.html index af38bef04..1f84390f3 100644 --- a/docs/classes/recipe_session.default.html +++ b/docs/classes/recipe_session.default.html @@ -1,4 +1,4 @@ -default | supertokens-node
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

Error: typeof default = SuperTokensError
init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

Type declaration

    • (config?: TypeInput): RecipeListFunction
    • Parameters

      • Optional config: TypeInput

      Returns RecipeListFunction

Methods

  • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
  • Parameters

    • Optional payload: any
    • Optional validitySeconds: number
    • Optional useStaticSigningKey: boolean
    • Optional userContext: Record<string, any>

    Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

  • createNewSession(req: any, res: any, tenantId: string, recipeUserId: RecipeUserId, accessTokenPayload?: any, sessionDataInDatabase?: any, userContext?: Record<string, any>): Promise<SessionContainer>
  • createNewSessionWithoutRequestResponse(tenantId: string, recipeUserId: RecipeUserId, accessTokenPayload?: any, sessionDataInDatabase?: any, disableAntiCsrf?: boolean, userContext?: Record<string, any>): Promise<SessionContainer>
  • fetchAndSetClaim(sessionHandle: string, claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<boolean>
  • getAllSessionHandlesForUser(userId: string, fetchSessionsForAllLinkedAccounts?: boolean, tenantId?: string, userContext?: Record<string, any>): Promise<string[]>
  • Parameters

    • userId: string
    • fetchSessionsForAllLinkedAccounts: boolean = true
    • Optional tenantId: string
    • Optional userContext: Record<string, any>

    Returns Promise<string[]>

  • getClaimValue<T>(sessionHandle: string, claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<{ status: "SESSION_DOES_NOT_EXIST_ERROR" } | { status: "OK"; value: undefined | T }>
  • Type Parameters

    • T

    Parameters

    • sessionHandle: string
    • claim: SessionClaim<T>
    • Optional userContext: Record<string, any>

    Returns Promise<{ status: "SESSION_DOES_NOT_EXIST_ERROR" } | { status: "OK"; value: undefined | T }>

  • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[] }>
  • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
  • getSessionInformation(sessionHandle: string, userContext?: Record<string, any>): Promise<undefined | SessionInformation>
  • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string): Promise<SessionContainer>
  • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions & { sessionRequired?: true }, userContext?: Record<string, any>): Promise<SessionContainer>
  • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions & { sessionRequired: false }, userContext?: Record<string, any>): Promise<undefined | SessionContainer>
  • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions, userContext?: Record<string, any>): Promise<undefined | SessionContainer>
  • mergeIntoAccessTokenPayload(sessionHandle: string, accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<boolean>
  • refreshSession(req: any, res: any, userContext?: Record<string, any>): Promise<SessionContainer>
  • refreshSessionWithoutRequestResponse(refreshToken: string, disableAntiCsrf?: boolean, antiCsrfToken?: string, userContext?: Record<string, any>): Promise<SessionContainer>
  • removeClaim(sessionHandle: string, claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<boolean>
  • revokeAllSessionsForUser(userId: string, revokeSessionsForLinkedAccounts?: boolean, tenantId?: string, userContext?: Record<string, any>): Promise<string[]>
  • Parameters

    • userId: string
    • revokeSessionsForLinkedAccounts: boolean = true
    • Optional tenantId: string
    • Optional userContext: Record<string, any>

    Returns Promise<string[]>

  • revokeMultipleSessions(sessionHandles: string[], userContext?: Record<string, any>): Promise<string[]>
  • revokeSession(sessionHandle: string, userContext?: Record<string, any>): Promise<boolean>
  • setClaimValue<T>(sessionHandle: string, claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<boolean>
  • Type Parameters

    • T

    Parameters

    • sessionHandle: string
    • claim: SessionClaim<T>
    • value: T
    • Optional userContext: Record<string, any>

    Returns Promise<boolean>

  • updateSessionDataInDatabase(sessionHandle: string, newSessionData: any, userContext?: Record<string, any>): Promise<boolean>

Legend

  • Variable
  • Function
  • Function with type parameter
  • Type alias
  • Type alias with type parameter
  • Class
  • Class with type parameter
  • Constructor
  • Static property
  • Static method
  • Interface

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +

Returns Promise<SessionContainer>

  • Parameters

    • accessToken: string
    • Optional antiCsrfToken: string
    • Optional options: VerifySessionOptions & { sessionRequired?: true }
    • Optional userContext: Record<string, any>

    Returns Promise<SessionContainer>

  • Parameters

    • accessToken: string
    • Optional antiCsrfToken: string
    • Optional options: VerifySessionOptions & { sessionRequired: false }
    • Optional userContext: Record<string, any>

    Returns Promise<undefined | SessionContainer>

  • Parameters

    • accessToken: string
    • Optional antiCsrfToken: string
    • Optional options: VerifySessionOptions
    • Optional userContext: Record<string, any>

    Returns Promise<undefined | SessionContainer>

    • mergeIntoAccessTokenPayload(sessionHandle: string, accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<boolean>
    • refreshSession(req: any, res: any, userContext?: Record<string, any>): Promise<SessionContainer>
    • refreshSessionWithoutRequestResponse(refreshToken: string, disableAntiCsrf?: boolean, antiCsrfToken?: string, userContext?: Record<string, any>): Promise<SessionContainer>
    • removeClaim(sessionHandle: string, claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<boolean>
    • revokeAllSessionsForUser(userId: string, revokeSessionsForLinkedAccounts?: boolean, tenantId?: string, userContext?: Record<string, any>): Promise<string[]>
    • Parameters

      • userId: string
      • revokeSessionsForLinkedAccounts: boolean = true
      • Optional tenantId: string
      • Optional userContext: Record<string, any>

      Returns Promise<string[]>

    • revokeMultipleSessions(sessionHandles: string[], userContext?: Record<string, any>): Promise<string[]>
    • revokeSession(sessionHandle: string, userContext?: Record<string, any>): Promise<boolean>
    • setClaimValue<T>(sessionHandle: string, claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<boolean>
    • Type Parameters

      • T

      Parameters

      • sessionHandle: string
      • claim: SessionClaim<T>
      • value: T
      • Optional userContext: Record<string, any>

      Returns Promise<boolean>

    • updateSessionDataInDatabase(sessionHandle: string, newSessionData: any, userContext?: Record<string, any>): Promise<boolean>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/classes/recipe_thirdparty.default.html b/docs/classes/recipe_thirdparty.default.html index c8b489b0c..24cb78832 100644 --- a/docs/classes/recipe_thirdparty.default.html +++ b/docs/classes/recipe_thirdparty.default.html @@ -1 +1 @@ -default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    Error: typeof default = SuperTokensError
    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • getProvider(tenantId: string, thirdPartyId: string, clientType: undefined | string, userContext?: Record<string, any>): Promise<undefined | TypeProvider>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session?: undefined, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session: SessionContainer, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    Error: typeof default = SuperTokensError
    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • getProvider(tenantId: string, thirdPartyId: string, clientType: undefined | string, userContext?: Record<string, any>): Promise<undefined | TypeProvider>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session?: undefined, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session: SessionContainer, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/classes/recipe_totp.default.html b/docs/classes/recipe_totp.default.html index f3a25113f..e703f5a49 100644 --- a/docs/classes/recipe_totp.default.html +++ b/docs/classes/recipe_totp.default.html @@ -1 +1 @@ -default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • createDevice(userId: string, userIdentifierInfo?: string, deviceName?: string, skew?: number, period?: number, userContext?: Record<string, any>): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • userId: string
      • Optional userIdentifierInfo: string
      • Optional deviceName: string
      • Optional skew: number
      • Optional period: number
      • Optional userContext: Record<string, any>

      Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • listDevices(userId: string, userContext?: Record<string, any>): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice(userId: string, deviceName: string, userContext?: Record<string, any>): Promise<{ didDeviceExist: boolean; status: "OK" }>
    • Parameters

      • userId: string
      • deviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice(userId: string, existingDeviceName: string, newDeviceName: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>
    • Parameters

      • userId: string
      • existingDeviceName: string
      • newDeviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>

    • verifyDevice(tenantId: string, userId: string, deviceName: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • deviceName: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP(tenantId: string, userId: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • createDevice(userId: string, userIdentifierInfo?: string, deviceName?: string, skew?: number, period?: number, userContext?: Record<string, any>): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • userId: string
      • Optional userIdentifierInfo: string
      • Optional deviceName: string
      • Optional skew: number
      • Optional period: number
      • Optional userContext: Record<string, any>

      Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • listDevices(userId: string, userContext?: Record<string, any>): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice(userId: string, deviceName: string, userContext?: Record<string, any>): Promise<{ didDeviceExist: boolean; status: "OK" }>
    • Parameters

      • userId: string
      • deviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice(userId: string, existingDeviceName: string, newDeviceName: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>
    • Parameters

      • userId: string
      • existingDeviceName: string
      • newDeviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>

    • verifyDevice(tenantId: string, userId: string, deviceName: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • deviceName: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP(tenantId: string, userId: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/classes/recipe_usermetadata.default.html b/docs/classes/recipe_usermetadata.default.html index d0238478b..63b097c7f 100644 --- a/docs/classes/recipe_usermetadata.default.html +++ b/docs/classes/recipe_usermetadata.default.html @@ -1 +1 @@ -default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • clearUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • getUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ metadata: any; status: "OK" }>
    • updateUserMetadata(userId: string, metadataUpdate: JSONObject, userContext?: Record<string, any>): Promise<{ metadata: JSONObject; status: "OK" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • clearUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • getUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ metadata: any; status: "OK" }>
    • updateUserMetadata(userId: string, metadataUpdate: JSONObject, userContext?: Record<string, any>): Promise<{ metadata: JSONObject; status: "OK" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/classes/recipe_userroles.default.html b/docs/classes/recipe_userroles.default.html index 09ab46ff1..b77d05901 100644 --- a/docs/classes/recipe_userroles.default.html +++ b/docs/classes/recipe_userroles.default.html @@ -1 +1 @@ -default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    PermissionClaim: PermissionClaimClass = PermissionClaim
    UserRoleClaim: UserRoleClaimClass = UserRoleClaim
    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • addRoleToUser(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ createdNewRole: boolean; status: "OK" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole(role: string, userContext?: Record<string, any>): Promise<{ didRoleExist: boolean; status: "OK" }>
    • getAllRoles(userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole(role: string, userContext?: Record<string, any>): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser(tenantId: string, userId: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • Parameters

      • tenantId: string
      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ roles: string[]; status: "OK" }>

    • getRolesThatHavePermission(permission: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getUsersThatHaveRole(tenantId: string, role: string, userContext?: Record<string, any>): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • removePermissionsFromRole(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +default | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • default

    Index

    Constructors

    Properties

    PermissionClaim: PermissionClaimClass = PermissionClaim
    UserRoleClaim: UserRoleClaimClass = UserRoleClaim
    init: ((config?: TypeInput) => RecipeListFunction) = Recipe.init

    Type declaration

      • (config?: TypeInput): RecipeListFunction
      • Parameters

        • Optional config: TypeInput

        Returns RecipeListFunction

    Methods

    • addRoleToUser(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ createdNewRole: boolean; status: "OK" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole(role: string, userContext?: Record<string, any>): Promise<{ didRoleExist: boolean; status: "OK" }>
    • getAllRoles(userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole(role: string, userContext?: Record<string, any>): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser(tenantId: string, userId: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • Parameters

      • tenantId: string
      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ roles: string[]; status: "OK" }>

    • getRolesThatHavePermission(permission: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getUsersThatHaveRole(tenantId: string, role: string, userContext?: Record<string, any>): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • removePermissionsFromRole(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Constructor
    • Static property
    • Static method
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/interfaces/framework_awsLambda.SessionEvent.html b/docs/interfaces/framework_awsLambda.SessionEvent.html index 717f87471..d5723e13b 100644 --- a/docs/interfaces/framework_awsLambda.SessionEvent.html +++ b/docs/interfaces/framework_awsLambda.SessionEvent.html @@ -1 +1 @@ -SessionEvent | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SupertokensLambdaEvent
      • SessionEvent

    Index

    Properties

    body: null | string
    headers: APIGatewayProxyEventHeaders
    httpMethod: string
    isBase64Encoded: boolean
    multiValueHeaders: APIGatewayProxyEventMultiValueHeaders
    multiValueQueryStringParameters: null | APIGatewayProxyEventMultiValueQueryStringParameters
    path: string
    pathParameters: null | APIGatewayProxyEventPathParameters
    queryStringParameters: null | APIGatewayProxyEventQueryStringParameters
    requestContext: APIGatewayEventRequestContextWithAuthorizer<APIGatewayEventDefaultAuthorizerContext>
    resource: string
    stageVariables: null | APIGatewayProxyEventStageVariables
    supertokens: { response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] } }

    Type declaration

    • response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] }
      • cookies: string[]
      • headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[]

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +SessionEvent | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SupertokensLambdaEvent
      • SessionEvent

    Index

    Properties

    body: null | string
    headers: APIGatewayProxyEventHeaders
    httpMethod: string
    isBase64Encoded: boolean
    multiValueHeaders: APIGatewayProxyEventMultiValueHeaders
    multiValueQueryStringParameters: null | APIGatewayProxyEventMultiValueQueryStringParameters
    path: string
    pathParameters: null | APIGatewayProxyEventPathParameters
    queryStringParameters: null | APIGatewayProxyEventQueryStringParameters
    requestContext: APIGatewayEventRequestContextWithAuthorizer<APIGatewayEventDefaultAuthorizerContext>
    resource: string
    stageVariables: null | APIGatewayProxyEventStageVariables
    supertokens: { response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] } }

    Type declaration

    • response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] }
      • cookies: string[]
      • headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[]

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/interfaces/framework_awsLambda.SessionEventV2.html b/docs/interfaces/framework_awsLambda.SessionEventV2.html index fd65728df..60337b237 100644 --- a/docs/interfaces/framework_awsLambda.SessionEventV2.html +++ b/docs/interfaces/framework_awsLambda.SessionEventV2.html @@ -1 +1 @@ -SessionEventV2 | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SupertokensLambdaEventV2
      • SessionEventV2

    Index

    Properties

    body?: string
    cookies?: string[]
    headers: APIGatewayProxyEventHeaders
    isBase64Encoded: boolean
    pathParameters?: APIGatewayProxyEventPathParameters
    queryStringParameters?: APIGatewayProxyEventQueryStringParameters
    rawPath: string
    rawQueryString: string
    requestContext: { accountId: string; apiId: string; authorizer?: { jwt: { claims: {}; scopes: string[] } }; domainName: string; domainPrefix: string; http: { method: string; path: string; protocol: string; sourceIp: string; userAgent: string }; requestId: string; routeKey: string; stage: string; time: string; timeEpoch: number }

    Type declaration

    • accountId: string
    • apiId: string
    • Optional authorizer?: { jwt: { claims: {}; scopes: string[] } }
      • jwt: { claims: {}; scopes: string[] }
        • claims: {}
          • [name: string]: string | number | boolean | string[]
        • scopes: string[]
    • domainName: string
    • domainPrefix: string
    • http: { method: string; path: string; protocol: string; sourceIp: string; userAgent: string }
      • method: string
      • path: string
      • protocol: string
      • sourceIp: string
      • userAgent: string
    • requestId: string
    • routeKey: string
    • stage: string
    • time: string
    • timeEpoch: number
    routeKey: string
    stageVariables?: APIGatewayProxyEventStageVariables
    supertokens: { response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] } }

    Type declaration

    • response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] }
      • cookies: string[]
      • headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[]
    version: string

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +SessionEventV2 | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SupertokensLambdaEventV2
      • SessionEventV2

    Index

    Properties

    body?: string
    cookies?: string[]
    headers: APIGatewayProxyEventHeaders
    isBase64Encoded: boolean
    pathParameters?: APIGatewayProxyEventPathParameters
    queryStringParameters?: APIGatewayProxyEventQueryStringParameters
    rawPath: string
    rawQueryString: string
    requestContext: { accountId: string; apiId: string; authorizer?: { jwt: { claims: {}; scopes: string[] } }; domainName: string; domainPrefix: string; http: { method: string; path: string; protocol: string; sourceIp: string; userAgent: string }; requestId: string; routeKey: string; stage: string; time: string; timeEpoch: number }

    Type declaration

    • accountId: string
    • apiId: string
    • Optional authorizer?: { jwt: { claims: {}; scopes: string[] } }
      • jwt: { claims: {}; scopes: string[] }
        • claims: {}
          • [name: string]: string | number | boolean | string[]
        • scopes: string[]
    • domainName: string
    • domainPrefix: string
    • http: { method: string; path: string; protocol: string; sourceIp: string; userAgent: string }
      • method: string
      • path: string
      • protocol: string
      • sourceIp: string
      • userAgent: string
    • requestId: string
    • routeKey: string
    • stage: string
    • time: string
    • timeEpoch: number
    routeKey: string
    stageVariables?: APIGatewayProxyEventStageVariables
    supertokens: { response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] } }

    Type declaration

    • response: { cookies: string[]; headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[] }
      • cookies: string[]
      • headers: { allowDuplicateKey: boolean; key: string; value: string | number | boolean }[]
    version: string

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/interfaces/framework_express.SessionRequest.html b/docs/interfaces/framework_express.SessionRequest.html index d2749a739..a86db681c 100644 --- a/docs/interfaces/framework_express.SessionRequest.html +++ b/docs/interfaces/framework_express.SessionRequest.html @@ -135,7 +135,7 @@
    route: any
    secure: boolean

    Short-hand for:

    req.protocol == 'https'

    -
    signedCookies: any
    socket: Socket
    +
    signedCookies: any
    socket: Socket

    The net.Socket object associated with the connection.

    With HTTPS support, use request.socket.getPeerCertificate() to obtain the client's authentication details.

    diff --git a/docs/interfaces/framework_hapi.SessionRequest.html b/docs/interfaces/framework_hapi.SessionRequest.html index fcb315eb0..76ddb08f1 100644 --- a/docs/interfaces/framework_hapi.SessionRequest.html +++ b/docs/interfaces/framework_hapi.SessionRequest.html @@ -89,7 +89,7 @@
    server: Server

    Access: read only and the public server interface. The server object.

    -
    state: Dictionary<any>
    +
    state: Dictionary<any>

    An object containing parsed HTTP state information (cookies) where each key is the cookie name and value is the matching cookie content after processing using any registered cookie definition.

    url: URL

    The parsed request URI.

    diff --git a/docs/interfaces/framework_koa.SessionContext.html b/docs/interfaces/framework_koa.SessionContext.html index 5a7cf7ee3..fbda99c36 100644 --- a/docs/interfaces/framework_koa.SessionContext.html +++ b/docs/interfaces/framework_koa.SessionContext.html @@ -81,7 +81,7 @@
    secure: boolean

    Short-hand for:

    this.protocol == 'https'

    -
    socket: Socket
    +
    socket: Socket

    Return the request socket.

    stale: boolean

    Check if the request is stale, aka diff --git a/docs/interfaces/framework_loopback.SessionContext.html b/docs/interfaces/framework_loopback.SessionContext.html index 8238ed078..74e57d185 100644 --- a/docs/interfaces/framework_loopback.SessionContext.html +++ b/docs/interfaces/framework_loopback.SessionContext.html @@ -14,7 +14,7 @@

    A flag to tell if the response is finished.

    scope: BindingScope

    Scope for binding resolution

    -
    subscriptionManager: ContextSubscriptionManager
    +
    subscriptionManager: ContextSubscriptionManager

    Manager for observer subscriptions

    tagIndexer: ContextTagIndexer

    Indexer for bindings by tag

    diff --git a/docs/interfaces/recipe_session.SessionContainer.html b/docs/interfaces/recipe_session.SessionContainer.html index 24f200395..88e6f6986 100644 --- a/docs/interfaces/recipe_session.SessionContainer.html +++ b/docs/interfaces/recipe_session.SessionContainer.html @@ -1 +1 @@ -SessionContainer | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SessionContainer

    Index

    Methods

    • attachToRequestResponse(reqResInfo: ReqResInfo, userContext?: Record<string, any>): void | Promise<void>
    • fetchAndSetClaim<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<void>
    • getAccessToken(userContext?: Record<string, any>): string
    • getAccessTokenPayload(userContext?: Record<string, any>): any
    • getAllSessionTokensDangerously(): { accessAndFrontTokenUpdated: boolean; accessToken: string; antiCsrfToken: undefined | string; frontToken: string; refreshToken: undefined | string }
    • Returns { accessAndFrontTokenUpdated: boolean; accessToken: string; antiCsrfToken: undefined | string; frontToken: string; refreshToken: undefined | string }

      • accessAndFrontTokenUpdated: boolean
      • accessToken: string
      • antiCsrfToken: undefined | string
      • frontToken: string
      • refreshToken: undefined | string
    • getClaimValue<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<undefined | T>
    • getExpiry(userContext?: Record<string, any>): Promise<number>
    • getHandle(userContext?: Record<string, any>): string
    • getRecipeUserId(userContext?: Record<string, any>): RecipeUserId
    • getSessionDataFromDatabase(userContext?: Record<string, any>): Promise<any>
    • getTenantId(userContext?: Record<string, any>): string
    • getTimeCreated(userContext?: Record<string, any>): Promise<number>
    • getUserId(userContext?: Record<string, any>): string
    • mergeIntoAccessTokenPayload(accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<void>
    • removeClaim(claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<void>
    • revokeSession(userContext?: Record<string, any>): Promise<void>
    • setClaimValue<T>(claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<void>
    • updateSessionDataInDatabase(newSessionData: any, userContext?: Record<string, any>): Promise<any>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Method
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +SessionContainer | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Hierarchy

    • SessionContainer

    Index

    Methods

    • attachToRequestResponse(reqResInfo: ReqResInfo, userContext?: Record<string, any>): void | Promise<void>
    • fetchAndSetClaim<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<void>
    • getAccessToken(userContext?: Record<string, any>): string
    • getAccessTokenPayload(userContext?: Record<string, any>): any
    • getAllSessionTokensDangerously(): { accessAndFrontTokenUpdated: boolean; accessToken: string; antiCsrfToken: undefined | string; frontToken: string; refreshToken: undefined | string }
    • Returns { accessAndFrontTokenUpdated: boolean; accessToken: string; antiCsrfToken: undefined | string; frontToken: string; refreshToken: undefined | string }

      • accessAndFrontTokenUpdated: boolean
      • accessToken: string
      • antiCsrfToken: undefined | string
      • frontToken: string
      • refreshToken: undefined | string
    • getClaimValue<T>(claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<undefined | T>
    • getExpiry(userContext?: Record<string, any>): Promise<number>
    • getHandle(userContext?: Record<string, any>): string
    • getRecipeUserId(userContext?: Record<string, any>): RecipeUserId
    • getSessionDataFromDatabase(userContext?: Record<string, any>): Promise<any>
    • getTenantId(userContext?: Record<string, any>): string
    • getTimeCreated(userContext?: Record<string, any>): Promise<number>
    • getUserId(userContext?: Record<string, any>): string
    • mergeIntoAccessTokenPayload(accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<void>
    • removeClaim(claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<void>
    • revokeSession(userContext?: Record<string, any>): Promise<void>
    • setClaimValue<T>(claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<void>
    • updateSessionDataInDatabase(newSessionData: any, userContext?: Record<string, any>): Promise<any>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Method
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/interfaces/recipe_session.VerifySessionOptions.html b/docs/interfaces/recipe_session.VerifySessionOptions.html index c9bbae358..ffb134e64 100644 --- a/docs/interfaces/recipe_session.VerifySessionOptions.html +++ b/docs/interfaces/recipe_session.VerifySessionOptions.html @@ -1 +1 @@ -VerifySessionOptions | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Method
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +VerifySessionOptions | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Interface
    • Property
    • Method
    • Class
    • Class with type parameter

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework.html b/docs/modules/framework.html index 7e0bf4c43..25ac792e8 100644 --- a/docs/modules/framework.html +++ b/docs/modules/framework.html @@ -1 +1 @@ -framework | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework

    Index

    Variables

    awsLambda: framework/awsLambda = awsLambdaFramework
    default: { awsLambda: framework/awsLambda; express: framework/express; fastify: framework/fastify; hapi: framework/hapi; koa: framework/koa; loopback: framework/loopback }

    Type declaration

    express: framework/express = expressFramework
    fastify: framework/fastify = fastifyFramework
    hapi: framework/hapi = hapiFramework
    koa: framework/koa = koaFramework
    loopback: framework/loopback = loopbackFramework

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework

    Index

    Variables

    awsLambda: framework/awsLambda = awsLambdaFramework
    default: { awsLambda: framework/awsLambda; express: framework/express; fastify: framework/fastify; hapi: framework/hapi; koa: framework/koa; loopback: framework/loopback }

    Type declaration

    express: framework/express = expressFramework
    fastify: framework/fastify = fastifyFramework
    hapi: framework/hapi = hapiFramework
    koa: framework/koa = koaFramework
    loopback: framework/loopback = loopbackFramework

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_awsLambda.html b/docs/modules/framework_awsLambda.html index 39477d9a1..e1cd760bb 100644 --- a/docs/modules/framework_awsLambda.html +++ b/docs/modules/framework_awsLambda.html @@ -1 +1 @@ -framework/awsLambda | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/awsLambda

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/awsLambda | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/awsLambda

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_custom.html b/docs/modules/framework_custom.html index 046fb726b..d4330d3b8 100644 --- a/docs/modules/framework_custom.html +++ b/docs/modules/framework_custom.html @@ -1 +1 @@ -framework/custom | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/custom

    Index

    Functions

    • middleware<OrigReqType, OrigRespType>(wrapRequest?: ((req: OrigReqType) => BaseRequest), wrapResponse?: ((req: OrigRespType) => BaseResponse)): ((request: OrigReqType, response: OrigRespType, next?: NextFunction) => Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>)
    • Type Parameters

      Parameters

      Returns ((request: OrigReqType, response: OrigRespType, next?: NextFunction) => Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>)

        • (request: OrigReqType, response: OrigRespType, next?: NextFunction): Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>
        • Parameters

          • request: OrigReqType
          • response: OrigRespType
          • Optional next: NextFunction

          Returns Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/custom | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/custom

    Index

    Functions

    • middleware<OrigReqType, OrigRespType>(wrapRequest?: ((req: OrigReqType) => BaseRequest), wrapResponse?: ((req: OrigRespType) => BaseResponse)): ((request: OrigReqType, response: OrigRespType, next?: NextFunction) => Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>)
    • Type Parameters

      Parameters

      Returns ((request: OrigReqType, response: OrigRespType, next?: NextFunction) => Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>)

        • (request: OrigReqType, response: OrigRespType, next?: NextFunction): Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>
        • Parameters

          • request: OrigReqType
          • response: OrigRespType
          • Optional next: NextFunction

          Returns Promise<{ error: undefined; handled: boolean } | { error: any; handled: undefined }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_express.html b/docs/modules/framework_express.html index d8a8e9daa..487e2fca4 100644 --- a/docs/modules/framework_express.html +++ b/docs/modules/framework_express.html @@ -1 +1 @@ -framework/express | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/express

    Index

    Functions

    • errorHandler(): ((err: any, req: Request, res: Response, next: NextFunction) => Promise<void>)
    • Returns ((err: any, req: Request, res: Response, next: NextFunction) => Promise<void>)

        • (err: any, req: Request, res: Response, next: NextFunction): Promise<void>
        • Parameters

          • err: any
          • req: Request
          • res: Response
          • next: NextFunction

          Returns Promise<void>

    • middleware(): ((req: Request, res: Response, next: NextFunction) => Promise<void>)
    • Returns ((req: Request, res: Response, next: NextFunction) => Promise<void>)

        • (req: Request, res: Response, next: NextFunction): Promise<void>
        • Parameters

          • req: Request
          • res: Response
          • next: NextFunction

          Returns Promise<void>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/express | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/express

    Index

    Functions

    • errorHandler(): ((err: any, req: Request, res: Response, next: NextFunction) => Promise<void>)
    • Returns ((err: any, req: Request, res: Response, next: NextFunction) => Promise<void>)

        • (err: any, req: Request, res: Response, next: NextFunction): Promise<void>
        • Parameters

          • err: any
          • req: Request
          • res: Response
          • next: NextFunction

          Returns Promise<void>

    • middleware(): ((req: Request, res: Response, next: NextFunction) => Promise<void>)
    • Returns ((req: Request, res: Response, next: NextFunction) => Promise<void>)

        • (req: Request, res: Response, next: NextFunction): Promise<void>
        • Parameters

          • req: Request
          • res: Response
          • next: NextFunction

          Returns Promise<void>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_fastify.html b/docs/modules/framework_fastify.html index a1c2086ac..7b028bd73 100644 --- a/docs/modules/framework_fastify.html +++ b/docs/modules/framework_fastify.html @@ -1 +1 @@ -framework/fastify | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/fastify

    Index

    Type Aliases

    SessionRequest<TRequest>: TRequest & { session?: SessionContainer }

    Type Parameters

    • TRequest extends OriginalFastifyRequest = OriginalFastifyRequest

    Functions

    • errorHandler(): ((err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>) => Promise<void>)
    • Returns ((err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>) => Promise<void>)

        • (err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>): Promise<void>
        • Parameters

          • err: any
          • req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>
          • res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>

          Returns Promise<void>

    • plugin(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyLoggerInstance>, opts: Record<never, never>, done: ((err?: Error) => void)): void
    • Parameters

      • instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyLoggerInstance>
      • opts: Record<never, never>
      • done: ((err?: Error) => void)
          • (err?: Error): void
          • Parameters

            • Optional err: Error

            Returns void

      Returns void

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/fastify | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/fastify

    Index

    Type Aliases

    SessionRequest<TRequest>: TRequest & { session?: SessionContainer }

    Type Parameters

    • TRequest extends OriginalFastifyRequest = OriginalFastifyRequest

    Functions

    • errorHandler(): ((err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>) => Promise<void>)
    • Returns ((err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>) => Promise<void>)

        • (err: any, req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>, res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>): Promise<void>
        • Parameters

          • err: any
          • req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage>
          • res: FastifyReply<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown>

          Returns Promise<void>

    • plugin(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyLoggerInstance>, opts: Record<never, never>, done: ((err?: Error) => void)): void
    • Parameters

      • instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyLoggerInstance>
      • opts: Record<never, never>
      • done: ((err?: Error) => void)
          • (err?: Error): void
          • Parameters

            • Optional err: Error

            Returns void

      Returns void

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_hapi.html b/docs/modules/framework_hapi.html index 51fec0510..fde73f1b6 100644 --- a/docs/modules/framework_hapi.html +++ b/docs/modules/framework_hapi.html @@ -1 +1 @@ -framework/hapi | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/hapi

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/hapi | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/hapi

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_koa.html b/docs/modules/framework_koa.html index 115e282ca..6d7197485 100644 --- a/docs/modules/framework_koa.html +++ b/docs/modules/framework_koa.html @@ -1 +1 @@ -framework/koa | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/koa

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/koa | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/koa

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/framework_loopback.html b/docs/modules/framework_loopback.html index ed9518d76..38da80f79 100644 --- a/docs/modules/framework_loopback.html +++ b/docs/modules/framework_loopback.html @@ -1 +1 @@ -framework/loopback | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/loopback

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +framework/loopback | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module framework/loopback

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/index.html b/docs/modules/index.html index 739f94dcb..5a10bf0f9 100644 --- a/docs/modules/index.html +++ b/docs/modules/index.html @@ -1 +1 @@ -index | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Index

    Variables

    Error: typeof default = SuperTokensWrapper.Error

    Functions

    • createUserIdMapping(input: { externalUserId: string; externalUserIdInfo?: string; force?: boolean; superTokensUserId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" | "UNKNOWN_SUPERTOKENS_USER_ID_ERROR" } | { doesExternalUserIdExist: boolean; doesSuperTokensUserIdExist: boolean; status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR" }>
    • Parameters

      • input: { externalUserId: string; externalUserIdInfo?: string; force?: boolean; superTokensUserId: string; userContext?: Record<string, any> }
        • externalUserId: string
        • Optional externalUserIdInfo?: string
        • Optional force?: boolean
        • superTokensUserId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_SUPERTOKENS_USER_ID_ERROR" } | { doesExternalUserIdExist: boolean; doesSuperTokensUserIdExist: boolean; status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR" }>

    • deleteUser(userId: string, removeAllLinkedAccounts?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • Parameters

      • userId: string
      • removeAllLinkedAccounts: boolean = true
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" }>

    • deleteUserIdMapping(input: { force?: boolean; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ didMappingExist: boolean; status: "OK" }>
    • Parameters

      • input: { force?: boolean; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional force?: boolean
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ didMappingExist: boolean; status: "OK" }>

    • getAllCORSHeaders(): string[]
    • getRequestFromUserContext(userContext: undefined | UserContext): undefined | BaseRequest
    • getUser(userId: string, userContext?: Record<string, any>): Promise<undefined | User>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<undefined | User>

    • getUserCount(includeRecipeIds?: string[], tenantId?: string, userContext?: Record<string, any>): Promise<number>
    • Parameters

      • Optional includeRecipeIds: string[]
      • Optional tenantId: string
      • Optional userContext: Record<string, any>

      Returns Promise<number>

    • getUserIdMapping(input: { userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ externalUserId: string; externalUserIdInfo: undefined | string; status: "OK"; superTokensUserId: string } | { status: "UNKNOWN_MAPPING_ERROR" }>
    • Parameters

      • input: { userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ externalUserId: string; externalUserIdInfo: undefined | string; status: "OK"; superTokensUserId: string } | { status: "UNKNOWN_MAPPING_ERROR" }>

    • getUsersNewestFirst(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }): Promise<{ nextPaginationToken?: string; users: User[] }>
    • Parameters

      • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }
        • Optional includeRecipeIds?: string[]
        • Optional limit?: number
        • Optional paginationToken?: string
        • Optional query?: {}
          • [key: string]: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • getUsersOldestFirst(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }): Promise<{ nextPaginationToken?: string; users: User[] }>
    • Parameters

      • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }
        • Optional includeRecipeIds?: string[]
        • Optional limit?: number
        • Optional paginationToken?: string
        • Optional query?: {}
          • [key: string]: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • init(config: TypeInput): void
    • listUsersByAccountInfo(tenantId: string, accountInfo: AccountInfo, doUnionOfAccountInfo?: boolean, userContext?: Record<string, any>): Promise<User[]>
    • Parameters

      • tenantId: string
      • accountInfo: AccountInfo
      • doUnionOfAccountInfo: boolean = false
      • Optional userContext: Record<string, any>

      Returns Promise<User[]>

    • updateOrDeleteUserIdMappingInfo(input: { externalUserIdInfo?: string; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ status: "OK" | "UNKNOWN_MAPPING_ERROR" }>
    • Parameters

      • input: { externalUserIdInfo?: string; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional externalUserIdInfo?: string
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ status: "OK" | "UNKNOWN_MAPPING_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +index | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Index

    Variables

    Error: typeof default = SuperTokensWrapper.Error

    Functions

    • createUserIdMapping(input: { externalUserId: string; externalUserIdInfo?: string; force?: boolean; superTokensUserId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" | "UNKNOWN_SUPERTOKENS_USER_ID_ERROR" } | { doesExternalUserIdExist: boolean; doesSuperTokensUserIdExist: boolean; status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR" }>
    • Parameters

      • input: { externalUserId: string; externalUserIdInfo?: string; force?: boolean; superTokensUserId: string; userContext?: Record<string, any> }
        • externalUserId: string
        • Optional externalUserIdInfo?: string
        • Optional force?: boolean
        • superTokensUserId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_SUPERTOKENS_USER_ID_ERROR" } | { doesExternalUserIdExist: boolean; doesSuperTokensUserIdExist: boolean; status: "USER_ID_MAPPING_ALREADY_EXISTS_ERROR" }>

    • deleteUser(userId: string, removeAllLinkedAccounts?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • Parameters

      • userId: string
      • removeAllLinkedAccounts: boolean = true
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" }>

    • deleteUserIdMapping(input: { force?: boolean; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ didMappingExist: boolean; status: "OK" }>
    • Parameters

      • input: { force?: boolean; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional force?: boolean
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ didMappingExist: boolean; status: "OK" }>

    • getAllCORSHeaders(): string[]
    • getRequestFromUserContext(userContext: undefined | UserContext): undefined | BaseRequest
    • getUser(userId: string, userContext?: Record<string, any>): Promise<undefined | User>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<undefined | User>

    • getUserCount(includeRecipeIds?: string[], tenantId?: string, userContext?: Record<string, any>): Promise<number>
    • Parameters

      • Optional includeRecipeIds: string[]
      • Optional tenantId: string
      • Optional userContext: Record<string, any>

      Returns Promise<number>

    • getUserIdMapping(input: { userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ externalUserId: string; externalUserIdInfo: undefined | string; status: "OK"; superTokensUserId: string } | { status: "UNKNOWN_MAPPING_ERROR" }>
    • Parameters

      • input: { userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ externalUserId: string; externalUserIdInfo: undefined | string; status: "OK"; superTokensUserId: string } | { status: "UNKNOWN_MAPPING_ERROR" }>

    • getUsersNewestFirst(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }): Promise<{ nextPaginationToken?: string; users: User[] }>
    • Parameters

      • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }
        • Optional includeRecipeIds?: string[]
        • Optional limit?: number
        • Optional paginationToken?: string
        • Optional query?: {}
          • [key: string]: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • getUsersOldestFirst(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }): Promise<{ nextPaginationToken?: string; users: User[] }>
    • Parameters

      • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; userContext?: Record<string, any> }
        • Optional includeRecipeIds?: string[]
        • Optional limit?: number
        • Optional paginationToken?: string
        • Optional query?: {}
          • [key: string]: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • init(config: TypeInput): void
    • listUsersByAccountInfo(tenantId: string, accountInfo: AccountInfo, doUnionOfAccountInfo?: boolean, userContext?: Record<string, any>): Promise<User[]>
    • Parameters

      • tenantId: string
      • accountInfo: AccountInfo
      • doUnionOfAccountInfo: boolean = false
      • Optional userContext: Record<string, any>

      Returns Promise<User[]>

    • updateOrDeleteUserIdMappingInfo(input: { externalUserIdInfo?: string; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }): Promise<{ status: "OK" | "UNKNOWN_MAPPING_ERROR" }>
    • Parameters

      • input: { externalUserIdInfo?: string; userContext?: Record<string, any>; userId: string; userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY" }
        • Optional externalUserIdInfo?: string
        • Optional userContext?: Record<string, any>
        • userId: string
        • Optional userIdType?: "SUPERTOKENS" | "EXTERNAL" | "ANY"

      Returns Promise<{ status: "OK" | "UNKNOWN_MAPPING_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_accountlinking.html b/docs/modules/recipe_accountlinking.html index a80aa29ff..065f8a3ec 100644 --- a/docs/modules/recipe_accountlinking.html +++ b/docs/modules/recipe_accountlinking.html @@ -1 +1 @@ -recipe/accountlinking | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/accountlinking

    Index

    Type Aliases

    RecipeInterface: { canCreatePrimaryUser: any; canLinkAccounts: any; createPrimaryUser: any; deleteUser: any; getUser: any; getUsers: any; linkAccounts: any; listUsersByAccountInfo: any; unlinkAccount: any }

    Type declaration

    • canCreatePrimaryUser:function
      • canCreatePrimaryUser(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
      • Parameters

        Returns Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • canLinkAccounts:function
      • canLinkAccounts(input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
      • Parameters

        • input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }
          • primaryUserId: string
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • createPrimaryUser:function
      • createPrimaryUser(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
      • Parameters

        Returns Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • deleteUser:function
      • deleteUser(input: { removeAllLinkedAccounts: boolean; userContext: UserContext; userId: string }): Promise<{ status: "OK" }>
      • Parameters

        • input: { removeAllLinkedAccounts: boolean; userContext: UserContext; userId: string }
          • removeAllLinkedAccounts: boolean
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" }>

    • getUser:function
      • getUser(input: { userContext: UserContext; userId: string }): Promise<undefined | User>
    • getUsers:function
      • getUsers(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; timeJoinedOrder: "ASC" | "DESC"; userContext: UserContext }): Promise<{ nextPaginationToken?: string; users: User[] }>
      • Parameters

        • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; timeJoinedOrder: "ASC" | "DESC"; userContext: UserContext }
          • Optional includeRecipeIds?: string[]
          • Optional limit?: number
          • Optional paginationToken?: string
          • Optional query?: {}
            • [key: string]: string
          • tenantId: string
          • timeJoinedOrder: "ASC" | "DESC"
          • userContext: UserContext

        Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • linkAccounts:function
      • linkAccounts(input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
      • Parameters

        • input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }
          • primaryUserId: string
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • listUsersByAccountInfo:function
      • listUsersByAccountInfo(input: { accountInfo: AccountInfo; doUnionOfAccountInfo: boolean; tenantId: string; userContext: UserContext }): Promise<User[]>
      • Parameters

        • input: { accountInfo: AccountInfo; doUnionOfAccountInfo: boolean; tenantId: string; userContext: UserContext }
          • accountInfo: AccountInfo
          • doUnionOfAccountInfo: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<User[]>

    • unlinkAccount:function
      • unlinkAccount(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; wasLinked: boolean; wasRecipeUserDeleted: boolean }>

    Functions

    • canCreatePrimaryUser(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • canLinkAccounts(recipeUserId: RecipeUserId, primaryUserId: string, userContext?: Record<string, any>): Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • primaryUserId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • createPrimaryUser(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • createPrimaryUserIdOrLinkAccounts(tenantId: string, recipeUserId: RecipeUserId, session?: SessionContainer, userContext?: Record<string, any>): Promise<User>
    • getPrimaryUserThatCanBeLinkedToRecipeUserId(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<undefined | User>
    • init(config?: TypeInput): RecipeListFunction
    • isEmailChangeAllowed(recipeUserId: RecipeUserId, newEmail: string, isVerified: boolean, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • isSignInAllowed(tenantId: string, recipeUserId: RecipeUserId, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • isSignUpAllowed(tenantId: string, newUser: AccountInfoWithRecipeId, isVerified: boolean, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • linkAccounts(recipeUserId: RecipeUserId, primaryUserId: string, userContext?: Record<string, any>): Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • primaryUserId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • unlinkAccount(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasLinked: boolean; wasRecipeUserDeleted: boolean }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/accountlinking | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/accountlinking

    Index

    Type Aliases

    RecipeInterface: { canCreatePrimaryUser: any; canLinkAccounts: any; createPrimaryUser: any; deleteUser: any; getUser: any; getUsers: any; linkAccounts: any; listUsersByAccountInfo: any; unlinkAccount: any }

    Type declaration

    • canCreatePrimaryUser:function
      • canCreatePrimaryUser(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
      • Parameters

        Returns Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • canLinkAccounts:function
      • canLinkAccounts(input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
      • Parameters

        • input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }
          • primaryUserId: string
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • createPrimaryUser:function
      • createPrimaryUser(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
      • Parameters

        Returns Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • deleteUser:function
      • deleteUser(input: { removeAllLinkedAccounts: boolean; userContext: UserContext; userId: string }): Promise<{ status: "OK" }>
      • Parameters

        • input: { removeAllLinkedAccounts: boolean; userContext: UserContext; userId: string }
          • removeAllLinkedAccounts: boolean
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" }>

    • getUser:function
      • getUser(input: { userContext: UserContext; userId: string }): Promise<undefined | User>
    • getUsers:function
      • getUsers(input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; timeJoinedOrder: "ASC" | "DESC"; userContext: UserContext }): Promise<{ nextPaginationToken?: string; users: User[] }>
      • Parameters

        • input: { includeRecipeIds?: string[]; limit?: number; paginationToken?: string; query?: {}; tenantId: string; timeJoinedOrder: "ASC" | "DESC"; userContext: UserContext }
          • Optional includeRecipeIds?: string[]
          • Optional limit?: number
          • Optional paginationToken?: string
          • Optional query?: {}
            • [key: string]: string
          • tenantId: string
          • timeJoinedOrder: "ASC" | "DESC"
          • userContext: UserContext

        Returns Promise<{ nextPaginationToken?: string; users: User[] }>

    • linkAccounts:function
      • linkAccounts(input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
      • Parameters

        • input: { primaryUserId: string; recipeUserId: RecipeUserId; userContext: UserContext }
          • primaryUserId: string
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • listUsersByAccountInfo:function
      • listUsersByAccountInfo(input: { accountInfo: AccountInfo; doUnionOfAccountInfo: boolean; tenantId: string; userContext: UserContext }): Promise<User[]>
      • Parameters

        • input: { accountInfo: AccountInfo; doUnionOfAccountInfo: boolean; tenantId: string; userContext: UserContext }
          • accountInfo: AccountInfo
          • doUnionOfAccountInfo: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<User[]>

    • unlinkAccount:function
      • unlinkAccount(input: { recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK"; wasLinked: boolean; wasRecipeUserDeleted: boolean }>

    Functions

    • canCreatePrimaryUser(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyAPrimaryUser: boolean } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • canLinkAccounts(recipeUserId: RecipeUserId, primaryUserId: string, userContext?: Record<string, any>): Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • primaryUserId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK" } | { description: string; primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • createPrimaryUser(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; user: User; wasAlreadyAPrimaryUser: boolean } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_PRIMARY_USER_ID_ERROR" } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" }>

    • createPrimaryUserIdOrLinkAccounts(tenantId: string, recipeUserId: RecipeUserId, session?: SessionContainer, userContext?: Record<string, any>): Promise<User>
    • getPrimaryUserThatCanBeLinkedToRecipeUserId(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<undefined | User>
    • init(config?: TypeInput): RecipeListFunction
    • isEmailChangeAllowed(recipeUserId: RecipeUserId, newEmail: string, isVerified: boolean, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • isSignInAllowed(tenantId: string, recipeUserId: RecipeUserId, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • isSignUpAllowed(tenantId: string, newUser: AccountInfoWithRecipeId, isVerified: boolean, session?: SessionContainer, userContext?: Record<string, any>): Promise<boolean>
    • linkAccounts(recipeUserId: RecipeUserId, primaryUserId: string, userContext?: Record<string, any>): Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>
    • Parameters

      • recipeUserId: RecipeUserId
      • primaryUserId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ accountsAlreadyLinked: boolean; status: "OK"; user: User } | { primaryUserId: string; status: "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; user: User } | { description: string; primaryUserId: string; status: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" } | { status: "INPUT_USER_IS_NOT_A_PRIMARY_USER" }>

    • unlinkAccount(recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasLinked: boolean; wasRecipeUserDeleted: boolean }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_dashboard.html b/docs/modules/recipe_dashboard.html index 02a730af8..115a5dd33 100644 --- a/docs/modules/recipe_dashboard.html +++ b/docs/modules/recipe_dashboard.html @@ -1 +1 @@ -recipe/dashboard | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/dashboard

    Index

    Type Aliases

    APIInterface: { dashboardGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<string>) }

    Type declaration

    • dashboardGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<string>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { getDashboardBundleLocation: any; shouldAllowAccess: any }

    Type declaration

    • getDashboardBundleLocation:function
      • getDashboardBundleLocation(input: { userContext: UserContext }): Promise<string>
    • shouldAllowAccess:function
      • shouldAllowAccess(input: { config: TypeNormalisedInput; req: BaseRequest; userContext: UserContext }): Promise<boolean>

    Functions

    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/dashboard | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/dashboard

    Index

    Type Aliases

    APIInterface: { dashboardGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<string>) }

    Type declaration

    • dashboardGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<string>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { getDashboardBundleLocation: any; shouldAllowAccess: any }

    Type declaration

    • getDashboardBundleLocation:function
      • getDashboardBundleLocation(input: { userContext: UserContext }): Promise<string>
    • shouldAllowAccess:function
      • shouldAllowAccess(input: { config: TypeNormalisedInput; req: BaseRequest; userContext: UserContext }): Promise<boolean>

    Functions

    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_emailpassword.html b/docs/modules/recipe_emailpassword.html index cf69c66dd..f2070586d 100644 --- a/docs/modules/recipe_emailpassword.html +++ b/docs/modules/recipe_emailpassword.html @@ -1,5 +1,5 @@ -recipe/emailpassword | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/emailpassword

    Index

    Type Aliases

    APIInterface: { emailExistsGET: undefined | ((input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ exists: boolean; status: "OK" } | GeneralErrorResponse>); generatePasswordResetTokenPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ status: "OK" } | { reason: string; status: "PASSWORD_RESET_NOT_ALLOWED" } | GeneralErrorResponse>); passwordResetPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; token: string; userContext: UserContext }) => Promise<{ email: string; status: "OK"; user: User } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" } | GeneralErrorResponse>); signInPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_NOT_ALLOWED" } | { status: "WRONG_CREDENTIALS_ERROR" } | GeneralErrorResponse>); signUpPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_UP_NOT_ALLOWED" } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>) }

    Type declaration

    • emailExistsGET: undefined | ((input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ exists: boolean; status: "OK" } | GeneralErrorResponse>)
    • generatePasswordResetTokenPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ status: "OK" } | { reason: string; status: "PASSWORD_RESET_NOT_ALLOWED" } | GeneralErrorResponse>)
    • passwordResetPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; token: string; userContext: UserContext }) => Promise<{ email: string; status: "OK"; user: User } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" } | GeneralErrorResponse>)
    • signInPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_NOT_ALLOWED" } | { status: "WRONG_CREDENTIALS_ERROR" } | GeneralErrorResponse>)
    • signUpPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_UP_NOT_ALLOWED" } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypeEmailPasswordEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { consumePasswordResetToken: any; createNewRecipeUser: any; createResetPasswordToken: any; signIn: any; signUp: any; updateEmailOrPassword: any; verifyCredentials: any }

    Type declaration

    • consumePasswordResetToken:function
      • consumePasswordResetToken(input: { tenantId: string; token: string; userContext: UserContext }): Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>
      • Parameters

        • input: { tenantId: string; token: string; userContext: UserContext }
          • tenantId: string
          • token: string
          • userContext: UserContext

        Returns Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>

    • createNewRecipeUser:function
      • createNewRecipeUser(input: { email: string; password: string; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>
      • Parameters

        • input: { email: string; password: string; tenantId: string; userContext: UserContext }
          • email: string
          • password: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>

    • createResetPasswordToken:function
      • createResetPasswordToken(input: { email: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>
      • +recipe/emailpassword | supertokens-node
        Options
        All
        • Public
        • Public/Protected
        • All
        Menu

        Module recipe/emailpassword

        Index

        Type Aliases

        APIInterface: { emailExistsGET: undefined | ((input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ exists: boolean; status: "OK" } | GeneralErrorResponse>); generatePasswordResetTokenPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ status: "OK" } | { reason: string; status: "PASSWORD_RESET_NOT_ALLOWED" } | GeneralErrorResponse>); passwordResetPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; token: string; userContext: UserContext }) => Promise<{ email: string; status: "OK"; user: User } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" } | GeneralErrorResponse>); signInPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_NOT_ALLOWED" } | { status: "WRONG_CREDENTIALS_ERROR" } | GeneralErrorResponse>); signUpPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_UP_NOT_ALLOWED" } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>) }

        Type declaration

        • emailExistsGET: undefined | ((input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ exists: boolean; status: "OK" } | GeneralErrorResponse>)
        • generatePasswordResetTokenPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; userContext: UserContext }) => Promise<{ status: "OK" } | { reason: string; status: "PASSWORD_RESET_NOT_ALLOWED" } | GeneralErrorResponse>)
        • passwordResetPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; tenantId: string; token: string; userContext: UserContext }) => Promise<{ email: string; status: "OK"; user: User } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" } | GeneralErrorResponse>)
        • signInPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_NOT_ALLOWED" } | { status: "WRONG_CREDENTIALS_ERROR" } | GeneralErrorResponse>)
        • signUpPOST: undefined | ((input: { formFields: { id: string; value: string }[]; options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }) => Promise<{ session: SessionContainer; status: "OK"; user: User } | { reason: string; status: "SIGN_UP_NOT_ALLOWED" } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>)
        APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypeEmailPasswordEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

        Type declaration

        RecipeInterface: { consumePasswordResetToken: any; createNewRecipeUser: any; createResetPasswordToken: any; signIn: any; signUp: any; updateEmailOrPassword: any; verifyCredentials: any }

        Type declaration

        • consumePasswordResetToken:function
          • consumePasswordResetToken(input: { tenantId: string; token: string; userContext: UserContext }): Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>
          • Parameters

            • input: { tenantId: string; token: string; userContext: UserContext }
              • tenantId: string
              • token: string
              • userContext: UserContext

            Returns Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>

        • createNewRecipeUser:function
          • createNewRecipeUser(input: { email: string; password: string; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>
          • Parameters

            • input: { email: string; password: string; tenantId: string; userContext: UserContext }
              • email: string
              • password: string
              • tenantId: string
              • userContext: UserContext

            Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>

        • createResetPasswordToken:function
          • createResetPasswordToken(input: { email: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>
          • We pass in the email as well to this function cause the input userId may not be associated with an emailpassword account. In this case, we need to know which email to use to create an emailpassword account later on.

            -

            Parameters

            • input: { email: string; tenantId: string; userContext: UserContext; userId: string }
              • email: string
              • tenantId: string
              • userContext: UserContext
              • userId: string

            Returns Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>

        • signIn:function
          • signIn(input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
          • Parameters

            • input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }
              • email: string
              • password: string
              • session: SessionContainer | undefined
              • tenantId: string
              • userContext: UserContext

            Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

        • signUp:function
          • signUp(input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
          • Parameters

            • input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }
              • email: string
              • password: string
              • session: SessionContainer | undefined
              • tenantId: string
              • userContext: UserContext

            Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

        • updateEmailOrPassword:function
          • updateEmailOrPassword(input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy: string; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
          • Parameters

            • input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy: string; userContext: UserContext }
              • Optional applyPasswordPolicy?: boolean
              • Optional email?: string
              • Optional password?: string
              • recipeUserId: RecipeUserId
              • tenantIdForPasswordPolicy: string
              • userContext: UserContext

            Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

        • verifyCredentials:function
          • verifyCredentials(input: { email: string; password: string; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>
          • Parameters

            • input: { email: string; password: string; tenantId: string; userContext: UserContext }
              • email: string
              • password: string
              • tenantId: string
              • userContext: UserContext

            Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>

        Variables

        Error: typeof default = Wrapper.Error

        Functions

        • consumePasswordResetToken(tenantId: string, token: string, userContext?: Record<string, any>): Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>
        • Parameters

          • tenantId: string
          • token: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>

        • createResetPasswordLink(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" }>
        • Parameters

          • tenantId: string
          • userId: string
          • email: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ link: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" }>

        • createResetPasswordToken(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>
        • Parameters

          • tenantId: string
          • userId: string
          • email: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>

        • init(config?: TypeInput): RecipeListFunction
        • resetPasswordUsingToken(tenantId: string, token: string, newPassword: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
        • Parameters

          • tenantId: string
          • token: string
          • newPassword: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

        • sendEmail(input: TypeEmailPasswordPasswordResetEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
        • sendResetPasswordEmail(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>
        • Parameters

          • tenantId: string
          • userId: string
          • email: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>

        • signIn(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>
        • signIn(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
        • Parameters

          • tenantId: string
          • email: string
          • password: string
          • Optional session: undefined
          • Optional userContext: Record<string, any>

          Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>

        • Parameters

          • tenantId: string
          • email: string
          • password: string
          • session: SessionContainer
          • Optional userContext: Record<string, any>

          Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

        • signUp(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>
        • signUp(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
        • Parameters

          • tenantId: string
          • email: string
          • password: string
          • Optional session: undefined
          • Optional userContext: Record<string, any>

          Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>

        • Parameters

          • tenantId: string
          • email: string
          • password: string
          • session: SessionContainer
          • Optional userContext: Record<string, any>

          Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

        • updateEmailOrPassword(input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
        • Parameters

          • input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }
            • Optional applyPasswordPolicy?: boolean
            • Optional email?: string
            • Optional password?: string
            • recipeUserId: RecipeUserId
            • Optional tenantIdForPasswordPolicy?: string
            • Optional userContext?: Record<string, any>

          Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

        • verifyCredentials(tenantId: string, email: string, password: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>
        • Parameters

          • tenantId: string
          • email: string
          • password: string
          • Optional userContext: Record<string, any>

          Returns Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>

        Legend

        • Variable
        • Function
        • Function with type parameter
        • Type alias
        • Type alias with type parameter
        • Class
        • Class with type parameter
        • Interface

        Settings

        Theme

        Generated using TypeDoc

        \ No newline at end of file +

        Parameters

        • input: { email: string; tenantId: string; userContext: UserContext; userId: string }
          • email: string
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • signIn:function
      • signIn(input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }
          • email: string
          • password: string
          • session: SessionContainer | undefined
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • signUp:function
      • signUp(input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; password: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }
          • email: string
          • password: string
          • session: SessionContainer | undefined
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • updateEmailOrPassword:function
      • updateEmailOrPassword(input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy: string; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
      • Parameters

        • input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy: string; userContext: UserContext }
          • Optional applyPasswordPolicy?: boolean
          • Optional email?: string
          • Optional password?: string
          • recipeUserId: RecipeUserId
          • tenantIdForPasswordPolicy: string
          • userContext: UserContext

        Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

    • verifyCredentials:function
      • verifyCredentials(input: { email: string; password: string; tenantId: string; userContext: UserContext }): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>
      • Parameters

        • input: { email: string; password: string; tenantId: string; userContext: UserContext }
          • email: string
          • password: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>

    Variables

    Error: typeof default = Wrapper.Error

    Functions

    • consumePasswordResetToken(tenantId: string, token: string, userContext?: Record<string, any>): Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>
    • Parameters

      • tenantId: string
      • token: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ email: string; status: "OK"; userId: string } | { status: "RESET_PASSWORD_INVALID_TOKEN_ERROR" }>

    • createResetPasswordLink(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • email: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ link: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • createResetPasswordToken(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • email: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; token: string } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • init(config?: TypeInput): RecipeListFunction
    • resetPasswordUsingToken(tenantId: string, token: string, newPassword: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
    • Parameters

      • tenantId: string
      • token: string
      • newPassword: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "RESET_PASSWORD_INVALID_TOKEN_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

    • sendEmail(input: TypeEmailPasswordPasswordResetEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendResetPasswordEmail(tenantId: string, userId: string, email: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • email: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" }>

    • signIn(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>
    • signIn(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • email: string
      • password: string
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" }>

    • Parameters

      • tenantId: string
      • email: string
      • password: string
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "WRONG_CREDENTIALS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • signUp(tenantId: string, email: string, password: string, session?: undefined, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>
    • signUp(tenantId: string, email: string, password: string, session: SessionContainer, userContext?: Record<string, any>): Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • email: string
      • password: string
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" }>

    • Parameters

      • tenantId: string
      • email: string
      • password: string
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ recipeUserId: RecipeUserId; status: "OK"; user: User } | { status: "EMAIL_ALREADY_EXISTS_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • updateEmailOrPassword(input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>
    • Parameters

      • input: { applyPasswordPolicy?: boolean; email?: string; password?: string; recipeUserId: RecipeUserId; tenantIdForPasswordPolicy?: string; userContext?: Record<string, any> }
        • Optional applyPasswordPolicy?: boolean
        • Optional email?: string
        • Optional password?: string
        • recipeUserId: RecipeUserId
        • Optional tenantIdForPasswordPolicy?: string
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { failureReason: string; status: "PASSWORD_POLICY_VIOLATED_ERROR" }>

    • verifyCredentials(tenantId: string, email: string, password: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>
    • Parameters

      • tenantId: string
      • email: string
      • password: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "WRONG_CREDENTIALS_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_emailverification.html b/docs/modules/recipe_emailverification.html index f112fa41d..0c51980c0 100644 --- a/docs/modules/recipe_emailverification.html +++ b/docs/modules/recipe_emailverification.html @@ -1 +1 @@ -recipe/emailverification | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/emailverification

    Index

    Type Aliases

    APIInterface: { generateEmailVerifyTokenPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ status: "OK" } | { newSession?: SessionContainer; status: "EMAIL_ALREADY_VERIFIED_ERROR" } | GeneralErrorResponse>); isEmailVerifiedGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ isVerified: boolean; newSession?: SessionContainer; status: "OK" } | GeneralErrorResponse>); verifyEmailPOST: undefined | ((input: { options: APIOptions; session: SessionContainer | undefined; tenantId: string; token: string; userContext: UserContext }) => Promise<{ newSession?: SessionContainer; status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" } | GeneralErrorResponse>) }

    Type declaration

    • generateEmailVerifyTokenPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ status: "OK" } | { newSession?: SessionContainer; status: "EMAIL_ALREADY_VERIFIED_ERROR" } | GeneralErrorResponse>)
    • isEmailVerifiedGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ isVerified: boolean; newSession?: SessionContainer; status: "OK" } | GeneralErrorResponse>)
    • verifyEmailPOST: undefined | ((input: { options: APIOptions; session: SessionContainer | undefined; tenantId: string; token: string; userContext: UserContext }) => Promise<{ newSession?: SessionContainer; status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" } | GeneralErrorResponse>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypeEmailVerificationEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • emailDelivery: default<TypeEmailVerificationEmailDeliveryInput>
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    RecipeInterface: { createEmailVerificationToken: any; isEmailVerified: any; revokeEmailVerificationTokens: any; unverifyEmail: any; verifyEmailUsingToken: any }

    Type declaration

    • createEmailVerificationToken:function
      • createEmailVerificationToken(input: { email: string; recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • isEmailVerified:function
      • isEmailVerified(input: { email: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<boolean>
    • revokeEmailVerificationTokens:function
      • revokeEmailVerificationTokens(input: { email: string; recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
    • unverifyEmail:function
      • unverifyEmail(input: { email: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK" }>
    • verifyEmailUsingToken:function
      • verifyEmailUsingToken(input: { attemptAccountLinking: boolean; tenantId: string; token: string; userContext: UserContext }): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>
      • Parameters

        • input: { attemptAccountLinking: boolean; tenantId: string; token: string; userContext: UserContext }
          • attemptAccountLinking: boolean
          • tenantId: string
          • token: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

    UserEmailInfo: { email: string; recipeUserId: RecipeUserId }

    Type declaration

    Variables

    EmailVerificationClaim: EmailVerificationClaimClass = ...
    Error: typeof default = Wrapper.Error

    Functions

    • createEmailVerificationLink(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • createEmailVerificationToken(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • init(config: TypeInput): RecipeListFunction
    • isEmailVerified(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<boolean>
    • revokeEmailVerificationTokens(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
    • sendEmail(input: TypeEmailVerificationEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendEmailVerificationEmail(tenantId: string, userId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • unverifyEmail(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
    • verifyEmailUsingToken(tenantId: string, token: string, attemptAccountLinking?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/emailverification | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/emailverification

    Index

    Type Aliases

    APIInterface: { generateEmailVerifyTokenPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ status: "OK" } | { newSession?: SessionContainer; status: "EMAIL_ALREADY_VERIFIED_ERROR" } | GeneralErrorResponse>); isEmailVerifiedGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ isVerified: boolean; newSession?: SessionContainer; status: "OK" } | GeneralErrorResponse>); verifyEmailPOST: undefined | ((input: { options: APIOptions; session: SessionContainer | undefined; tenantId: string; token: string; userContext: UserContext }) => Promise<{ newSession?: SessionContainer; status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" } | GeneralErrorResponse>) }

    Type declaration

    • generateEmailVerifyTokenPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ status: "OK" } | { newSession?: SessionContainer; status: "EMAIL_ALREADY_VERIFIED_ERROR" } | GeneralErrorResponse>)
    • isEmailVerifiedGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ isVerified: boolean; newSession?: SessionContainer; status: "OK" } | GeneralErrorResponse>)
    • verifyEmailPOST: undefined | ((input: { options: APIOptions; session: SessionContainer | undefined; tenantId: string; token: string; userContext: UserContext }) => Promise<{ newSession?: SessionContainer; status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" } | GeneralErrorResponse>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypeEmailVerificationEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • emailDelivery: default<TypeEmailVerificationEmailDeliveryInput>
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    RecipeInterface: { createEmailVerificationToken: any; isEmailVerified: any; revokeEmailVerificationTokens: any; unverifyEmail: any; verifyEmailUsingToken: any }

    Type declaration

    • createEmailVerificationToken:function
      • createEmailVerificationToken(input: { email: string; recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • isEmailVerified:function
      • isEmailVerified(input: { email: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<boolean>
    • revokeEmailVerificationTokens:function
      • revokeEmailVerificationTokens(input: { email: string; recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
    • unverifyEmail:function
      • unverifyEmail(input: { email: string; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK" }>
    • verifyEmailUsingToken:function
      • verifyEmailUsingToken(input: { attemptAccountLinking: boolean; tenantId: string; token: string; userContext: UserContext }): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>
      • Parameters

        • input: { attemptAccountLinking: boolean; tenantId: string; token: string; userContext: UserContext }
          • attemptAccountLinking: boolean
          • tenantId: string
          • token: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

    UserEmailInfo: { email: string; recipeUserId: RecipeUserId }

    Type declaration

    Variables

    EmailVerificationClaim: EmailVerificationClaimClass = ...
    Error: typeof default = Wrapper.Error

    Functions

    • createEmailVerificationLink(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ link: string; status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • createEmailVerificationToken(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK"; token: string } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • init(config: TypeInput): RecipeListFunction
    • isEmailVerified(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<boolean>
    • revokeEmailVerificationTokens(tenantId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
    • sendEmail(input: TypeEmailVerificationEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendEmailVerificationEmail(tenantId: string, userId: string, recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: "OK" } | { status: "EMAIL_ALREADY_VERIFIED_ERROR" }>
    • unverifyEmail(recipeUserId: RecipeUserId, email?: string, userContext?: Record<string, any>): Promise<{ status: string }>
    • verifyEmailUsingToken(tenantId: string, token: string, attemptAccountLinking?: boolean, userContext?: Record<string, any>): Promise<{ status: "OK"; user: UserEmailInfo } | { status: "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_jwt.html b/docs/modules/recipe_jwt.html index 5717a952f..20b4d5e92 100644 --- a/docs/modules/recipe_jwt.html +++ b/docs/modules/recipe_jwt.html @@ -1 +1 @@ -recipe/jwt | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/jwt

    Index

    Type Aliases

    APIInterface: { getJWKSGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<{ keys: JsonWebKey[] } | GeneralErrorResponse>) }

    Type declaration

    • getJWKSGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<{ keys: JsonWebKey[] } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    JsonWebKey: { alg: string; e: string; kid: string; kty: string; n: string; use: string }

    Type declaration

    • alg: string
    • e: string
    • kid: string
    • kty: string
    • n: string
    • use: string
    RecipeInterface: { createJWT: any; getJWKS: any }

    Type declaration

    • createJWT:function
      • createJWT(input: { payload?: any; useStaticSigningKey?: boolean; userContext: UserContext; validitySeconds?: number }): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
      • Parameters

        • input: { payload?: any; useStaticSigningKey?: boolean; userContext: UserContext; validitySeconds?: number }
          • Optional payload?: any
          • Optional useStaticSigningKey?: boolean
          • userContext: UserContext
          • Optional validitySeconds?: number

        Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS:function
      • getJWKS(input: { userContext: UserContext }): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>

    Functions

    • createJWT(payload: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
    • Parameters

      • payload: any
      • Optional validitySeconds: number
      • Optional useStaticSigningKey: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/jwt | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/jwt

    Index

    Type Aliases

    APIInterface: { getJWKSGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<{ keys: JsonWebKey[] } | GeneralErrorResponse>) }

    Type declaration

    • getJWKSGET: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<{ keys: JsonWebKey[] } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    JsonWebKey: { alg: string; e: string; kid: string; kty: string; n: string; use: string }

    Type declaration

    • alg: string
    • e: string
    • kid: string
    • kty: string
    • n: string
    • use: string
    RecipeInterface: { createJWT: any; getJWKS: any }

    Type declaration

    • createJWT:function
      • createJWT(input: { payload?: any; useStaticSigningKey?: boolean; userContext: UserContext; validitySeconds?: number }): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
      • Parameters

        • input: { payload?: any; useStaticSigningKey?: boolean; userContext: UserContext; validitySeconds?: number }
          • Optional payload?: any
          • Optional useStaticSigningKey?: boolean
          • userContext: UserContext
          • Optional validitySeconds?: number

        Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS:function
      • getJWKS(input: { userContext: UserContext }): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>

    Functions

    • createJWT(payload: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
    • Parameters

      • payload: any
      • Optional validitySeconds: number
      • Optional useStaticSigningKey: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_multifactorauth.html b/docs/modules/recipe_multifactorauth.html index 18b84158e..b59d78664 100644 --- a/docs/modules/recipe_multifactorauth.html +++ b/docs/modules/recipe_multifactorauth.html @@ -1 +1 @@ -recipe/multifactorauth | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/multifactorauth

    Index

    Type Aliases

    APIInterface: { resyncSessionAndFetchMFAInfoPUT: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ emails: Record<string, string[] | undefined>; factors: { allowedToSetup: string[]; alreadySetup: string[]; next: string[] }; phoneNumbers: Record<string, string[] | undefined>; status: "OK" } | GeneralErrorResponse>) }

    Type declaration

    • resyncSessionAndFetchMFAInfoPUT: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ emails: Record<string, string[] | undefined>; factors: { allowedToSetup: string[]; alreadySetup: string[]; next: string[] }; phoneNumbers: Record<string, string[] | undefined>; status: "OK" } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; recipeInstance: Recipe; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { addToRequiredSecondaryFactorsForUser: any; assertAllowedToSetupFactorElseThrowInvalidClaimError: any; getFactorsSetupForUser: any; getMFARequirementsForAuth: any; getRequiredSecondaryFactorsForUser: any; markFactorAsCompleteInSession: any; removeFromRequiredSecondaryFactorsForUser: any }

    Type declaration

    • addToRequiredSecondaryFactorsForUser:function
      • addToRequiredSecondaryFactorsForUser(input: { factorId: string; userContext: UserContext; userId: string }): Promise<void>
      • Parameters

        • input: { factorId: string; userContext: UserContext; userId: string }
          • factorId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<void>

    • assertAllowedToSetupFactorElseThrowInvalidClaimError:function
      • assertAllowedToSetupFactorElseThrowInvalidClaimError(input: { factorId: string; factorsSetUpForUser: Promise<string[]>; mfaRequirementsForAuth: Promise<MFARequirementList>; session: SessionContainer; userContext: UserContext }): Promise<void>
      • Parameters

        • input: { factorId: string; factorsSetUpForUser: Promise<string[]>; mfaRequirementsForAuth: Promise<MFARequirementList>; session: SessionContainer; userContext: UserContext }
          • factorId: string
          • factorsSetUpForUser: Promise<string[]>
          • mfaRequirementsForAuth: Promise<MFARequirementList>
          • session: SessionContainer
          • userContext: UserContext

        Returns Promise<void>

    • getFactorsSetupForUser:function
      • getFactorsSetupForUser(input: { user: User; userContext: UserContext }): Promise<string[]>
    • getMFARequirementsForAuth:function
      • getMFARequirementsForAuth(input: { accessTokenPayload: JSONObject; completedFactors: MFAClaimValue["c"]; factorsSetUpForUser: Promise<string[]>; requiredSecondaryFactorsForTenant: Promise<string[]>; requiredSecondaryFactorsForUser: Promise<string[]>; tenantId: string; user: Promise<User>; userContext: UserContext }): MFARequirementList | Promise<MFARequirementList>
      • Parameters

        • input: { accessTokenPayload: JSONObject; completedFactors: MFAClaimValue["c"]; factorsSetUpForUser: Promise<string[]>; requiredSecondaryFactorsForTenant: Promise<string[]>; requiredSecondaryFactorsForUser: Promise<string[]>; tenantId: string; user: Promise<User>; userContext: UserContext }
          • accessTokenPayload: JSONObject
          • completedFactors: MFAClaimValue["c"]
          • factorsSetUpForUser: Promise<string[]>
          • requiredSecondaryFactorsForTenant: Promise<string[]>
          • requiredSecondaryFactorsForUser: Promise<string[]>
          • tenantId: string
          • user: Promise<User>
          • userContext: UserContext

        Returns MFARequirementList | Promise<MFARequirementList>

    • getRequiredSecondaryFactorsForUser:function
      • getRequiredSecondaryFactorsForUser(input: { userContext: UserContext; userId: string }): Promise<string[]>
    • markFactorAsCompleteInSession:function
      • markFactorAsCompleteInSession(input: { factorId: string; session: SessionContainer; userContext: UserContext }): Promise<void>
    • removeFromRequiredSecondaryFactorsForUser:function
      • removeFromRequiredSecondaryFactorsForUser(input: { factorId: string; userContext: UserContext; userId: string }): Promise<void>

    Variables

    FactorIds: { EMAILPASSWORD: string; LINK_EMAIL: string; LINK_PHONE: string; OTP_EMAIL: string; OTP_PHONE: string; THIRDPARTY: string; TOTP: string } = ...

    Type declaration

    • EMAILPASSWORD: string
    • LINK_EMAIL: string
    • LINK_PHONE: string
    • OTP_EMAIL: string
    • OTP_PHONE: string
    • THIRDPARTY: string
    • TOTP: string
    MultiFactorAuthClaim: MultiFactorAuthClaimClass = ...

    Functions

    • addToRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>
    • assertAllowedToSetupFactorElseThrowInvalidClaimError(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
    • getFactorsSetupForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
    • getRequiredSecondaryFactorsForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
    • init(config?: TypeInput): RecipeListFunction
    • markFactorAsCompleteInSession(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
    • removeFromRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/multifactorauth | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/multifactorauth

    Index

    Type Aliases

    APIInterface: { resyncSessionAndFetchMFAInfoPUT: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ emails: Record<string, string[] | undefined>; factors: { allowedToSetup: string[]; alreadySetup: string[]; next: string[] }; phoneNumbers: Record<string, string[] | undefined>; status: "OK" } | GeneralErrorResponse>) }

    Type declaration

    • resyncSessionAndFetchMFAInfoPUT: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ emails: Record<string, string[] | undefined>; factors: { allowedToSetup: string[]; alreadySetup: string[]; next: string[] }; phoneNumbers: Record<string, string[] | undefined>; status: "OK" } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; recipeInstance: Recipe; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { addToRequiredSecondaryFactorsForUser: any; assertAllowedToSetupFactorElseThrowInvalidClaimError: any; getFactorsSetupForUser: any; getMFARequirementsForAuth: any; getRequiredSecondaryFactorsForUser: any; markFactorAsCompleteInSession: any; removeFromRequiredSecondaryFactorsForUser: any }

    Type declaration

    • addToRequiredSecondaryFactorsForUser:function
      • addToRequiredSecondaryFactorsForUser(input: { factorId: string; userContext: UserContext; userId: string }): Promise<void>
      • Parameters

        • input: { factorId: string; userContext: UserContext; userId: string }
          • factorId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<void>

    • assertAllowedToSetupFactorElseThrowInvalidClaimError:function
      • assertAllowedToSetupFactorElseThrowInvalidClaimError(input: { factorId: string; factorsSetUpForUser: Promise<string[]>; mfaRequirementsForAuth: Promise<MFARequirementList>; session: SessionContainer; userContext: UserContext }): Promise<void>
      • Parameters

        • input: { factorId: string; factorsSetUpForUser: Promise<string[]>; mfaRequirementsForAuth: Promise<MFARequirementList>; session: SessionContainer; userContext: UserContext }
          • factorId: string
          • factorsSetUpForUser: Promise<string[]>
          • mfaRequirementsForAuth: Promise<MFARequirementList>
          • session: SessionContainer
          • userContext: UserContext

        Returns Promise<void>

    • getFactorsSetupForUser:function
      • getFactorsSetupForUser(input: { user: User; userContext: UserContext }): Promise<string[]>
    • getMFARequirementsForAuth:function
      • getMFARequirementsForAuth(input: { accessTokenPayload: JSONObject; completedFactors: MFAClaimValue["c"]; factorsSetUpForUser: Promise<string[]>; requiredSecondaryFactorsForTenant: Promise<string[]>; requiredSecondaryFactorsForUser: Promise<string[]>; tenantId: string; user: Promise<User>; userContext: UserContext }): MFARequirementList | Promise<MFARequirementList>
      • Parameters

        • input: { accessTokenPayload: JSONObject; completedFactors: MFAClaimValue["c"]; factorsSetUpForUser: Promise<string[]>; requiredSecondaryFactorsForTenant: Promise<string[]>; requiredSecondaryFactorsForUser: Promise<string[]>; tenantId: string; user: Promise<User>; userContext: UserContext }
          • accessTokenPayload: JSONObject
          • completedFactors: MFAClaimValue["c"]
          • factorsSetUpForUser: Promise<string[]>
          • requiredSecondaryFactorsForTenant: Promise<string[]>
          • requiredSecondaryFactorsForUser: Promise<string[]>
          • tenantId: string
          • user: Promise<User>
          • userContext: UserContext

        Returns MFARequirementList | Promise<MFARequirementList>

    • getRequiredSecondaryFactorsForUser:function
      • getRequiredSecondaryFactorsForUser(input: { userContext: UserContext; userId: string }): Promise<string[]>
    • markFactorAsCompleteInSession:function
      • markFactorAsCompleteInSession(input: { factorId: string; session: SessionContainer; userContext: UserContext }): Promise<void>
    • removeFromRequiredSecondaryFactorsForUser:function
      • removeFromRequiredSecondaryFactorsForUser(input: { factorId: string; userContext: UserContext; userId: string }): Promise<void>

    Variables

    FactorIds: { EMAILPASSWORD: string; LINK_EMAIL: string; LINK_PHONE: string; OTP_EMAIL: string; OTP_PHONE: string; THIRDPARTY: string; TOTP: string } = ...

    Type declaration

    • EMAILPASSWORD: string
    • LINK_EMAIL: string
    • LINK_PHONE: string
    • OTP_EMAIL: string
    • OTP_PHONE: string
    • THIRDPARTY: string
    • TOTP: string
    MultiFactorAuthClaim: MultiFactorAuthClaimClass = ...

    Functions

    • addToRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>
    • assertAllowedToSetupFactorElseThrowInvalidClaimError(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
    • getFactorsSetupForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
    • getRequiredSecondaryFactorsForUser(userId: string, userContext?: Record<string, any>): Promise<string[]>
    • init(config?: TypeInput): RecipeListFunction
    • markFactorAsCompleteInSession(session: SessionContainer, factorId: string, userContext?: Record<string, any>): Promise<void>
    • removeFromRequiredSecondaryFactorsForUser(userId: string, factorId: string, userContext?: Record<string, any>): Promise<void>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_multitenancy.html b/docs/modules/recipe_multitenancy.html index 3b61cf73c..bc199eb52 100644 --- a/docs/modules/recipe_multitenancy.html +++ b/docs/modules/recipe_multitenancy.html @@ -1 +1 @@ -recipe/multitenancy | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/multitenancy

    Index

    Type Aliases

    APIInterface: { loginMethodsGET: any }

    Type declaration

    • loginMethodsGET:function
      • loginMethodsGET(input: { clientType?: string; options: APIOptions; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { emailPassword: { enabled: boolean }; firstFactors: string[]; passwordless: { enabled: boolean }; status: "OK"; thirdParty: { enabled: boolean; providers: { id: string; name?: string }[] } }>
      • Parameters

        • input: { clientType?: string; options: APIOptions; tenantId: string; userContext: UserContext }
          • Optional clientType?: string
          • options: APIOptions
          • tenantId: string
          • userContext: UserContext

        Returns Promise<GeneralErrorResponse | { emailPassword: { enabled: boolean }; firstFactors: string[]; passwordless: { enabled: boolean }; status: "OK"; thirdParty: { enabled: boolean; providers: { id: string; name?: string }[] } }>

    APIOptions: { allAvailableFirstFactors: string[]; config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse; staticFirstFactors: string[] | undefined; staticThirdPartyProviders: ProviderInput[] }

    Type declaration

    • allAvailableFirstFactors: string[]
    • config: TypeNormalisedInput
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    • staticFirstFactors: string[] | undefined
    • staticThirdPartyProviders: ProviderInput[]
    RecipeInterface: { associateUserToTenant: any; createOrUpdateTenant: any; createOrUpdateThirdPartyConfig: any; deleteTenant: any; deleteThirdPartyConfig: any; disassociateUserFromTenant: any; getTenant: any; getTenantId: any; listAllTenants: any }

    Type declaration

    • associateUserToTenant:function
      • associateUserToTenant(input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
      • Parameters

        • input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }
          • recipeUserId: RecipeUserId
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

    • createOrUpdateTenant:function
      • createOrUpdateTenant(input: { config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }; tenantId: string; userContext: UserContext }): Promise<{ createdNew: boolean; status: "OK" }>
      • Parameters

        • input: { config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }; tenantId: string; userContext: UserContext }
          • Optional config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
            • Optional coreConfig?: {}
              • [key: string]: any
            • Optional emailPasswordEnabled?: boolean
            • Optional firstFactors?: string[]
            • Optional passwordlessEnabled?: boolean
            • Optional requiredSecondaryFactors?: string[]
            • Optional thirdPartyEnabled?: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ createdNew: boolean; status: "OK" }>

    • createOrUpdateThirdPartyConfig:function
      • createOrUpdateThirdPartyConfig(input: { config: ProviderConfig; skipValidation?: boolean; tenantId: string; userContext: UserContext }): Promise<{ createdNew: boolean; status: "OK" }>
      • Parameters

        • input: { config: ProviderConfig; skipValidation?: boolean; tenantId: string; userContext: UserContext }
          • config: ProviderConfig
          • Optional skipValidation?: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ createdNew: boolean; status: "OK" }>

    • deleteTenant:function
      • deleteTenant(input: { tenantId: string; userContext: UserContext }): Promise<{ didExist: boolean; status: "OK" }>
      • Parameters

        • input: { tenantId: string; userContext: UserContext }
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ didExist: boolean; status: "OK" }>

    • deleteThirdPartyConfig:function
      • deleteThirdPartyConfig(input: { tenantId: string; thirdPartyId: string; userContext: UserContext }): Promise<{ didConfigExist: boolean; status: "OK" }>
      • Parameters

        • input: { tenantId: string; thirdPartyId: string; userContext: UserContext }
          • tenantId: string
          • thirdPartyId: string
          • userContext: UserContext

        Returns Promise<{ didConfigExist: boolean; status: "OK" }>

    • disassociateUserFromTenant:function
      • disassociateUserFromTenant(input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; wasAssociated: boolean }>
    • getTenant:function
      • getTenant(input: { tenantId: string; userContext: UserContext }): Promise<undefined | { status: "OK" } & TenantConfig>
      • Parameters

        • input: { tenantId: string; userContext: UserContext }
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | { status: "OK" } & TenantConfig>

    • getTenantId:function
      • getTenantId(input: { tenantIdFromFrontend: string; userContext: UserContext }): Promise<string>
      • Parameters

        • input: { tenantIdFromFrontend: string; userContext: UserContext }
          • tenantIdFromFrontend: string
          • userContext: UserContext

        Returns Promise<string>

    • listAllTenants:function
      • listAllTenants(input: { userContext: UserContext }): Promise<{ status: "OK"; tenants: (TenantConfig & { tenantId: string })[] }>
      • Parameters

        • input: { userContext: UserContext }
          • userContext: UserContext

        Returns Promise<{ status: "OK"; tenants: (TenantConfig & { tenantId: string })[] }>

    Variables

    AllowedDomainsClaim: AllowedDomainsClaimClass = ...

    Functions

    • associateUserToTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
    • Parameters

      • tenantId: string
      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

    • createOrUpdateTenant(tenantId: string, config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • Optional config: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
        • Optional coreConfig?: {}
          • [key: string]: any
        • Optional emailPasswordEnabled?: boolean
        • Optional firstFactors?: string[]
        • Optional passwordlessEnabled?: boolean
        • Optional requiredSecondaryFactors?: string[]
        • Optional thirdPartyEnabled?: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNew: boolean; status: "OK" }>

    • createOrUpdateThirdPartyConfig(tenantId: string, config: ProviderConfig, skipValidation?: boolean, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • config: ProviderConfig
      • Optional skipValidation: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNew: boolean; status: "OK" }>

    • deleteTenant(tenantId: string, userContext?: Record<string, any>): Promise<{ didExist: boolean; status: "OK" }>
    • deleteThirdPartyConfig(tenantId: string, thirdPartyId: string, userContext?: Record<string, any>): Promise<{ didConfigExist: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didConfigExist: boolean; status: "OK" }>

    • disassociateUserFromTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAssociated: boolean }>
    • getTenant(tenantId: string, userContext?: Record<string, any>): Promise<undefined | { status: "OK" } & TenantConfig>
    • init(config?: TypeInput): RecipeListFunction
    • listAllTenants(userContext?: Record<string, any>): Promise<{ status: "OK"; tenants: ({ tenantId: string } & TenantConfig)[] }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/multitenancy | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/multitenancy

    Index

    Type Aliases

    APIInterface: { loginMethodsGET: any }

    Type declaration

    • loginMethodsGET:function
      • loginMethodsGET(input: { clientType?: string; options: APIOptions; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { emailPassword: { enabled: boolean }; firstFactors: string[]; passwordless: { enabled: boolean }; status: "OK"; thirdParty: { enabled: boolean; providers: { id: string; name?: string }[] } }>
      • Parameters

        • input: { clientType?: string; options: APIOptions; tenantId: string; userContext: UserContext }
          • Optional clientType?: string
          • options: APIOptions
          • tenantId: string
          • userContext: UserContext

        Returns Promise<GeneralErrorResponse | { emailPassword: { enabled: boolean }; firstFactors: string[]; passwordless: { enabled: boolean }; status: "OK"; thirdParty: { enabled: boolean; providers: { id: string; name?: string }[] } }>

    APIOptions: { allAvailableFirstFactors: string[]; config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse; staticFirstFactors: string[] | undefined; staticThirdPartyProviders: ProviderInput[] }

    Type declaration

    • allAvailableFirstFactors: string[]
    • config: TypeNormalisedInput
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    • staticFirstFactors: string[] | undefined
    • staticThirdPartyProviders: ProviderInput[]
    RecipeInterface: { associateUserToTenant: any; createOrUpdateTenant: any; createOrUpdateThirdPartyConfig: any; deleteTenant: any; deleteThirdPartyConfig: any; disassociateUserFromTenant: any; getTenant: any; getTenantId: any; listAllTenants: any }

    Type declaration

    • associateUserToTenant:function
      • associateUserToTenant(input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
      • Parameters

        • input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }
          • recipeUserId: RecipeUserId
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

    • createOrUpdateTenant:function
      • createOrUpdateTenant(input: { config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }; tenantId: string; userContext: UserContext }): Promise<{ createdNew: boolean; status: "OK" }>
      • Parameters

        • input: { config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }; tenantId: string; userContext: UserContext }
          • Optional config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
            • Optional coreConfig?: {}
              • [key: string]: any
            • Optional emailPasswordEnabled?: boolean
            • Optional firstFactors?: string[]
            • Optional passwordlessEnabled?: boolean
            • Optional requiredSecondaryFactors?: string[]
            • Optional thirdPartyEnabled?: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ createdNew: boolean; status: "OK" }>

    • createOrUpdateThirdPartyConfig:function
      • createOrUpdateThirdPartyConfig(input: { config: ProviderConfig; skipValidation?: boolean; tenantId: string; userContext: UserContext }): Promise<{ createdNew: boolean; status: "OK" }>
      • Parameters

        • input: { config: ProviderConfig; skipValidation?: boolean; tenantId: string; userContext: UserContext }
          • config: ProviderConfig
          • Optional skipValidation?: boolean
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ createdNew: boolean; status: "OK" }>

    • deleteTenant:function
      • deleteTenant(input: { tenantId: string; userContext: UserContext }): Promise<{ didExist: boolean; status: "OK" }>
      • Parameters

        • input: { tenantId: string; userContext: UserContext }
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ didExist: boolean; status: "OK" }>

    • deleteThirdPartyConfig:function
      • deleteThirdPartyConfig(input: { tenantId: string; thirdPartyId: string; userContext: UserContext }): Promise<{ didConfigExist: boolean; status: "OK" }>
      • Parameters

        • input: { tenantId: string; thirdPartyId: string; userContext: UserContext }
          • tenantId: string
          • thirdPartyId: string
          • userContext: UserContext

        Returns Promise<{ didConfigExist: boolean; status: "OK" }>

    • disassociateUserFromTenant:function
      • disassociateUserFromTenant(input: { recipeUserId: RecipeUserId; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; wasAssociated: boolean }>
    • getTenant:function
      • getTenant(input: { tenantId: string; userContext: UserContext }): Promise<undefined | { status: "OK" } & TenantConfig>
      • Parameters

        • input: { tenantId: string; userContext: UserContext }
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | { status: "OK" } & TenantConfig>

    • getTenantId:function
      • getTenantId(input: { tenantIdFromFrontend: string; userContext: UserContext }): Promise<string>
      • Parameters

        • input: { tenantIdFromFrontend: string; userContext: UserContext }
          • tenantIdFromFrontend: string
          • userContext: UserContext

        Returns Promise<string>

    • listAllTenants:function
      • listAllTenants(input: { userContext: UserContext }): Promise<{ status: "OK"; tenants: (TenantConfig & { tenantId: string })[] }>
      • Parameters

        • input: { userContext: UserContext }
          • userContext: UserContext

        Returns Promise<{ status: "OK"; tenants: (TenantConfig & { tenantId: string })[] }>

    Variables

    AllowedDomainsClaim: AllowedDomainsClaimClass = ...

    Functions

    • associateUserToTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>
    • Parameters

      • tenantId: string
      • recipeUserId: RecipeUserId
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyAssociated: boolean } | { status: "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" | "THIRD_PARTY_USER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "ASSOCIATION_NOT_ALLOWED_ERROR" }>

    • createOrUpdateTenant(tenantId: string, config?: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • Optional config: { coreConfig?: {}; emailPasswordEnabled?: boolean; firstFactors?: string[]; passwordlessEnabled?: boolean; requiredSecondaryFactors?: string[]; thirdPartyEnabled?: boolean }
        • Optional coreConfig?: {}
          • [key: string]: any
        • Optional emailPasswordEnabled?: boolean
        • Optional firstFactors?: string[]
        • Optional passwordlessEnabled?: boolean
        • Optional requiredSecondaryFactors?: string[]
        • Optional thirdPartyEnabled?: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNew: boolean; status: "OK" }>

    • createOrUpdateThirdPartyConfig(tenantId: string, config: ProviderConfig, skipValidation?: boolean, userContext?: Record<string, any>): Promise<{ createdNew: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • config: ProviderConfig
      • Optional skipValidation: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNew: boolean; status: "OK" }>

    • deleteTenant(tenantId: string, userContext?: Record<string, any>): Promise<{ didExist: boolean; status: "OK" }>
    • deleteThirdPartyConfig(tenantId: string, thirdPartyId: string, userContext?: Record<string, any>): Promise<{ didConfigExist: boolean; status: "OK" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didConfigExist: boolean; status: "OK" }>

    • disassociateUserFromTenant(tenantId: string, recipeUserId: RecipeUserId, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAssociated: boolean }>
    • getTenant(tenantId: string, userContext?: Record<string, any>): Promise<undefined | { status: "OK" } & TenantConfig>
    • init(config?: TypeInput): RecipeListFunction
    • listAllTenants(userContext?: Record<string, any>): Promise<{ status: "OK"; tenants: ({ tenantId: string } & TenantConfig)[] }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_openid.html b/docs/modules/recipe_openid.html index 51d77d9be..10eb335f0 100644 --- a/docs/modules/recipe_openid.html +++ b/docs/modules/recipe_openid.html @@ -1 +1 @@ -recipe/openid | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/openid

    Index

    Functions

    • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
    • Parameters

      • Optional payload: any
      • Optional validitySeconds: number
      • Optional useStaticSigningKey: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
    • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
    • Parameters

      • Optional userContext: Record<string, any>

      Returns Promise<{ issuer: string; jwks_uri: string; status: "OK" }>

    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/openid | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/openid

    Index

    Functions

    • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
    • Parameters

      • Optional payload: any
      • Optional validitySeconds: number
      • Optional useStaticSigningKey: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[]; validityInSeconds?: number }>
    • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
    • Parameters

      • Optional userContext: Record<string, any>

      Returns Promise<{ issuer: string; jwks_uri: string; status: "OK" }>

    • init(config?: TypeInput): RecipeListFunction

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_passwordless.html b/docs/modules/recipe_passwordless.html index a1e685692..68ae1cab6 100644 --- a/docs/modules/recipe_passwordless.html +++ b/docs/modules/recipe_passwordless.html @@ -1 +1 @@ -recipe/passwordless | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/passwordless

    Index

    Type Aliases

    APIInterface: { consumeCodePOST?: any; createCodePOST?: any; emailExistsGET?: any; phoneNumberExistsGET?: any; resendCodePOST?: any }

    Type declaration

    • consumeCodePOST?:function
      • consumeCodePOST(input: { deviceId: string; preAuthSessionId: string; userInputCode: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { linkCode: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { createdNewRecipeUser: boolean; session: SessionContainer; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; userInputCode: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { linkCode: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<GeneralErrorResponse | { createdNewRecipeUser: boolean; session: SessionContainer; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • createCodePOST?:function
      • createCodePOST(input: { email: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { phoneNumber: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { deviceId: string; flowType: "USER_INPUT_CODE" | "MAGIC_LINK" | "USER_INPUT_CODE_AND_MAGIC_LINK"; preAuthSessionId: string; status: "OK" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
      • Parameters

        • input: { email: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { phoneNumber: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<GeneralErrorResponse | { deviceId: string; flowType: "USER_INPUT_CODE" | "MAGIC_LINK" | "USER_INPUT_CODE_AND_MAGIC_LINK"; preAuthSessionId: string; status: "OK" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • emailExistsGET?:function
      • emailExistsGET(input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>
    • phoneNumberExistsGET?:function
      • phoneNumberExistsGET(input: { options: APIOptions; phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>
      • Parameters

        • input: { options: APIOptions; phoneNumber: string; tenantId: string; userContext: UserContext }
          • options: APIOptions
          • phoneNumber: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>

    • resendCodePOST?:function
      • resendCodePOST(input: { deviceId: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { status: "RESTART_FLOW_ERROR" | "OK" }>
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypePasswordlessEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse; smsDelivery: default<TypePasswordlessSmsDeliveryInput> }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • emailDelivery: default<TypePasswordlessEmailDeliveryInput>
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    • smsDelivery: default<TypePasswordlessSmsDeliveryInput>
    RecipeInterface: { checkCode: any; consumeCode: any; createCode: any; createNewCodeForDevice: any; listCodesByDeviceId: any; listCodesByEmail: any; listCodesByPhoneNumber: any; listCodesByPreAuthSessionId: any; revokeAllCodes: any; revokeCode: any; updateUser: any }

    Type declaration

    • checkCode:function
      • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode:function
      • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • createCode:function
      • createCode(input: { email: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string } & { phoneNumber: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
      • Parameters

        • input: { email: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string } & { phoneNumber: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string }

        Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

    • createNewCodeForDevice:function
      • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext: UserContext; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
      • Parameters

        • input: { deviceId: string; tenantId: string; userContext: UserContext; userInputCode?: string }
          • deviceId: string
          • tenantId: string
          • userContext: UserContext
          • Optional userInputCode?: string

        Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

    • listCodesByDeviceId:function
      • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext: UserContext }): Promise<undefined | DeviceType>
      • Parameters

        • input: { deviceId: string; tenantId: string; userContext: UserContext }
          • deviceId: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | DeviceType>

    • listCodesByEmail:function
      • listCodesByEmail(input: { email: string; tenantId: string; userContext: UserContext }): Promise<DeviceType[]>
      • Parameters

        • input: { email: string; tenantId: string; userContext: UserContext }
          • email: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<DeviceType[]>

    • listCodesByPhoneNumber:function
      • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<DeviceType[]>
      • Parameters

        • input: { phoneNumber: string; tenantId: string; userContext: UserContext }
          • phoneNumber: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<DeviceType[]>

    • listCodesByPreAuthSessionId:function
      • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<undefined | DeviceType>
      • Parameters

        • input: { preAuthSessionId: string; tenantId: string; userContext: UserContext }
          • preAuthSessionId: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | DeviceType>

    • revokeAllCodes:function
      • revokeAllCodes(input: { email: string; tenantId: string; userContext: UserContext } | { phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
      • Parameters

        • input: { email: string; tenantId: string; userContext: UserContext } | { phoneNumber: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ status: "OK" }>

    • revokeCode:function
      • revokeCode(input: { codeId: string; tenantId: string; userContext: UserContext } | { preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
      • Parameters

        • input: { codeId: string; tenantId: string; userContext: UserContext } | { preAuthSessionId: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ status: "OK" }>

    • updateUser:function
      • updateUser(input: { email?: string | null; phoneNumber?: string | null; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
      • Parameters

        • input: { email?: string | null; phoneNumber?: string | null; recipeUserId: RecipeUserId; userContext: UserContext }
          • Optional email?: string | null
          • Optional phoneNumber?: string | null
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

    Variables

    Error: typeof default = Wrapper.Error

    Functions

    • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • createCode(input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
    • Parameters

      • input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

    • createMagicLink(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<string>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<string>

    • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>
        • Optional userInputCode?: string

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

    • init(config: TypeInput): RecipeListFunction
    • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • listCodesByEmail(input: { email: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> }
        • email: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }
        • phoneNumber: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }
        • preAuthSessionId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • revokeAllCodes(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • revokeCode(input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • sendEmail(input: TypePasswordlessEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendSms(input: TypePasswordlessSmsDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • signInUp(input: { email: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: string; user: User }>
    • updateUser(input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
    • Parameters

      • input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }
        • Optional email?: null | string
        • Optional phoneNumber?: null | string
        • recipeUserId: RecipeUserId
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/passwordless | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/passwordless

    Index

    Type Aliases

    APIInterface: { consumeCodePOST?: any; createCodePOST?: any; emailExistsGET?: any; phoneNumberExistsGET?: any; resendCodePOST?: any }

    Type declaration

    • consumeCodePOST?:function
      • consumeCodePOST(input: { deviceId: string; preAuthSessionId: string; userInputCode: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { linkCode: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { createdNewRecipeUser: boolean; session: SessionContainer; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; userInputCode: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { linkCode: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<GeneralErrorResponse | { createdNewRecipeUser: boolean; session: SessionContainer; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • createCodePOST?:function
      • createCodePOST(input: { email: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { phoneNumber: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { deviceId: string; flowType: "USER_INPUT_CODE" | "MAGIC_LINK" | "USER_INPUT_CODE_AND_MAGIC_LINK"; preAuthSessionId: string; status: "OK" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
      • Parameters

        • input: { email: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & { phoneNumber: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<GeneralErrorResponse | { deviceId: string; flowType: "USER_INPUT_CODE" | "MAGIC_LINK" | "USER_INPUT_CODE_AND_MAGIC_LINK"; preAuthSessionId: string; status: "OK" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • emailExistsGET?:function
      • emailExistsGET(input: { email: string; options: APIOptions; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>
    • phoneNumberExistsGET?:function
      • phoneNumberExistsGET(input: { options: APIOptions; phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>
      • Parameters

        • input: { options: APIOptions; phoneNumber: string; tenantId: string; userContext: UserContext }
          • options: APIOptions
          • phoneNumber: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<GeneralErrorResponse | { exists: boolean; status: "OK" }>

    • resendCodePOST?:function
      • resendCodePOST(input: { deviceId: string; preAuthSessionId: string } & { options: APIOptions; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<GeneralErrorResponse | { status: "RESTART_FLOW_ERROR" | "OK" }>
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; emailDelivery: default<TypePasswordlessEmailDeliveryInput>; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse; smsDelivery: default<TypePasswordlessSmsDeliveryInput> }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • emailDelivery: default<TypePasswordlessEmailDeliveryInput>
    • isInServerlessEnv: boolean
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    • smsDelivery: default<TypePasswordlessSmsDeliveryInput>
    RecipeInterface: { checkCode: any; consumeCode: any; createCode: any; createNewCodeForDevice: any; listCodesByDeviceId: any; listCodesByEmail: any; listCodesByPhoneNumber: any; listCodesByPreAuthSessionId: any; revokeAllCodes: any; revokeCode: any; updateUser: any }

    Type declaration

    • checkCode:function
      • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode:function
      • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer | undefined; tenantId: string; userContext: UserContext }

        Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • createCode:function
      • createCode(input: { email: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string } & { phoneNumber: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
      • Parameters

        • input: { email: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string } & { phoneNumber: string } & { session: SessionContainer | undefined; tenantId: string; userContext: UserContext; userInputCode?: string }

        Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

    • createNewCodeForDevice:function
      • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext: UserContext; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
      • Parameters

        • input: { deviceId: string; tenantId: string; userContext: UserContext; userInputCode?: string }
          • deviceId: string
          • tenantId: string
          • userContext: UserContext
          • Optional userInputCode?: string

        Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

    • listCodesByDeviceId:function
      • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext: UserContext }): Promise<undefined | DeviceType>
      • Parameters

        • input: { deviceId: string; tenantId: string; userContext: UserContext }
          • deviceId: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | DeviceType>

    • listCodesByEmail:function
      • listCodesByEmail(input: { email: string; tenantId: string; userContext: UserContext }): Promise<DeviceType[]>
      • Parameters

        • input: { email: string; tenantId: string; userContext: UserContext }
          • email: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<DeviceType[]>

    • listCodesByPhoneNumber:function
      • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<DeviceType[]>
      • Parameters

        • input: { phoneNumber: string; tenantId: string; userContext: UserContext }
          • phoneNumber: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<DeviceType[]>

    • listCodesByPreAuthSessionId:function
      • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<undefined | DeviceType>
      • Parameters

        • input: { preAuthSessionId: string; tenantId: string; userContext: UserContext }
          • preAuthSessionId: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<undefined | DeviceType>

    • revokeAllCodes:function
      • revokeAllCodes(input: { email: string; tenantId: string; userContext: UserContext } | { phoneNumber: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
      • Parameters

        • input: { email: string; tenantId: string; userContext: UserContext } | { phoneNumber: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ status: "OK" }>

    • revokeCode:function
      • revokeCode(input: { codeId: string; tenantId: string; userContext: UserContext } | { preAuthSessionId: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK" }>
      • Parameters

        • input: { codeId: string; tenantId: string; userContext: UserContext } | { preAuthSessionId: string; tenantId: string; userContext: UserContext }

        Returns Promise<{ status: "OK" }>

    • updateUser:function
      • updateUser(input: { email?: string | null; phoneNumber?: string | null; recipeUserId: RecipeUserId; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
      • Parameters

        • input: { email?: string | null; phoneNumber?: string | null; recipeUserId: RecipeUserId; userContext: UserContext }
          • Optional email?: string | null
          • Optional phoneNumber?: string | null
          • recipeUserId: RecipeUserId
          • userContext: UserContext

        Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" | "EMAIL_ALREADY_EXISTS_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

    Variables

    Error: typeof default = Wrapper.Error

    Functions

    • checkCode(input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; status: "OK" } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>
    • consumeCode(input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session?: undefined; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" }>

    • Parameters

      • input: { deviceId: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode: string } | { linkCode: string; preAuthSessionId: string; session: SessionContainer; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ consumedDevice: { email?: string; failedCodeInputAttemptCount: number; phoneNumber?: string; preAuthSessionId: string }; createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { failedCodeInputAttemptCount: number; maximumCodeInputAttempts: number; status: "INCORRECT_USER_INPUT_CODE_ERROR" | "EXPIRED_USER_INPUT_CODE_ERROR" } | { status: "RESTART_FLOW_ERROR" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • createCode(input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>
    • Parameters

      • input: { email: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string } & { phoneNumber: string } & { session?: SessionContainer; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string }>

    • createMagicLink(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<string>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<string>

    • createNewCodeForDevice(input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }): Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any>; userInputCode?: string }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>
        • Optional userInputCode?: string

      Returns Promise<{ codeId: string; codeLifetime: number; deviceId: string; linkCode: string; preAuthSessionId: string; status: "OK"; timeCreated: number; userInputCode: string } | { status: "RESTART_FLOW_ERROR" | "USER_INPUT_CODE_ALREADY_USED_ERROR" }>

    • init(config: TypeInput): RecipeListFunction
    • listCodesByDeviceId(input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { deviceId: string; tenantId: string; userContext?: Record<string, any> }
        • deviceId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • listCodesByEmail(input: { email: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> }
        • email: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPhoneNumber(input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<DeviceType[]>
    • Parameters

      • input: { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }
        • phoneNumber: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<DeviceType[]>

    • listCodesByPreAuthSessionId(input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<undefined | DeviceType>
    • Parameters

      • input: { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }
        • preAuthSessionId: string
        • tenantId: string
        • Optional userContext?: Record<string, any>

      Returns Promise<undefined | DeviceType>

    • revokeAllCodes(input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { email: string; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • revokeCode(input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }): Promise<{ status: "OK" }>
    • Parameters

      • input: { codeId: string; tenantId: string; userContext?: Record<string, any> } | { preAuthSessionId: string; tenantId: string; userContext?: Record<string, any> }

      Returns Promise<{ status: "OK" }>

    • sendEmail(input: TypePasswordlessEmailDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • sendSms(input: TypePasswordlessSmsDeliveryInput & { userContext?: Record<string, any> }): Promise<void>
    • signInUp(input: { email: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> } | { phoneNumber: string; session?: SessionContainer; tenantId: string; userContext?: Record<string, any> }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: string; user: User }>
    • updateUser(input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }): Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>
    • Parameters

      • input: { email?: null | string; phoneNumber?: null | string; recipeUserId: RecipeUserId; userContext?: Record<string, any> }
        • Optional email?: null | string
        • Optional phoneNumber?: null | string
        • recipeUserId: RecipeUserId
        • Optional userContext?: Record<string, any>

      Returns Promise<{ status: "OK" | "EMAIL_ALREADY_EXISTS_ERROR" | "UNKNOWN_USER_ID_ERROR" | "PHONE_NUMBER_ALREADY_EXISTS_ERROR" } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" | "PHONE_NUMBER_CHANGE_NOT_ALLOWED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_session.html b/docs/modules/recipe_session.html index c661a3139..dc35823bd 100644 --- a/docs/modules/recipe_session.html +++ b/docs/modules/recipe_session.html @@ -1,13 +1,13 @@ -recipe/session | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/session

    Index

    Type Aliases

    APIInterface: { refreshPOST: undefined | ((input: { options: APIOptions; userContext: UserContext }) => Promise<SessionContainer>); signOutPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ status: "OK" } | GeneralErrorResponse>); verifySession: any }

    Type declaration

  • mergeIntoAccessTokenPayload:function
    • mergeIntoAccessTokenPayload(input: { accessTokenPayloadUpdate: JSONObject; sessionHandle: string; userContext: UserContext }): Promise<boolean>
  • refreshSession:function
    • refreshSession(input: { antiCsrfToken?: string; disableAntiCsrf: boolean; refreshToken: string; userContext: UserContext }): Promise<SessionContainer>
    • Parameters

      • input: { antiCsrfToken?: string; disableAntiCsrf: boolean; refreshToken: string; userContext: UserContext }
        • Optional antiCsrfToken?: string
        • disableAntiCsrf: boolean
        • refreshToken: string
        • userContext: UserContext

      Returns Promise<SessionContainer>

  • regenerateAccessToken:function
    • regenerateAccessToken(input: { accessToken: string; newAccessTokenPayload?: any; userContext: UserContext }): Promise<undefined | { accessToken?: { createdTime: number; expiry: number; token: string }; session: { handle: string; recipeUserId: RecipeUserId; tenantId: string; userDataInJWT: any; userId: string }; status: "OK" }>
    • Parameters

      • input: { accessToken: string; newAccessTokenPayload?: any; userContext: UserContext }
        • accessToken: string
        • Optional newAccessTokenPayload?: any
        • userContext: UserContext

      Returns Promise<undefined | { accessToken?: { createdTime: number; expiry: number; token: string }; session: { handle: string; recipeUserId: RecipeUserId; tenantId: string; userDataInJWT: any; userId: string }; status: "OK" }>

      Returns false if the sessionHandle does not exist

      +
  • removeClaim:function
    • removeClaim(input: { claim: SessionClaim<any>; sessionHandle: string; userContext: UserContext }): Promise<boolean>
    • Parameters

      • input: { claim: SessionClaim<any>; sessionHandle: string; userContext: UserContext }
        • claim: SessionClaim<any>
        • sessionHandle: string
        • userContext: UserContext

      Returns Promise<boolean>

  • revokeAllSessionsForUser:function
    • revokeAllSessionsForUser(input: { revokeAcrossAllTenants?: boolean; revokeSessionsForLinkedAccounts: boolean; tenantId: string; userContext: UserContext; userId: string }): Promise<string[]>
    • Parameters

      • input: { revokeAcrossAllTenants?: boolean; revokeSessionsForLinkedAccounts: boolean; tenantId: string; userContext: UserContext; userId: string }
        • Optional revokeAcrossAllTenants?: boolean
        • revokeSessionsForLinkedAccounts: boolean
        • tenantId: string
        • userContext: UserContext
        • userId: string

      Returns Promise<string[]>

  • revokeMultipleSessions:function
    • revokeMultipleSessions(input: { sessionHandles: string[]; userContext: UserContext }): Promise<string[]>
    • Parameters

      • input: { sessionHandles: string[]; userContext: UserContext }
        • sessionHandles: string[]
        • userContext: UserContext

      Returns Promise<string[]>

  • revokeSession:function
    • revokeSession(input: { sessionHandle: string; userContext: UserContext }): Promise<boolean>
    • Parameters

      • input: { sessionHandle: string; userContext: UserContext }
        • sessionHandle: string
        • userContext: UserContext

      Returns Promise<boolean>

  • setClaimValue:function
    • setClaimValue<T>(input: { claim: SessionClaim<T>; sessionHandle: string; userContext: UserContext; value: T }): Promise<boolean>
    • Type Parameters

      • T

      Parameters

      • input: { claim: SessionClaim<T>; sessionHandle: string; userContext: UserContext; value: T }
        • claim: SessionClaim<T>
        • sessionHandle: string
        • userContext: UserContext
        • value: T

      Returns Promise<boolean>

  • updateSessionDataInDatabase:function
    • updateSessionDataInDatabase(input: { newSessionData: any; sessionHandle: string; userContext: UserContext }): Promise<boolean>
    • Parameters

      • input: { newSessionData: any; sessionHandle: string; userContext: UserContext }
        • newSessionData: any
        • sessionHandle: string
        • userContext: UserContext

      Returns Promise<boolean>

  • validateClaims:function
    • validateClaims(input: { accessTokenPayload: any; claimValidators: SessionClaimValidator[]; recipeUserId: RecipeUserId; userContext: UserContext; userId: string }): Promise<{ accessTokenPayloadUpdate?: any; invalidClaims: ClaimValidationError[] }>
  • SessionClaimValidator: ({ claim: SessionClaim<any>; shouldRefetch: any } | {}) & { id: string; validate: any }
    SessionInformation: { customClaimsInAccessTokenPayload: any; expiry: number; recipeUserId: RecipeUserId; sessionDataInDatabase: any; sessionHandle: string; tenantId: string; timeCreated: number; userId: string }

    Type declaration

    • customClaimsInAccessTokenPayload: any
    • expiry: number
    • recipeUserId: RecipeUserId
    • sessionDataInDatabase: any
    • sessionHandle: string
    • tenantId: string
    • timeCreated: number
    • userId: string

    Variables

    Error: typeof default = SessionWrapper.Error

    Functions

    • createJWT(payload?: any, validitySeconds?: number, useStaticSigningKey?: boolean, userContext?: Record<string, any>): Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>
    • Parameters

      • Optional payload: any
      • Optional validitySeconds: number
      • Optional useStaticSigningKey: boolean
      • Optional userContext: Record<string, any>

      Returns Promise<{ jwt: string; status: "OK" } | { status: "UNSUPPORTED_ALGORITHM_ERROR" }>

    • createNewSession(req: any, res: any, tenantId: string, recipeUserId: RecipeUserId, accessTokenPayload?: any, sessionDataInDatabase?: any, userContext?: Record<string, any>): Promise<SessionContainer>
    • createNewSessionWithoutRequestResponse(tenantId: string, recipeUserId: RecipeUserId, accessTokenPayload?: any, sessionDataInDatabase?: any, disableAntiCsrf?: boolean, userContext?: Record<string, any>): Promise<SessionContainer>
    • fetchAndSetClaim(sessionHandle: string, claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<boolean>
    • getAllSessionHandlesForUser(userId: string, fetchSessionsForAllLinkedAccounts?: boolean, tenantId?: string, userContext?: Record<string, any>): Promise<string[]>
    • Parameters

      • userId: string
      • fetchSessionsForAllLinkedAccounts: boolean = true
      • Optional tenantId: string
      • Optional userContext: Record<string, any>

      Returns Promise<string[]>

    • getClaimValue<T>(sessionHandle: string, claim: SessionClaim<T>, userContext?: Record<string, any>): Promise<{ status: "SESSION_DOES_NOT_EXIST_ERROR" } | { status: "OK"; value: undefined | T }>
    • Type Parameters

      • T

      Parameters

      • sessionHandle: string
      • claim: SessionClaim<T>
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "SESSION_DOES_NOT_EXIST_ERROR" } | { status: "OK"; value: undefined | T }>

    • getJWKS(userContext?: Record<string, any>): Promise<{ keys: JsonWebKey[] }>
    • getOpenIdDiscoveryConfiguration(userContext?: Record<string, any>): Promise<{ issuer: string; jwks_uri: string; status: "OK" }>
    • getSessionInformation(sessionHandle: string, userContext?: Record<string, any>): Promise<undefined | SessionInformation>
    • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string): Promise<SessionContainer>
    • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions & { sessionRequired?: true }, userContext?: Record<string, any>): Promise<SessionContainer>
    • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions & { sessionRequired: false }, userContext?: Record<string, any>): Promise<undefined | SessionContainer>
    • getSessionWithoutRequestResponse(accessToken: string, antiCsrfToken?: string, options?: VerifySessionOptions, userContext?: Record<string, any>): Promise<undefined | SessionContainer>
    • init(config?: TypeInput): RecipeListFunction
    • mergeIntoAccessTokenPayload(sessionHandle: string, accessTokenPayloadUpdate: JSONObject, userContext?: Record<string, any>): Promise<boolean>
    • refreshSession(req: any, res: any, userContext?: Record<string, any>): Promise<SessionContainer>
    • refreshSessionWithoutRequestResponse(refreshToken: string, disableAntiCsrf?: boolean, antiCsrfToken?: string, userContext?: Record<string, any>): Promise<SessionContainer>
    • removeClaim(sessionHandle: string, claim: SessionClaim<any>, userContext?: Record<string, any>): Promise<boolean>
    • revokeAllSessionsForUser(userId: string, revokeSessionsForLinkedAccounts?: boolean, tenantId?: string, userContext?: Record<string, any>): Promise<string[]>
    • Parameters

      • userId: string
      • revokeSessionsForLinkedAccounts: boolean = true
      • Optional tenantId: string
      • Optional userContext: Record<string, any>

      Returns Promise<string[]>

    • revokeMultipleSessions(sessionHandles: string[], userContext?: Record<string, any>): Promise<string[]>
    • revokeSession(sessionHandle: string, userContext?: Record<string, any>): Promise<boolean>
    • setClaimValue<T>(sessionHandle: string, claim: SessionClaim<T>, value: T, userContext?: Record<string, any>): Promise<boolean>
    • Type Parameters

      • T

      Parameters

      • sessionHandle: string
      • claim: SessionClaim<T>
      • value: T
      • Optional userContext: Record<string, any>

      Returns Promise<boolean>

    • updateSessionDataInDatabase(sessionHandle: string, newSessionData: any, userContext?: Record<string, any>): Promise<boolean>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_thirdparty.html b/docs/modules/recipe_thirdparty.html index 3e88c9b3d..6c0437075 100644 --- a/docs/modules/recipe_thirdparty.html +++ b/docs/modules/recipe_thirdparty.html @@ -1 +1 @@ -recipe/thirdparty | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/thirdparty

    Index

    Type Aliases

    APIInterface: { appleRedirectHandlerPOST: undefined | ((input: { formPostInfoFromProvider: {}; options: APIOptions; userContext: UserContext }) => Promise<void>); authorisationUrlGET: undefined | ((input: { options: APIOptions; provider: TypeProvider; redirectURIOnProviderDashboard: string; tenantId: string; userContext: UserContext }) => Promise<{ pkceCodeVerifier?: string; status: "OK"; urlWithQueryParams: string } | GeneralErrorResponse>); signInUpPOST: undefined | ((input: { options: APIOptions; provider: TypeProvider; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & ({ redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any } } | { oAuthTokens: {} })) => Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer; status: "OK"; user: User } | { status: "NO_EMAIL_GIVEN_BY_PROVIDER" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | GeneralErrorResponse>) }

    Type declaration

    • appleRedirectHandlerPOST: undefined | ((input: { formPostInfoFromProvider: {}; options: APIOptions; userContext: UserContext }) => Promise<void>)
    • authorisationUrlGET: undefined | ((input: { options: APIOptions; provider: TypeProvider; redirectURIOnProviderDashboard: string; tenantId: string; userContext: UserContext }) => Promise<{ pkceCodeVerifier?: string; status: "OK"; urlWithQueryParams: string } | GeneralErrorResponse>)
    • signInUpPOST: undefined | ((input: { options: APIOptions; provider: TypeProvider; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & ({ redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any } } | { oAuthTokens: {} })) => Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer; status: "OK"; user: User } | { status: "NO_EMAIL_GIVEN_BY_PROVIDER" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | GeneralErrorResponse>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; isInServerlessEnv: boolean; providers: ProviderInput[]; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • isInServerlessEnv: boolean
    • providers: ProviderInput[]
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    RecipeInterface: { getProvider: any; manuallyCreateOrUpdateUser: any; signInUp: any }

    Type declaration

    • getProvider:function
      • getProvider(input: { clientType?: string; tenantId: string; thirdPartyId: string; userContext: UserContext }): Promise<undefined | TypeProvider>
      • Parameters

        • input: { clientType?: string; tenantId: string; thirdPartyId: string; userContext: UserContext }
          • Optional clientType?: string
          • tenantId: string
          • thirdPartyId: string
          • userContext: UserContext

        Returns Promise<undefined | TypeProvider>

    • manuallyCreateOrUpdateUser:function
      • manuallyCreateOrUpdateUser(input: { email: string; isVerified: boolean; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; isVerified: boolean; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }
          • email: string
          • isVerified: boolean
          • session: SessionContainer | undefined
          • tenantId: string
          • thirdPartyId: string
          • thirdPartyUserId: string
          • userContext: UserContext

        Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • signInUp:function
      • signInUp(input: { email: string; isVerified: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }): Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; isVerified: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }
          • email: string
          • isVerified: boolean
          • oAuthTokens: {}
            • [key: string]: any
          • rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }
            • Optional fromIdTokenPayload?: {}
              • [key: string]: any
            • Optional fromUserInfoAPI?: {}
              • [key: string]: any
          • session: SessionContainer | undefined
          • tenantId: string
          • thirdPartyId: string
          • thirdPartyUserId: string
          • userContext: UserContext

        Returns Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    TypeProvider: { config: ProviderConfigForClientType; id: string; exchangeAuthCodeForOAuthTokens: any; getAuthorisationRedirectURL: any; getConfigForClientType: any; getUserInfo: any }

    Type declaration

    • config: ProviderConfigForClientType
    • id: string
    • exchangeAuthCodeForOAuthTokens:function
      • exchangeAuthCodeForOAuthTokens(input: { redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }; userContext: UserContext }): Promise<any>
      • Parameters

        • input: { redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }; userContext: UserContext }
          • redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }
            • Optional pkceCodeVerifier?: string
            • redirectURIOnProviderDashboard: string
            • redirectURIQueryParams: any
          • userContext: UserContext

        Returns Promise<any>

    • getAuthorisationRedirectURL:function
      • getAuthorisationRedirectURL(input: { redirectURIOnProviderDashboard: string; userContext: UserContext }): Promise<{ pkceCodeVerifier?: string; urlWithQueryParams: string }>
      • Parameters

        • input: { redirectURIOnProviderDashboard: string; userContext: UserContext }
          • redirectURIOnProviderDashboard: string
          • userContext: UserContext

        Returns Promise<{ pkceCodeVerifier?: string; urlWithQueryParams: string }>

    • getConfigForClientType:function
      • getConfigForClientType(input: { clientType?: string; userContext: UserContext }): Promise<ProviderConfigForClientType>
      • Parameters

        • input: { clientType?: string; userContext: UserContext }
          • Optional clientType?: string
          • userContext: UserContext

        Returns Promise<ProviderConfigForClientType>

    • getUserInfo:function
      • getUserInfo(input: { oAuthTokens: any; userContext: UserContext }): Promise<UserInfo>

    Variables

    Error: typeof default = Wrapper.Error

    Functions

    • getProvider(tenantId: string, thirdPartyId: string, clientType: undefined | string, userContext?: Record<string, any>): Promise<undefined | TypeProvider>
    • init(config?: TypeInput): RecipeListFunction
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session?: undefined, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session: SessionContainer, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/thirdparty | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/thirdparty

    Index

    Type Aliases

    APIInterface: { appleRedirectHandlerPOST: undefined | ((input: { formPostInfoFromProvider: {}; options: APIOptions; userContext: UserContext }) => Promise<void>); authorisationUrlGET: undefined | ((input: { options: APIOptions; provider: TypeProvider; redirectURIOnProviderDashboard: string; tenantId: string; userContext: UserContext }) => Promise<{ pkceCodeVerifier?: string; status: "OK"; urlWithQueryParams: string } | GeneralErrorResponse>); signInUpPOST: undefined | ((input: { options: APIOptions; provider: TypeProvider; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & ({ redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any } } | { oAuthTokens: {} })) => Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer; status: "OK"; user: User } | { status: "NO_EMAIL_GIVEN_BY_PROVIDER" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | GeneralErrorResponse>) }

    Type declaration

    • appleRedirectHandlerPOST: undefined | ((input: { formPostInfoFromProvider: {}; options: APIOptions; userContext: UserContext }) => Promise<void>)
    • authorisationUrlGET: undefined | ((input: { options: APIOptions; provider: TypeProvider; redirectURIOnProviderDashboard: string; tenantId: string; userContext: UserContext }) => Promise<{ pkceCodeVerifier?: string; status: "OK"; urlWithQueryParams: string } | GeneralErrorResponse>)
    • signInUpPOST: undefined | ((input: { options: APIOptions; provider: TypeProvider; session: SessionContainer | undefined; tenantId: string; userContext: UserContext } & ({ redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any } } | { oAuthTokens: {} })) => Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer; status: "OK"; user: User } | { status: "NO_EMAIL_GIVEN_BY_PROVIDER" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | GeneralErrorResponse>)
    APIOptions: { appInfo: NormalisedAppinfo; config: TypeNormalisedInput; isInServerlessEnv: boolean; providers: ProviderInput[]; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    • appInfo: NormalisedAppinfo
    • config: TypeNormalisedInput
    • isInServerlessEnv: boolean
    • providers: ProviderInput[]
    • recipeId: string
    • recipeImplementation: RecipeInterface
    • req: BaseRequest
    • res: BaseResponse
    RecipeInterface: { getProvider: any; manuallyCreateOrUpdateUser: any; signInUp: any }

    Type declaration

    • getProvider:function
      • getProvider(input: { clientType?: string; tenantId: string; thirdPartyId: string; userContext: UserContext }): Promise<undefined | TypeProvider>
      • Parameters

        • input: { clientType?: string; tenantId: string; thirdPartyId: string; userContext: UserContext }
          • Optional clientType?: string
          • tenantId: string
          • thirdPartyId: string
          • userContext: UserContext

        Returns Promise<undefined | TypeProvider>

    • manuallyCreateOrUpdateUser:function
      • manuallyCreateOrUpdateUser(input: { email: string; isVerified: boolean; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; isVerified: boolean; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }
          • email: string
          • isVerified: boolean
          • session: SessionContainer | undefined
          • tenantId: string
          • thirdPartyId: string
          • thirdPartyUserId: string
          • userContext: UserContext

        Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    • signInUp:function
      • signInUp(input: { email: string; isVerified: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }): Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
      • Parameters

        • input: { email: string; isVerified: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; session: SessionContainer | undefined; tenantId: string; thirdPartyId: string; thirdPartyUserId: string; userContext: UserContext }
          • email: string
          • isVerified: boolean
          • oAuthTokens: {}
            • [key: string]: any
          • rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }
            • Optional fromIdTokenPayload?: {}
              • [key: string]: any
            • Optional fromUserInfoAPI?: {}
              • [key: string]: any
          • session: SessionContainer | undefined
          • tenantId: string
          • thirdPartyId: string
          • thirdPartyUserId: string
          • userContext: UserContext

        Returns Promise<{ createdNewRecipeUser: boolean; oAuthTokens: {}; rawUserInfoFromProvider: { fromIdTokenPayload?: {}; fromUserInfoAPI?: {} }; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "EMAIL_VERIFICATION_REQUIRED" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    TypeProvider: { config: ProviderConfigForClientType; id: string; exchangeAuthCodeForOAuthTokens: any; getAuthorisationRedirectURL: any; getConfigForClientType: any; getUserInfo: any }

    Type declaration

    • config: ProviderConfigForClientType
    • id: string
    • exchangeAuthCodeForOAuthTokens:function
      • exchangeAuthCodeForOAuthTokens(input: { redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }; userContext: UserContext }): Promise<any>
      • Parameters

        • input: { redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }; userContext: UserContext }
          • redirectURIInfo: { pkceCodeVerifier?: string; redirectURIOnProviderDashboard: string; redirectURIQueryParams: any }
            • Optional pkceCodeVerifier?: string
            • redirectURIOnProviderDashboard: string
            • redirectURIQueryParams: any
          • userContext: UserContext

        Returns Promise<any>

    • getAuthorisationRedirectURL:function
      • getAuthorisationRedirectURL(input: { redirectURIOnProviderDashboard: string; userContext: UserContext }): Promise<{ pkceCodeVerifier?: string; urlWithQueryParams: string }>
      • Parameters

        • input: { redirectURIOnProviderDashboard: string; userContext: UserContext }
          • redirectURIOnProviderDashboard: string
          • userContext: UserContext

        Returns Promise<{ pkceCodeVerifier?: string; urlWithQueryParams: string }>

    • getConfigForClientType:function
      • getConfigForClientType(input: { clientType?: string; userContext: UserContext }): Promise<ProviderConfigForClientType>
      • Parameters

        • input: { clientType?: string; userContext: UserContext }
          • Optional clientType?: string
          • userContext: UserContext

        Returns Promise<ProviderConfigForClientType>

    • getUserInfo:function
      • getUserInfo(input: { oAuthTokens: any; userContext: UserContext }): Promise<UserInfo>

    Variables

    Error: typeof default = Wrapper.Error

    Functions

    • getProvider(tenantId: string, thirdPartyId: string, clientType: undefined | string, userContext?: Record<string, any>): Promise<undefined | TypeProvider>
    • init(config?: TypeInput): RecipeListFunction
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session?: undefined, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>
    • manuallyCreateOrUpdateUser(tenantId: string, thirdPartyId: string, thirdPartyUserId: string, email: string, isVerified: boolean, session: SessionContainer, userContext?: Record<string, any>): Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>
    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • Optional session: undefined
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" }>

    • Parameters

      • tenantId: string
      • thirdPartyId: string
      • thirdPartyUserId: string
      • email: string
      • isVerified: boolean
      • session: SessionContainer
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRecipeUser: boolean; recipeUserId: RecipeUserId; status: "OK"; user: User } | { reason: string; status: "EMAIL_CHANGE_NOT_ALLOWED_ERROR" } | { reason: string; status: "SIGN_IN_UP_NOT_ALLOWED" } | { reason: "ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "RECIPE_USER_ID_ALREADY_LINKED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR" | "EMAIL_VERIFICATION_REQUIRED" | "SESSION_USER_ACCOUNT_INFO_ALREADY_ASSOCIATED_WITH_ANOTHER_PRIMARY_USER_ID_ERROR"; status: "LINKING_TO_SESSION_USER_FAILED" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_totp.html b/docs/modules/recipe_totp.html index 3e2c21b89..82339ee25 100644 --- a/docs/modules/recipe_totp.html +++ b/docs/modules/recipe_totp.html @@ -1 +1 @@ -recipe/totp | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/totp

    Index

    Type Aliases

    APIInterface: { createDevicePOST: undefined | ((input: { deviceName?: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>); listDevicesGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" } | GeneralErrorResponse>); removeDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ didDeviceExist: boolean; status: "OK" } | GeneralErrorResponse>); verifyDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>); verifyTOTPPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>) }

    Type declaration

    • createDevicePOST: undefined | ((input: { deviceName?: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>)
    • listDevicesGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" } | GeneralErrorResponse>)
    • removeDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ didDeviceExist: boolean; status: "OK" } | GeneralErrorResponse>)
    • verifyDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>)
    • verifyTOTPPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { createDevice: any; getUserIdentifierInfoForUserId: any; listDevices: any; removeDevice: any; updateDevice: any; verifyDevice: any; verifyTOTP: any }

    Type declaration

    • createDevice:function
      • createDevice(input: { deviceName?: string; period?: number; skew?: number; userContext: UserContext; userId: string; userIdentifierInfo?: string }): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
      • Parameters

        • input: { deviceName?: string; period?: number; skew?: number; userContext: UserContext; userId: string; userIdentifierInfo?: string }
          • Optional deviceName?: string
          • Optional period?: number
          • Optional skew?: number
          • userContext: UserContext
          • userId: string
          • Optional userIdentifierInfo?: string

        Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • getUserIdentifierInfoForUserId:function
      • getUserIdentifierInfoForUserId(input: { userContext: UserContext; userId: string }): Promise<{ info: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" | "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" }>
      • Parameters

        • input: { userContext: UserContext; userId: string }
          • userContext: UserContext
          • userId: string

        Returns Promise<{ info: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" | "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" }>

    • listDevices:function
      • listDevices(input: { userContext: UserContext; userId: string }): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
      • Parameters

        • input: { userContext: UserContext; userId: string }
          • userContext: UserContext
          • userId: string

        Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice:function
      • removeDevice(input: { deviceName: string; userContext: UserContext; userId: string }): Promise<{ didDeviceExist: boolean; status: "OK" }>
      • Parameters

        • input: { deviceName: string; userContext: UserContext; userId: string }
          • deviceName: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice:function
      • updateDevice(input: { existingDeviceName: string; newDeviceName: string; userContext: UserContext; userId: string }): Promise<{ status: "OK" | "UNKNOWN_DEVICE_ERROR" | "DEVICE_ALREADY_EXISTS_ERROR" }>
      • Parameters

        • input: { existingDeviceName: string; newDeviceName: string; userContext: UserContext; userId: string }
          • existingDeviceName: string
          • newDeviceName: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" | "UNKNOWN_DEVICE_ERROR" | "DEVICE_ALREADY_EXISTS_ERROR" }>

    • verifyDevice:function
      • verifyDevice(input: { deviceName: string; tenantId: string; totp: string; userContext: UserContext; userId: string }): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
      • Parameters

        • input: { deviceName: string; tenantId: string; totp: string; userContext: UserContext; userId: string }
          • deviceName: string
          • tenantId: string
          • totp: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP:function
      • verifyTOTP(input: { tenantId: string; totp: string; userContext: UserContext; userId: string }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
      • Parameters

        • input: { tenantId: string; totp: string; userContext: UserContext; userId: string }
          • tenantId: string
          • totp: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Functions

    • createDevice(userId: string, userIdentifierInfo?: string, deviceName?: string, skew?: number, period?: number, userContext?: Record<string, any>): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • userId: string
      • Optional userIdentifierInfo: string
      • Optional deviceName: string
      • Optional skew: number
      • Optional period: number
      • Optional userContext: Record<string, any>

      Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • init(config?: TypeInput): RecipeListFunction
    • listDevices(userId: string, userContext?: Record<string, any>): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice(userId: string, deviceName: string, userContext?: Record<string, any>): Promise<{ didDeviceExist: boolean; status: "OK" }>
    • Parameters

      • userId: string
      • deviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice(userId: string, existingDeviceName: string, newDeviceName: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>
    • Parameters

      • userId: string
      • existingDeviceName: string
      • newDeviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>

    • verifyDevice(tenantId: string, userId: string, deviceName: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • deviceName: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP(tenantId: string, userId: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/totp | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/totp

    Index

    Type Aliases

    APIInterface: { createDevicePOST: undefined | ((input: { deviceName?: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>); listDevicesGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" } | GeneralErrorResponse>); removeDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ didDeviceExist: boolean; status: "OK" } | GeneralErrorResponse>); verifyDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>); verifyTOTPPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>) }

    Type declaration

    • createDevicePOST: undefined | ((input: { deviceName?: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | GeneralErrorResponse>)
    • listDevicesGET: undefined | ((input: { options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" } | GeneralErrorResponse>)
    • removeDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; userContext: UserContext }) => Promise<{ didDeviceExist: boolean; status: "OK" } | GeneralErrorResponse>)
    • verifyDevicePOST: undefined | ((input: { deviceName: string; options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>)
    • verifyTOTPPOST: undefined | ((input: { options: APIOptions; session: SessionContainer; totp: string; userContext: UserContext }) => Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" } | GeneralErrorResponse>)
    APIOptions: { config: TypeNormalisedInput; isInServerlessEnv: boolean; recipeId: string; recipeImplementation: RecipeInterface; req: BaseRequest; res: BaseResponse }

    Type declaration

    RecipeInterface: { createDevice: any; getUserIdentifierInfoForUserId: any; listDevices: any; removeDevice: any; updateDevice: any; verifyDevice: any; verifyTOTP: any }

    Type declaration

    • createDevice:function
      • createDevice(input: { deviceName?: string; period?: number; skew?: number; userContext: UserContext; userId: string; userIdentifierInfo?: string }): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
      • Parameters

        • input: { deviceName?: string; period?: number; skew?: number; userContext: UserContext; userId: string; userIdentifierInfo?: string }
          • Optional deviceName?: string
          • Optional period?: number
          • Optional skew?: number
          • userContext: UserContext
          • userId: string
          • Optional userIdentifierInfo?: string

        Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • getUserIdentifierInfoForUserId:function
      • getUserIdentifierInfoForUserId(input: { userContext: UserContext; userId: string }): Promise<{ info: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" | "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" }>
      • Parameters

        • input: { userContext: UserContext; userId: string }
          • userContext: UserContext
          • userId: string

        Returns Promise<{ info: string; status: "OK" } | { status: "UNKNOWN_USER_ID_ERROR" | "USER_IDENTIFIER_INFO_DOES_NOT_EXIST_ERROR" }>

    • listDevices:function
      • listDevices(input: { userContext: UserContext; userId: string }): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
      • Parameters

        • input: { userContext: UserContext; userId: string }
          • userContext: UserContext
          • userId: string

        Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice:function
      • removeDevice(input: { deviceName: string; userContext: UserContext; userId: string }): Promise<{ didDeviceExist: boolean; status: "OK" }>
      • Parameters

        • input: { deviceName: string; userContext: UserContext; userId: string }
          • deviceName: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice:function
      • updateDevice(input: { existingDeviceName: string; newDeviceName: string; userContext: UserContext; userId: string }): Promise<{ status: "OK" | "UNKNOWN_DEVICE_ERROR" | "DEVICE_ALREADY_EXISTS_ERROR" }>
      • Parameters

        • input: { existingDeviceName: string; newDeviceName: string; userContext: UserContext; userId: string }
          • existingDeviceName: string
          • newDeviceName: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" | "UNKNOWN_DEVICE_ERROR" | "DEVICE_ALREADY_EXISTS_ERROR" }>

    • verifyDevice:function
      • verifyDevice(input: { deviceName: string; tenantId: string; totp: string; userContext: UserContext; userId: string }): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
      • Parameters

        • input: { deviceName: string; tenantId: string; totp: string; userContext: UserContext; userId: string }
          • deviceName: string
          • tenantId: string
          • totp: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP:function
      • verifyTOTP(input: { tenantId: string; totp: string; userContext: UserContext; userId: string }): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
      • Parameters

        • input: { tenantId: string; totp: string; userContext: UserContext; userId: string }
          • tenantId: string
          • totp: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Functions

    • createDevice(userId: string, userIdentifierInfo?: string, deviceName?: string, skew?: number, period?: number, userContext?: Record<string, any>): Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>
    • Parameters

      • userId: string
      • Optional userIdentifierInfo: string
      • Optional deviceName: string
      • Optional skew: number
      • Optional period: number
      • Optional userContext: Record<string, any>

      Returns Promise<{ deviceName: string; qrCodeString: string; secret: string; status: "OK" } | { status: "DEVICE_ALREADY_EXISTS_ERROR" } | { status: "UNKNOWN_USER_ID_ERROR" }>

    • init(config?: TypeInput): RecipeListFunction
    • listDevices(userId: string, userContext?: Record<string, any>): Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>
    • Parameters

      • userId: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ devices: { name: string; period: number; skew: number; verified: boolean }[]; status: "OK" }>

    • removeDevice(userId: string, deviceName: string, userContext?: Record<string, any>): Promise<{ didDeviceExist: boolean; status: "OK" }>
    • Parameters

      • userId: string
      • deviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didDeviceExist: boolean; status: "OK" }>

    • updateDevice(userId: string, existingDeviceName: string, newDeviceName: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>
    • Parameters

      • userId: string
      • existingDeviceName: string
      • newDeviceName: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "DEVICE_ALREADY_EXISTS_ERROR" | "UNKNOWN_DEVICE_ERROR" }>

    • verifyDevice(tenantId: string, userId: string, deviceName: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • deviceName: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; wasAlreadyVerified: boolean } | { status: "UNKNOWN_DEVICE_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    • verifyTOTP(tenantId: string, userId: string, totp: string, userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • totp: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_USER_ID_ERROR" } | { currentNumberOfFailedAttempts: number; maxNumberOfFailedAttempts: number; status: "INVALID_TOTP_ERROR" } | { retryAfterMs: number; status: "LIMIT_REACHED_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_usermetadata.html b/docs/modules/recipe_usermetadata.html index ddc173c38..2881b37e9 100644 --- a/docs/modules/recipe_usermetadata.html +++ b/docs/modules/recipe_usermetadata.html @@ -1,4 +1,4 @@ -recipe/usermetadata | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/usermetadata

    Index

    Type Aliases

    RecipeInterface: { clearUserMetadata: any; getUserMetadata: any; updateUserMetadata: any }

    Type declaration

    • clearUserMetadata:function
      • clearUserMetadata(input: { userContext: UserContext; userId: string }): Promise<{ status: "OK" }>
    • getUserMetadata:function
      • getUserMetadata(input: { userContext: UserContext; userId: string }): Promise<{ metadata: any; status: "OK" }>
      • Parameters

        • input: { userContext: UserContext; userId: string }
          • userContext: UserContext
          • userId: string

        Returns Promise<{ metadata: any; status: "OK" }>

    • updateUserMetadata:function
      • updateUserMetadata(input: { metadataUpdate: JSONObject; userContext: UserContext; userId: string }): Promise<{ metadata: JSONObject; status: "OK" }>
      • +recipe/usermetadata | supertokens-node
        Options
        All
        • Public
        • Public/Protected
        • All
        Menu

        Module recipe/usermetadata

        Index

        Type Aliases

        RecipeInterface: { clearUserMetadata: any; getUserMetadata: any; updateUserMetadata: any }

        Type declaration

        • clearUserMetadata:function
          • clearUserMetadata(input: { userContext: UserContext; userId: string }): Promise<{ status: "OK" }>
        • getUserMetadata:function
          • getUserMetadata(input: { userContext: UserContext; userId: string }): Promise<{ metadata: any; status: "OK" }>
          • Parameters

            • input: { userContext: UserContext; userId: string }
              • userContext: UserContext
              • userId: string

            Returns Promise<{ metadata: any; status: "OK" }>

        • updateUserMetadata:function
          • updateUserMetadata(input: { metadataUpdate: JSONObject; userContext: UserContext; userId: string }): Promise<{ metadata: JSONObject; status: "OK" }>
          • Updates the metadata object of the user by doing a shallow merge of the stored and the update JSONs and removing properties set to null on the root level of the update object. e.g.:

            @@ -7,4 +7,4 @@
          • update: { "notifications": { "sms": true }, "todos": null }
          • result: { "preferences": { "theme":"dark" }, "notifications": { "sms": true } }
          -

        Parameters

        • input: { metadataUpdate: JSONObject; userContext: UserContext; userId: string }
          • metadataUpdate: JSONObject
          • userContext: UserContext
          • userId: string

        Returns Promise<{ metadata: JSONObject; status: "OK" }>

    Functions

    • clearUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • getUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ metadata: any; status: "OK" }>
    • init(config?: TypeInput): RecipeListFunction
    • updateUserMetadata(userId: string, metadataUpdate: JSONObject, userContext?: Record<string, any>): Promise<{ metadata: JSONObject; status: "OK" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +

    Parameters

    • input: { metadataUpdate: JSONObject; userContext: UserContext; userId: string }
      • metadataUpdate: JSONObject
      • userContext: UserContext
      • userId: string

    Returns Promise<{ metadata: JSONObject; status: "OK" }>

    Functions

    • clearUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ status: "OK" }>
    • getUserMetadata(userId: string, userContext?: Record<string, any>): Promise<{ metadata: any; status: "OK" }>
    • init(config?: TypeInput): RecipeListFunction
    • updateUserMetadata(userId: string, metadataUpdate: JSONObject, userContext?: Record<string, any>): Promise<{ metadata: JSONObject; status: "OK" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file diff --git a/docs/modules/recipe_userroles.html b/docs/modules/recipe_userroles.html index 320b060fc..d46177c65 100644 --- a/docs/modules/recipe_userroles.html +++ b/docs/modules/recipe_userroles.html @@ -1 +1 @@ -recipe/userroles | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/userroles

    Index

    Type Aliases

    RecipeInterface: { addRoleToUser: any; createNewRoleOrAddPermissions: any; deleteRole: any; getAllRoles: any; getPermissionsForRole: any; getRolesForUser: any; getRolesThatHavePermission: any; getUsersThatHaveRole: any; removePermissionsFromRole: any; removeUserRole: any }

    Type declaration

    • addRoleToUser:function
      • addRoleToUser(input: { role: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext; userId: string }
          • role: string
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions:function
      • createNewRoleOrAddPermissions(input: { permissions: string[]; role: string; userContext: UserContext }): Promise<{ createdNewRole: boolean; status: "OK" }>
      • Parameters

        • input: { permissions: string[]; role: string; userContext: UserContext }
          • permissions: string[]
          • role: string
          • userContext: UserContext

        Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole:function
      • deleteRole(input: { role: string; userContext: UserContext }): Promise<{ didRoleExist: boolean; status: "OK" }>
      • Parameters

        • input: { role: string; userContext: UserContext }
          • role: string
          • userContext: UserContext

        Returns Promise<{ didRoleExist: boolean; status: "OK" }>

    • getAllRoles:function
      • getAllRoles(input: { userContext: UserContext }): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole:function
      • getPermissionsForRole(input: { role: string; userContext: UserContext }): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; userContext: UserContext }
          • role: string
          • userContext: UserContext

        Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser:function
      • getRolesForUser(input: { tenantId: string; userContext: UserContext; userId: string }): Promise<{ roles: string[]; status: "OK" }>
      • Parameters

        • input: { tenantId: string; userContext: UserContext; userId: string }
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ roles: string[]; status: "OK" }>

    • getRolesThatHavePermission:function
      • getRolesThatHavePermission(input: { permission: string; userContext: UserContext }): Promise<{ roles: string[]; status: "OK" }>
      • Parameters

        • input: { permission: string; userContext: UserContext }
          • permission: string
          • userContext: UserContext

        Returns Promise<{ roles: string[]; status: "OK" }>

    • getUsersThatHaveRole:function
      • getUsersThatHaveRole(input: { role: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext }
          • role: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • removePermissionsFromRole:function
      • removePermissionsFromRole(input: { permissions: string[]; role: string; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { permissions: string[]; role: string; userContext: UserContext }
          • permissions: string[]
          • role: string
          • userContext: UserContext

        Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole:function
      • removeUserRole(input: { role: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext; userId: string }
          • role: string
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Variables

    PermissionClaim: PermissionClaimClass = ...
    UserRoleClaim: UserRoleClaimClass = ...

    Functions

    • addRoleToUser(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ createdNewRole: boolean; status: "OK" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole(role: string, userContext?: Record<string, any>): Promise<{ didRoleExist: boolean; status: "OK" }>
    • getAllRoles(userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole(role: string, userContext?: Record<string, any>): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser(tenantId: string, userId: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getRolesThatHavePermission(permission: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getUsersThatHaveRole(tenantId: string, role: string, userContext?: Record<string, any>): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • init(config?: TypeInput): RecipeListFunction
    • removePermissionsFromRole(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +recipe/userroles | supertokens-node
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    Module recipe/userroles

    Index

    Type Aliases

    RecipeInterface: { addRoleToUser: any; createNewRoleOrAddPermissions: any; deleteRole: any; getAllRoles: any; getPermissionsForRole: any; getRolesForUser: any; getRolesThatHavePermission: any; getUsersThatHaveRole: any; removePermissionsFromRole: any; removeUserRole: any }

    Type declaration

    • addRoleToUser:function
      • addRoleToUser(input: { role: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext; userId: string }
          • role: string
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions:function
      • createNewRoleOrAddPermissions(input: { permissions: string[]; role: string; userContext: UserContext }): Promise<{ createdNewRole: boolean; status: "OK" }>
      • Parameters

        • input: { permissions: string[]; role: string; userContext: UserContext }
          • permissions: string[]
          • role: string
          • userContext: UserContext

        Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole:function
      • deleteRole(input: { role: string; userContext: UserContext }): Promise<{ didRoleExist: boolean; status: "OK" }>
      • Parameters

        • input: { role: string; userContext: UserContext }
          • role: string
          • userContext: UserContext

        Returns Promise<{ didRoleExist: boolean; status: "OK" }>

    • getAllRoles:function
      • getAllRoles(input: { userContext: UserContext }): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole:function
      • getPermissionsForRole(input: { role: string; userContext: UserContext }): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; userContext: UserContext }
          • role: string
          • userContext: UserContext

        Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser:function
      • getRolesForUser(input: { tenantId: string; userContext: UserContext; userId: string }): Promise<{ roles: string[]; status: "OK" }>
      • Parameters

        • input: { tenantId: string; userContext: UserContext; userId: string }
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ roles: string[]; status: "OK" }>

    • getRolesThatHavePermission:function
      • getRolesThatHavePermission(input: { permission: string; userContext: UserContext }): Promise<{ roles: string[]; status: "OK" }>
      • Parameters

        • input: { permission: string; userContext: UserContext }
          • permission: string
          • userContext: UserContext

        Returns Promise<{ roles: string[]; status: "OK" }>

    • getUsersThatHaveRole:function
      • getUsersThatHaveRole(input: { role: string; tenantId: string; userContext: UserContext }): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext }
          • role: string
          • tenantId: string
          • userContext: UserContext

        Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • removePermissionsFromRole:function
      • removePermissionsFromRole(input: { permissions: string[]; role: string; userContext: UserContext }): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { permissions: string[]; role: string; userContext: UserContext }
          • permissions: string[]
          • role: string
          • userContext: UserContext

        Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole:function
      • removeUserRole(input: { role: string; tenantId: string; userContext: UserContext; userId: string }): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
      • Parameters

        • input: { role: string; tenantId: string; userContext: UserContext; userId: string }
          • role: string
          • tenantId: string
          • userContext: UserContext
          • userId: string

        Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Variables

    PermissionClaim: PermissionClaimClass = ...
    UserRoleClaim: UserRoleClaimClass = ...

    Functions

    • addRoleToUser(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserAlreadyHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • createNewRoleOrAddPermissions(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ createdNewRole: boolean; status: "OK" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ createdNewRole: boolean; status: "OK" }>

    • deleteRole(role: string, userContext?: Record<string, any>): Promise<{ didRoleExist: boolean; status: "OK" }>
    • getAllRoles(userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getPermissionsForRole(role: string, userContext?: Record<string, any>): Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ permissions: string[]; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    • getRolesForUser(tenantId: string, userId: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getRolesThatHavePermission(permission: string, userContext?: Record<string, any>): Promise<{ roles: string[]; status: "OK" }>
    • getUsersThatHaveRole(tenantId: string, role: string, userContext?: Record<string, any>): Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK"; users: string[] } | { status: "UNKNOWN_ROLE_ERROR" }>

    • init(config?: TypeInput): RecipeListFunction
    • removePermissionsFromRole(role: string, permissions: string[], userContext?: Record<string, any>): Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • role: string
      • permissions: string[]
      • Optional userContext: Record<string, any>

      Returns Promise<{ status: "OK" | "UNKNOWN_ROLE_ERROR" }>

    • removeUserRole(tenantId: string, userId: string, role: string, userContext?: Record<string, any>): Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>
    • Parameters

      • tenantId: string
      • userId: string
      • role: string
      • Optional userContext: Record<string, any>

      Returns Promise<{ didUserHaveRole: boolean; status: "OK" } | { status: "UNKNOWN_ROLE_ERROR" }>

    Legend

    • Variable
    • Function
    • Function with type parameter
    • Type alias
    • Type alias with type parameter
    • Class
    • Class with type parameter
    • Interface

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file