UNPKG

supertokens-node

Version:
300 lines (299 loc) 13.6 kB
"use strict"; /* Copyright (c) 2025, 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 __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = getRecipeInterface; const recipeUserId_1 = __importDefault(require("../../recipeUserId")); const user_1 = require("../../user"); const authUtils_1 = require("../../authUtils"); function getRecipeInterface(stInstance, querier, getWebauthnConfig) { return { registerOptions: async function (_a) { var _b, _c; var { relyingPartyId, relyingPartyName, origin, timeout, attestation = "none", tenantId, userContext, supportedAlgorithmIds, userVerification, userPresence, residentKey } = _a, rest = __rest(_a, ["relyingPartyId", "relyingPartyName", "origin", "timeout", "attestation", "tenantId", "userContext", "supportedAlgorithmIds", "userVerification", "userPresence", "residentKey"]); const emailInput = "email" in rest ? rest.email : undefined; const recoverAccountTokenInput = "recoverAccountToken" in rest ? rest.recoverAccountToken : undefined; let email; if (emailInput !== undefined) { email = emailInput; } else if (recoverAccountTokenInput !== undefined) { const result = await this.getUserFromRecoverAccountToken({ token: recoverAccountTokenInput, tenantId, userContext, }); if (result.status !== "OK") { return result; } const user = result.user; // if the recipeUserId is not present, it means that the user does not have a webauthn login method and we should just use the user id // this will make account recovery act as a sign up const userId = ((_b = result.recipeUserId) === null || _b === void 0 ? void 0 : _b.getAsString()) || user.id; email = (_c = user.loginMethods.find((lm) => lm.recipeUserId.getAsString() === userId)) === null || _c === void 0 ? void 0 : _c.email; } if (!email) { return { status: "INVALID_EMAIL_ERROR", err: "The email is missing", }; } const err = await getWebauthnConfig().validateEmailAddress(email, tenantId, userContext); if (err) { return { status: "INVALID_EMAIL_ERROR", err, }; } let displayName; if ("displayName" in rest && rest.displayName !== undefined) { displayName = rest.displayName; } else { displayName = email; } return await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/options/register", params: { tenantId: tenantId, }, }, { email, displayName, relyingPartyName, relyingPartyId, origin, timeout, attestation, supportedAlgorithmIds, userVerification, userPresence, residentKey, }, userContext); }, signInOptions: async function ({ relyingPartyId, relyingPartyName, origin, timeout, userVerification, userPresence, tenantId, userContext, }) { return await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/options/signin", params: { tenantId: tenantId, }, }, { userVerification, userPresence, relyingPartyId, relyingPartyName, origin, timeout, }, userContext); }, signUp: async function ({ webauthnGeneratedOptionsId, credential, tenantId, session, shouldTryLinkingWithSessionUser, userContext }) { const response = await this.createNewRecipeUser({ credential, webauthnGeneratedOptionsId, tenantId, userContext, }); if (response.status !== "OK") { return response; } const linkResult = await authUtils_1.AuthUtils.linkToSessionIfRequiredElseCreatePrimaryUserIdOrLinkByAccountInfo({ stInstance, tenantId, inputUser: response.user, recipeUserId: response.recipeUserId, session, shouldTryLinkingWithSessionUser, userContext, }); if (linkResult.status != "OK") { return linkResult; } return { status: "OK", user: linkResult.user, recipeUserId: response.recipeUserId, }; }, signIn: async function ({ credential, webauthnGeneratedOptionsId, tenantId, session, shouldTryLinkingWithSessionUser, userContext }) { const response = await this.verifyCredentials({ credential, webauthnGeneratedOptionsId, tenantId, userContext, }); if (response.status !== "OK") { return response; } const loginMethod = response.user.loginMethods.find((lm) => lm.recipeUserId.getAsString() === response.recipeUserId.getAsString()); if (!loginMethod.verified) { await stInstance .getRecipeInstanceOrThrow("accountlinking") .verifyEmailForRecipeUserIfLinkedAccountsAreVerified({ user: response.user, recipeUserId: response.recipeUserId, userContext, }); // We do this so that we get the updated user (in case the above // function updated the verification status) and can return that response.user = (await stInstance .getRecipeInstanceOrThrow("accountlinking") .recipeInterfaceImpl.getUser({ userId: response.recipeUserId.getAsString(), userContext })); } const linkResult = await authUtils_1.AuthUtils.linkToSessionIfRequiredElseCreatePrimaryUserIdOrLinkByAccountInfo({ stInstance, tenantId, inputUser: response.user, recipeUserId: response.recipeUserId, session, shouldTryLinkingWithSessionUser, userContext, }); if (linkResult.status === "LINKING_TO_SESSION_USER_FAILED") { return linkResult; } response.user = linkResult.user; return response; }, verifyCredentials: async function ({ credential, webauthnGeneratedOptionsId, tenantId, userContext }) { const response = await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/signin", params: { tenantId: tenantId, }, }, { credential, webauthnGeneratedOptionsId, }, userContext); if (response.status === "OK") { return { status: "OK", user: new user_1.User(response.user), recipeUserId: new recipeUserId_1.default(response.recipeUserId), }; } return response; }, createNewRecipeUser: async function (input) { const resp = await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/signup", params: { tenantId: input.tenantId, }, }, { webauthnGeneratedOptionsId: input.webauthnGeneratedOptionsId, credential: input.credential, }, input.userContext); if (resp.status === "OK") { return { status: "OK", user: new user_1.User(resp.user), recipeUserId: new recipeUserId_1.default(resp.recipeUserId), }; } return resp; }, generateRecoverAccountToken: async function ({ userId, email, tenantId, userContext }) { return await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/user/recover/token", params: { tenantId: tenantId, }, }, { userId, email, }, userContext); }, consumeRecoverAccountToken: async function ({ token, tenantId, userContext }) { return await querier.sendPostRequest({ path: "/<tenantId>/recipe/webauthn/user/recover/token/consume", params: { tenantId: tenantId, }, }, { token, }, userContext); }, registerCredential: async function ({ webauthnGeneratedOptionsId, credential, userContext, recipeUserId }) { return await querier.sendPostRequest("/recipe/webauthn/user/credential/register", { recipeUserId, webauthnGeneratedOptionsId, credential, }, userContext); }, getUserFromRecoverAccountToken: async function ({ token, tenantId, userContext }) { const resp = await querier.sendGetRequest({ path: "/<tenantId>/recipe/webauthn/user/recover", params: { tenantId: tenantId, }, }, { token }, userContext); if (resp.status === "OK") { return Object.assign(Object.assign({}, resp), { user: new user_1.User(resp.user), recipeUserId: resp.recipeUserId ? new recipeUserId_1.default(resp.recipeUserId) : undefined }); } return resp; }, removeCredential: async function ({ webauthnCredentialId, recipeUserId, userContext }) { return await querier.sendDeleteRequest("/recipe/webauthn/user/credential/remove", undefined, { recipeUserId, webauthnCredentialId }, userContext); }, getCredential: async function ({ webauthnCredentialId, recipeUserId, userContext }) { const resp = await querier.sendGetRequest("/recipe/webauthn/user/credential", { webauthnCredentialId, recipeUserId }, userContext); if (resp.status === "OK") { return Object.assign(Object.assign({}, resp), { recipeUserId: new recipeUserId_1.default(resp.recipeUserId) }); } return resp; }, listCredentials: async function ({ recipeUserId, userContext }) { return await querier.sendGetRequest("/recipe/webauthn/user/credential/list", { recipeUserId }, userContext); }, removeGeneratedOptions: async function ({ webauthnGeneratedOptionsId, tenantId, userContext }) { return await querier.sendDeleteRequest({ path: "/<tenantId>/recipe/webauthn/options/remove", params: { tenantId: tenantId, }, }, undefined, { webauthnGeneratedOptionsId }, userContext); }, getGeneratedOptions: async function ({ webauthnGeneratedOptionsId, tenantId, userContext }) { return await querier.sendGetRequest({ path: "/<tenantId>/recipe/webauthn/options", params: { tenantId: tenantId || "public", }, }, { webauthnGeneratedOptionsId }, userContext); }, updateUserEmail: async function ({ email, recipeUserId, tenantId, userContext }) { return await querier.sendPutRequest({ path: "/<tenantId>/recipe/webauthn/user/email", params: { tenantId: tenantId, }, }, { email, recipeUserId }, {}, userContext); }, }; }