supertokens-node
Version:
NodeJS driver for SuperTokens core
766 lines (765 loc) • 24.9 kB
TypeScript
// @ts-nocheck
import type { BaseRequest, BaseResponse } from "../../framework";
import OverrideableBuilder from "supertokens-js-override";
import { SessionContainerInterface } from "../session/types";
import {
TypeInput as EmailDeliveryTypeInput,
TypeInputWithService as EmailDeliveryTypeInputWithService,
} from "../../ingredients/emaildelivery/types";
import EmailDeliveryIngredient from "../../ingredients/emaildelivery";
import { GeneralErrorResponse, NormalisedAppinfo, User, UserContext } from "../../types";
import RecipeUserId from "../../recipeUserId";
export type TypeNormalisedInput = {
getRelyingPartyId: TypeNormalisedInputRelyingPartyId;
getRelyingPartyName: TypeNormalisedInputRelyingPartyName;
getOrigin: TypeNormalisedInputGetOrigin;
getEmailDeliveryConfig: (
isInServerlessEnv: boolean
) => EmailDeliveryTypeInputWithService<TypeWebauthnEmailDeliveryInput>;
validateEmailAddress: TypeNormalisedInputValidateEmailAddress;
override: {
functions: (
originalImplementation: RecipeInterface,
builder: OverrideableBuilder<RecipeInterface>
) => RecipeInterface;
apis: (originalImplementation: APIInterface, builder: OverrideableBuilder<APIInterface>) => APIInterface;
};
};
export type TypeNormalisedInputRelyingPartyId = (input: {
tenantId: string;
request: BaseRequest | undefined;
userContext: UserContext;
}) => Promise<string>;
export type TypeNormalisedInputRelyingPartyName = (input: {
tenantId: string;
request: BaseRequest | undefined;
userContext: UserContext;
}) => Promise<string>;
export type TypeNormalisedInputGetOrigin = (input: {
tenantId: string;
request: BaseRequest;
userContext: UserContext;
}) => Promise<string>;
export type TypeNormalisedInputValidateEmailAddress = (
email: string,
tenantId: string,
userContext: UserContext
) => Promise<string | undefined> | string | undefined;
export type TypeInput = {
emailDelivery?: EmailDeliveryTypeInput<TypeWebauthnEmailDeliveryInput>;
getRelyingPartyId?: TypeInputRelyingPartyId;
getRelyingPartyName?: TypeInputRelyingPartyName;
validateEmailAddress?: TypeInputValidateEmailAddress;
getOrigin?: TypeInputGetOrigin;
override?: {
functions?: (
originalImplementation: RecipeInterface,
builder: OverrideableBuilder<RecipeInterface>
) => RecipeInterface;
apis?: (originalImplementation: APIInterface, builder: OverrideableBuilder<APIInterface>) => APIInterface;
};
};
export type TypeInputRelyingPartyId =
| string
| ((input: { tenantId: string; request: BaseRequest | undefined; userContext: UserContext }) => Promise<string>);
export type TypeInputRelyingPartyName =
| string
| ((input: { tenantId: string; userContext: UserContext }) => Promise<string>);
export type TypeInputGetOrigin = (input: {
tenantId: string;
request: BaseRequest;
userContext: UserContext;
}) => Promise<string>;
export type TypeInputValidateEmailAddress = (
email: string,
tenantId: string,
userContext: UserContext
) => Promise<string | undefined> | string | undefined;
type RegisterOptionsErrorResponse =
| {
status: "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR";
}
| {
status: "INVALID_EMAIL_ERROR";
err: string;
}
| {
status: "INVALID_OPTIONS_ERROR";
};
type SignInOptionsErrorResponse = {
status: "INVALID_OPTIONS_ERROR";
};
type CreateNewRecipeUserErrorResponse =
| {
status: "EMAIL_ALREADY_EXISTS_ERROR";
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
}
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
reason: string;
};
type SignUpErrorResponse =
| CreateNewRecipeUserErrorResponse
| {
status: "LINKING_TO_SESSION_USER_FAILED";
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";
};
type VerifyCredentialsErrorResponse =
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
}
| {
status: "CREDENTIAL_NOT_FOUND_ERROR";
}
| {
status: "UNKNOWN_USER_ID_ERROR";
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
};
type SignInErrorResponse =
| VerifyCredentialsErrorResponse
| {
status: "LINKING_TO_SESSION_USER_FAILED";
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";
};
type GenerateRecoverAccountTokenErrorResponse = {
status: "UNKNOWN_USER_ID_ERROR";
};
type ConsumeRecoverAccountTokenErrorResponse = {
status: "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR";
};
type RegisterCredentialErrorResponse =
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
reason: string;
};
type GetUserFromRecoverAccountTokenErrorResponse = {
status: "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR";
};
type RemoveCredentialErrorResponse = {
status: "CREDENTIAL_NOT_FOUND_ERROR";
};
type GetCredentialErrorResponse = {
status: "CREDENTIAL_NOT_FOUND_ERROR";
};
type RemoveGeneratedOptionsErrorResponse = {
status: "OPTIONS_NOT_FOUND_ERROR";
};
type GetGeneratedOptionsErrorResponse = {
status: "OPTIONS_NOT_FOUND_ERROR";
};
type UpdateUserEmailErrorResponse =
| {
status: "EMAIL_ALREADY_EXISTS_ERROR";
}
| {
status: "UNKNOWN_USER_ID_ERROR";
};
type Base64URLString = string;
export type ResidentKey = "required" | "preferred" | "discouraged";
export type UserVerification = "required" | "preferred" | "discouraged";
export type Attestation = "none" | "indirect" | "direct" | "enterprise";
export type RecipeInterface = {
registerOptions(
input: {
relyingPartyId: string;
relyingPartyName: string;
origin: string;
residentKey: ResidentKey | undefined;
userVerification: UserVerification | undefined;
userPresence: boolean | undefined;
attestation: Attestation | undefined;
supportedAlgorithmIds: number[] | undefined;
timeout: number | undefined;
tenantId: string;
userContext: UserContext;
} & (
| {
recoverAccountToken: string;
}
| {
displayName: string | undefined;
email: string;
}
)
): Promise<
| {
status: "OK";
webauthnGeneratedOptionsId: string;
createdAt: number;
expiresAt: number;
rp: {
id: string;
name: string;
};
user: {
id: string;
name: string;
displayName: string;
};
challenge: string;
timeout: number;
excludeCredentials: {
id: string;
type: "public-key";
transports: ("ble" | "hybrid" | "internal" | "nfc" | "usb")[];
}[];
attestation: Attestation;
pubKeyCredParams: {
alg: number;
type: "public-key";
}[];
authenticatorSelection: {
requireResidentKey: boolean;
residentKey: ResidentKey;
userVerification: UserVerification;
};
}
| RegisterOptionsErrorResponse
>;
signInOptions(input: {
relyingPartyId: string;
relyingPartyName: string;
origin: string;
userVerification: UserVerification | undefined;
userPresence: boolean | undefined;
timeout: number | undefined;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
webauthnGeneratedOptionsId: string;
createdAt: number;
expiresAt: number;
challenge: string;
timeout: number;
userVerification: UserVerification;
}
| SignInOptionsErrorResponse
>;
signUp(input: {
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
session: SessionContainerInterface | undefined;
shouldTryLinkingWithSessionUser: boolean | undefined;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
user: User;
recipeUserId: RecipeUserId;
}
| SignUpErrorResponse
>;
signIn(input: {
webauthnGeneratedOptionsId: string;
credential: AuthenticationPayload;
session: SessionContainerInterface | undefined;
shouldTryLinkingWithSessionUser: boolean | undefined;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
user: User;
recipeUserId: RecipeUserId;
}
| SignInErrorResponse
>;
verifyCredentials(input: {
webauthnGeneratedOptionsId: string;
credential: AuthenticationPayload;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
user: User;
recipeUserId: RecipeUserId;
}
| VerifyCredentialsErrorResponse
>;
/**
* This function is meant only for creating the recipe in the core and nothing else.
* We added this even though signUp exists cause devs may override signup expecting it
* to be called just during sign up. But we also need a version of signing up which can be
* called during operations like creating a user during account recovery flow.
*/
createNewRecipeUser(input: {
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
user: User;
recipeUserId: RecipeUserId;
}
| CreateNewRecipeUserErrorResponse
>;
/**
* We pass in the email as well to this function cause the input userId
* may not be associated with an webauthn account. In this case, we
* need to know which email to use to create an webauthn account later on.
*/
generateRecoverAccountToken(input: {
userId: string;
email: string;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
token: string;
}
| GenerateRecoverAccountTokenErrorResponse
>;
consumeRecoverAccountToken(input: { token: string; tenantId: string; userContext: UserContext }): Promise<
| {
status: "OK";
email: string;
userId: string;
}
| ConsumeRecoverAccountTokenErrorResponse
>;
registerCredential(input: {
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
userContext: UserContext;
recipeUserId: string;
}): Promise<
| {
status: "OK";
}
| RegisterCredentialErrorResponse
>;
getUserFromRecoverAccountToken(input: { token: string; tenantId: string; userContext: UserContext }): Promise<
| {
status: "OK";
user: User;
recipeUserId?: RecipeUserId;
}
| GetUserFromRecoverAccountTokenErrorResponse
>;
removeCredential(input: { webauthnCredentialId: string; recipeUserId: string; userContext: UserContext }): Promise<
| {
status: "OK";
}
| RemoveCredentialErrorResponse
>;
getCredential(input: { webauthnCredentialId: string; recipeUserId: string; userContext: UserContext }): Promise<
| {
status: "OK";
webauthnCredentialId: string;
relyingPartyId: string;
recipeUserId: RecipeUserId;
createdAt: number;
}
| GetCredentialErrorResponse
>;
listCredentials(input: { recipeUserId: string; userContext: UserContext }): Promise<{
status: "OK";
credentials: {
webauthnCredentialId: string;
relyingPartyId: string;
recipeUserId: string;
createdAt: number;
}[];
}>;
removeGeneratedOptions(input: {
webauthnGeneratedOptionsId: string;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
}
| RemoveGeneratedOptionsErrorResponse
>;
getGeneratedOptions(input: {
webauthnGeneratedOptionsId: string;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
webauthnGeneratedOptionsId: string;
relyingPartyId: string;
relyingPartyName: string;
userVerification: UserVerification;
userPresence: boolean;
origin: string;
email?: string;
timeout: number;
challenge: string;
createdAt: number;
expiresAt: number;
}
| GetGeneratedOptionsErrorResponse
>;
updateUserEmail(input: {
recipeUserId: string;
email: string;
tenantId: string;
userContext: UserContext;
}): Promise<
| {
status: "OK";
}
| UpdateUserEmailErrorResponse
>;
};
export type APIOptions = {
recipeImplementation: RecipeInterface;
appInfo: NormalisedAppinfo;
config: TypeNormalisedInput;
recipeId: string;
isInServerlessEnv: boolean;
req: BaseRequest;
res: BaseResponse;
emailDelivery: EmailDeliveryIngredient<TypeWebauthnEmailDeliveryInput>;
};
type RegisterOptionsPOSTErrorResponse = RegisterOptionsErrorResponse;
type SignInOptionsPOSTErrorResponse = SignInOptionsErrorResponse;
type SignUpPOSTErrorResponse =
| {
status: "SIGN_UP_NOT_ALLOWED";
reason: string;
}
| {
status: "EMAIL_ALREADY_EXISTS_ERROR";
}
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
reason: string;
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
};
type SignInPOSTErrorResponse =
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "SIGN_IN_NOT_ALLOWED";
reason: string;
};
type GenerateRecoverAccountTokenPOSTErrorResponse = {
status: "RECOVER_ACCOUNT_NOT_ALLOWED";
reason: string;
};
type RecoverAccountPOSTErrorResponse =
| {
status: "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR";
}
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
reason: string;
};
type RegisterCredentialPOSTErrorResponse =
| {
status: "REGISTER_CREDENTIAL_NOT_ALLOWED";
reason: string;
}
| {
status: "INVALID_CREDENTIALS_ERROR";
}
| {
status: "OPTIONS_NOT_FOUND_ERROR";
}
| {
status: "INVALID_OPTIONS_ERROR";
}
| {
status: "INVALID_AUTHENTICATOR_ERROR";
reason: string;
};
type RemoveCredentialPOSTErrorResponse = {
status: "CREDENTIAL_NOT_FOUND_ERROR";
};
export type APIInterface = {
registerOptionsPOST:
| undefined
| ((
input: {
tenantId: string;
options: APIOptions;
userContext: UserContext;
} & (
| {
email: string;
displayName?: string;
}
| {
recoverAccountToken: string;
}
)
) => Promise<
| {
status: "OK";
webauthnGeneratedOptionsId: string;
createdAt: number;
expiresAt: number;
rp: {
id: string;
name: string;
};
user: {
id: string;
name: string;
displayName: string;
};
challenge: string;
timeout: number;
excludeCredentials: {
id: string;
type: "public-key";
transports: ("ble" | "hybrid" | "internal" | "nfc" | "usb")[];
}[];
attestation: "none" | "indirect" | "direct" | "enterprise";
pubKeyCredParams: {
alg: number;
type: string;
}[];
authenticatorSelection: {
requireResidentKey: boolean;
residentKey: ResidentKey;
userVerification: UserVerification;
};
}
| GeneralErrorResponse
| RegisterOptionsPOSTErrorResponse
>);
signInOptionsPOST:
| undefined
| ((input: { tenantId: string; options: APIOptions; userContext: UserContext }) => Promise<
| {
status: "OK";
webauthnGeneratedOptionsId: string;
createdAt: number;
expiresAt: number;
rpId: string;
challenge: string;
timeout: number;
userVerification: UserVerification;
}
| GeneralErrorResponse
| SignInOptionsPOSTErrorResponse
>);
signUpPOST:
| undefined
| ((input: {
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
tenantId: string;
session: SessionContainerInterface | undefined;
shouldTryLinkingWithSessionUser: boolean | undefined;
options: APIOptions;
userContext: UserContext;
}) => Promise<
| {
status: "OK";
user: User;
session: SessionContainerInterface;
}
| GeneralErrorResponse
| SignUpPOSTErrorResponse
>);
signInPOST:
| undefined
| ((input: {
webauthnGeneratedOptionsId: string;
credential: AuthenticationPayload;
tenantId: string;
session: SessionContainerInterface | undefined;
shouldTryLinkingWithSessionUser: boolean | undefined;
options: APIOptions;
userContext: UserContext;
}) => Promise<
| {
status: "OK";
user: User;
session: SessionContainerInterface;
}
| GeneralErrorResponse
| SignInPOSTErrorResponse
>);
generateRecoverAccountTokenPOST:
| undefined
| ((input: { email: string; tenantId: string; options: APIOptions; userContext: UserContext }) => Promise<
| {
status: "OK";
}
| GeneralErrorResponse
| GenerateRecoverAccountTokenPOSTErrorResponse
>);
recoverAccountPOST:
| undefined
| ((input: {
token: string;
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
tenantId: string;
options: APIOptions;
userContext: UserContext;
}) => Promise<
| {
status: "OK";
user: User;
email: string;
}
| GeneralErrorResponse
| RecoverAccountPOSTErrorResponse
>);
listCredentialsGET:
| undefined
| ((input: { session: SessionContainerInterface; options: APIOptions; userContext: UserContext }) => Promise<
| {
status: "OK";
credentials: {
recipeUserId: string;
webauthnCredentialId: string;
relyingPartyId: string;
createdAt: number;
}[];
}
| GeneralErrorResponse
>);
registerCredentialPOST:
| undefined
| ((input: {
webauthnGeneratedOptionsId: string;
credential: RegistrationPayload;
tenantId: string;
session: SessionContainerInterface;
options: APIOptions;
userContext: UserContext;
recipeUserId: string;
}) => Promise<
| {
status: "OK";
}
| GeneralErrorResponse
| RegisterCredentialPOSTErrorResponse
>);
removeCredentialPOST:
| undefined
| ((input: {
webauthnCredentialId: string;
session: SessionContainerInterface;
options: APIOptions;
userContext: UserContext;
}) => Promise<
| {
status: "OK";
}
| GeneralErrorResponse
| RemoveCredentialPOSTErrorResponse
>);
emailExistsGET:
| undefined
| ((input: { email: string; tenantId: string; options: APIOptions; userContext: UserContext }) => Promise<
| {
status: "OK";
exists: boolean;
}
| GeneralErrorResponse
>);
};
export type TypeWebauthnRecoverAccountEmailDeliveryInput = {
type: "RECOVER_ACCOUNT";
user: {
id: string;
recipeUserId: RecipeUserId | undefined;
email: string;
};
recoverAccountLink: string;
tenantId: string;
};
export type TypeWebauthnEmailDeliveryInput = TypeWebauthnRecoverAccountEmailDeliveryInput;
export type CredentialPayloadBase = {
id: string;
rawId: string;
authenticatorAttachment?: "platform" | "cross-platform";
clientExtensionResults: Record<string, unknown>;
type: "public-key";
};
export type AuthenticatorAssertionResponseJSON = {
clientDataJSON: Base64URLString;
authenticatorData: Base64URLString;
signature: Base64URLString;
userHandle?: Base64URLString;
};
export type AuthenticatorAttestationResponseJSON = {
clientDataJSON: Base64URLString;
attestationObject: Base64URLString;
authenticatorData?: Base64URLString;
transports?: ("ble" | "cable" | "hybrid" | "internal" | "nfc" | "smart-card" | "usb")[];
publicKeyAlgorithm?: COSEAlgorithmIdentifier;
publicKey?: Base64URLString;
};
export type AuthenticationPayload = CredentialPayloadBase & {
response: AuthenticatorAssertionResponseJSON;
};
export type RegistrationPayload = CredentialPayloadBase & {
response: AuthenticatorAttestationResponseJSON;
};
export type CredentialPayload = CredentialPayloadBase & {
response: {
clientDataJSON: string;
attestationObject: string;
transports?: ("ble" | "cable" | "hybrid" | "internal" | "nfc" | "smart-card" | "usb")[];
userHandle: string;
};
};
export {};