mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
1223 lines
33 KiB
TypeScript
1223 lines
33 KiB
TypeScript
import { StackAssertionError, StatusError, throwErr } from "./utils/errors";
|
|
import { identityArgs } from "./utils/functions";
|
|
import { Json } from "./utils/json";
|
|
import { filterUndefined } from "./utils/objects";
|
|
import { deindent } from "./utils/strings";
|
|
|
|
export type KnownErrorJson = {
|
|
code: string,
|
|
message: string,
|
|
details?: Json,
|
|
};
|
|
|
|
export type AbstractKnownErrorConstructor<Args extends any[]> =
|
|
& (abstract new (...args: Args) => KnownError)
|
|
& {
|
|
constructorArgsFromJson: (json: KnownErrorJson) => Args,
|
|
};
|
|
|
|
export type KnownErrorConstructor<SuperInstance extends KnownError, Args extends any[]> = {
|
|
new (...args: Args): SuperInstance & { constructorArgs: Args },
|
|
errorCode: string,
|
|
constructorArgsFromJson: (json: KnownErrorJson) => Args,
|
|
};
|
|
|
|
export abstract class KnownError extends StatusError {
|
|
public name = "KnownError";
|
|
|
|
constructor(
|
|
public readonly statusCode: number,
|
|
public readonly humanReadableMessage: string,
|
|
public readonly details?: Json,
|
|
) {
|
|
super(
|
|
statusCode,
|
|
humanReadableMessage
|
|
);
|
|
}
|
|
|
|
public override getBody(): Uint8Array {
|
|
return new TextEncoder().encode(JSON.stringify(this.toDescriptiveJson(), undefined, 2));
|
|
}
|
|
|
|
public override getHeaders(): Record<string, string[]> {
|
|
return {
|
|
"Content-Type": ["application/json; charset=utf-8"],
|
|
"X-Stack-Known-Error": [this.errorCode],
|
|
};
|
|
}
|
|
|
|
public override toDescriptiveJson(): Json {
|
|
return {
|
|
code: this.errorCode,
|
|
...this.details ? { details: this.details } : {},
|
|
error: this.humanReadableMessage,
|
|
};
|
|
}
|
|
|
|
get errorCode(): string {
|
|
return (this.constructor as any).errorCode ?? throwErr(`Can't find error code for this KnownError. Is its constructor a KnownErrorConstructor? ${this}`);
|
|
}
|
|
|
|
public static constructorArgsFromJson(json: KnownErrorJson): ConstructorParameters<typeof KnownError> {
|
|
return [
|
|
400,
|
|
json.message,
|
|
json,
|
|
];
|
|
}
|
|
|
|
public static fromJson(json: KnownErrorJson): KnownError {
|
|
for (const [_, KnownErrorType] of Object.entries(KnownErrors)) {
|
|
if (json.code === KnownErrorType.prototype.errorCode) {
|
|
const constructorArgs = KnownErrorType.constructorArgsFromJson(json);
|
|
return new KnownErrorType(
|
|
// @ts-expect-error
|
|
...constructorArgs,
|
|
);
|
|
}
|
|
}
|
|
|
|
throw new Error(`Unknown KnownError code. You may need to update your version of Stack to see more detailed information. ${json.code}: ${json.message}`);
|
|
}
|
|
}
|
|
|
|
const knownErrorConstructorErrorCodeSentinel = Symbol("knownErrorConstructorErrorCodeSentinel");
|
|
/**
|
|
* Exists solely so that known errors are nominative types (ie. two KnownErrors with the same interface are not the same type)
|
|
*/
|
|
type KnownErrorBrand<ErrorCode extends string> = {
|
|
/**
|
|
* Does not exist at runtime
|
|
*
|
|
* Must be an object because it may be true for multiple error codes (it's true for all parents)
|
|
*/
|
|
[knownErrorConstructorErrorCodeSentinel]: {
|
|
[K in ErrorCode]: true
|
|
},
|
|
};
|
|
|
|
function createKnownErrorConstructor<ErrorCode extends string, Super extends AbstractKnownErrorConstructor<any>, Args extends any[]>(
|
|
SuperClass: Super,
|
|
errorCode: ErrorCode,
|
|
create: ((...args: Args) => Readonly<ConstructorParameters<Super>>),
|
|
constructorArgsFromJson: ((jsonDetails: any) => Args),
|
|
): KnownErrorConstructor<InstanceType<Super> & KnownErrorBrand<ErrorCode>, Args> & { errorCode: ErrorCode };
|
|
function createKnownErrorConstructor<ErrorCode extends string, Super extends AbstractKnownErrorConstructor<any>>(
|
|
SuperClass: Super,
|
|
errorCode: ErrorCode,
|
|
create: "inherit",
|
|
constructorArgsFromJson: "inherit",
|
|
): KnownErrorConstructor<InstanceType<Super> & KnownErrorBrand<ErrorCode>, ConstructorParameters<Super>> & { errorCode: ErrorCode };
|
|
function createKnownErrorConstructor<ErrorCode extends string, Super extends AbstractKnownErrorConstructor<any>, Args extends any[]>(
|
|
SuperClass: Super,
|
|
errorCode: ErrorCode,
|
|
create: "inherit" | ((...args: Args) => Readonly<ConstructorParameters<Super>>),
|
|
constructorArgsFromJson: "inherit" | ((jsonDetails: any) => Args),
|
|
): KnownErrorConstructor<InstanceType<Super> & KnownErrorBrand<ErrorCode>, Args> & { errorCode: ErrorCode } {
|
|
const createFn = create === "inherit" ? identityArgs<Args> as never : create;
|
|
const constructorArgsFromJsonFn = constructorArgsFromJson === "inherit" ? SuperClass.constructorArgsFromJson as never : constructorArgsFromJson;
|
|
|
|
// @ts-expect-error this is not a mixin, but TS detects it as one
|
|
class KnownErrorImpl extends SuperClass {
|
|
public static readonly errorCode = errorCode;
|
|
public name = `KnownError<${errorCode}>`;
|
|
public readonly constructorArgs: Args;
|
|
|
|
constructor(...args: Args) {
|
|
// @ts-expect-error
|
|
super(...createFn(...args));
|
|
this.constructorArgs = args;
|
|
}
|
|
|
|
static constructorArgsFromJson(json: KnownErrorJson): Args {
|
|
return constructorArgsFromJsonFn(json.details);
|
|
}
|
|
};
|
|
|
|
// @ts-expect-error
|
|
return KnownErrorImpl;
|
|
}
|
|
|
|
const UnsupportedError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"UNSUPPORTED_ERROR",
|
|
(originalErrorCode: string) => [
|
|
500,
|
|
`An error occurred that is not currently supported (possibly because it was added in a version of Stack that is newer than this client). The original unsupported error code was: ${originalErrorCode}`,
|
|
{
|
|
originalErrorCode,
|
|
},
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.originalErrorCode ?? throwErr("originalErrorCode not found in UnsupportedError details"),
|
|
] as const,
|
|
);
|
|
|
|
const BodyParsingError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"BODY_PARSING_ERROR",
|
|
(message: string) => [
|
|
400,
|
|
message,
|
|
] as const,
|
|
(json) => [json.message] as const,
|
|
);
|
|
|
|
const SchemaError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"SCHEMA_ERROR",
|
|
(message: string) => [
|
|
400,
|
|
message || throwErr("SchemaError requires a message"),
|
|
{
|
|
message,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.message] as const,
|
|
);
|
|
|
|
const AllOverloadsFailed = createKnownErrorConstructor(
|
|
KnownError,
|
|
"ALL_OVERLOADS_FAILED",
|
|
(overloadErrors: Json[]) => [
|
|
400,
|
|
deindent`
|
|
This endpoint has multiple overloads, but they all failed to process the request.
|
|
|
|
${overloadErrors.map((e, i) => deindent`
|
|
Overload ${i + 1}: ${JSON.stringify(e, undefined, 2)}
|
|
`).join("\n\n")}
|
|
`,
|
|
{
|
|
overload_errors: overloadErrors,
|
|
},
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.overload_errors ?? throwErr("overload_errors not found in AllOverloadsFailed details"),
|
|
] as const,
|
|
);
|
|
|
|
const ProjectAuthenticationError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PROJECT_AUTHENTICATION_ERROR",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const InvalidProjectAuthentication = createKnownErrorConstructor(
|
|
ProjectAuthenticationError,
|
|
"INVALID_PROJECT_AUTHENTICATION",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
// TODO next-release: delete deprecated error type
|
|
/**
|
|
* @deprecated Use ProjectKeyWithoutAccessType instead
|
|
*/
|
|
const ProjectKeyWithoutRequestType = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"PROJECT_KEY_WITHOUT_REQUEST_TYPE",
|
|
() => [
|
|
400,
|
|
"Either an API key or an admin access token was provided, but the x-stack-access-type header is missing. Set it to 'client', 'server', or 'admin' as appropriate.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
// TODO next-release: delete deprecated error type
|
|
/**
|
|
* @deprecated Use InvalidAccessType instead
|
|
*/
|
|
const InvalidRequestType = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_REQUEST_TYPE",
|
|
(requestType: string) => [
|
|
400,
|
|
`The x-stack-access-type header must be 'client', 'server', or 'admin', but was '${requestType}'.`,
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.requestType ?? throwErr("requestType not found in InvalidRequestType details"),
|
|
] as const,
|
|
);
|
|
|
|
// TODO next-release: delete deprecated error type
|
|
/**
|
|
* @deprecated Use AccessTypeWithoutProjectId instead
|
|
*/
|
|
const RequestTypeWithoutProjectId = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"REQUEST_TYPE_WITHOUT_PROJECT_ID",
|
|
(requestType: "client" | "server" | "admin") => [
|
|
400,
|
|
`The x-stack-access-type header was '${requestType}', but the x-stack-project-id header was not provided.`,
|
|
{
|
|
request_type: requestType,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.request_type] as const,
|
|
);
|
|
|
|
const ProjectKeyWithoutAccessType = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"PROJECT_KEY_WITHOUT_ACCESS_TYPE",
|
|
() => [
|
|
400,
|
|
"Either an API key or an admin access token was provided, but the x-stack-access-type header is missing. Set it to 'client', 'server', or 'admin' as appropriate.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const InvalidAccessType = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_ACCESS_TYPE",
|
|
(accessType: string) => [
|
|
400,
|
|
`The x-stack-access-type header must be 'client', 'server', or 'admin', but was '${accessType}'.`,
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.accessType ?? throwErr("accessType not found in InvalidAccessType details"),
|
|
] as const,
|
|
);
|
|
|
|
const AccessTypeWithoutProjectId = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"ACCESS_TYPE_WITHOUT_PROJECT_ID",
|
|
(accessType: "client" | "server" | "admin") => [
|
|
400,
|
|
deindent`
|
|
The x-stack-access-type header was '${accessType}', but the x-stack-project-id header was not provided.
|
|
|
|
For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/auth#authentication
|
|
`,
|
|
{
|
|
request_type: accessType,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.request_type] as const,
|
|
);
|
|
|
|
const AccessTypeRequired = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"ACCESS_TYPE_REQUIRED",
|
|
() => [
|
|
400,
|
|
deindent`
|
|
You must specify an access level for this Stack project. Make sure project API keys are provided (eg. x-stack-publishable-client-key) and you set the x-stack-access-type header to 'client', 'server', or 'admin'.
|
|
|
|
For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/auth#authentication
|
|
`,
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const InsufficientAccessType = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INSUFFICIENT_ACCESS_TYPE",
|
|
(actualAccessType: "client" | "server" | "admin", allowedAccessTypes: ("client" | "server" | "admin")[]) => [
|
|
401,
|
|
`The x-stack-access-type header must be ${allowedAccessTypes.map(s => `'${s}'`).join(" or ")}, but was '${actualAccessType}'.`,
|
|
{
|
|
actual_access_type: actualAccessType,
|
|
allowed_access_types: allowedAccessTypes,
|
|
},
|
|
] as const,
|
|
(json: any) => [
|
|
json.actual_access_type,
|
|
json.allowed_access_types,
|
|
] as const,
|
|
);
|
|
|
|
const InvalidPublishableClientKey = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_PUBLISHABLE_CLIENT_KEY",
|
|
(projectId: string) => [
|
|
401,
|
|
`The publishable key is not valid for the project ${JSON.stringify(projectId)}. Does the project and/or the key exist?`,
|
|
{
|
|
project_id: projectId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.project_id] as const,
|
|
);
|
|
|
|
const InvalidSecretServerKey = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_SECRET_SERVER_KEY",
|
|
(projectId: string) => [
|
|
401,
|
|
`The secret server key is not valid for the project ${JSON.stringify(projectId)}. Does the project and/or the key exist?`,
|
|
{
|
|
project_id: projectId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.project_id] as const,
|
|
);
|
|
|
|
const InvalidSuperSecretAdminKey = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_SUPER_SECRET_ADMIN_KEY",
|
|
(projectId: string) => [
|
|
401,
|
|
`The super secret admin key is not valid for the project ${JSON.stringify(projectId)}. Does the project and/or the key exist?`,
|
|
{
|
|
project_id: projectId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.project_id] as const,
|
|
);
|
|
|
|
const InvalidAdminAccessToken = createKnownErrorConstructor(
|
|
InvalidProjectAuthentication,
|
|
"INVALID_ADMIN_ACCESS_TOKEN",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const UnparsableAdminAccessToken = createKnownErrorConstructor(
|
|
InvalidAdminAccessToken,
|
|
"UNPARSABLE_ADMIN_ACCESS_TOKEN",
|
|
() => [
|
|
401,
|
|
"Admin access token is not parsable.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const AdminAccessTokenExpired = createKnownErrorConstructor(
|
|
InvalidAdminAccessToken,
|
|
"ADMIN_ACCESS_TOKEN_EXPIRED",
|
|
() => [
|
|
401,
|
|
"Admin access token has expired. Please refresh it and try again.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const InvalidProjectForAdminAccessToken = createKnownErrorConstructor(
|
|
InvalidAdminAccessToken,
|
|
"INVALID_PROJECT_FOR_ADMIN_ACCESS_TOKEN",
|
|
() => [
|
|
401,
|
|
"Admin access tokens must be created on the internal project.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const AdminAccessTokenIsNotAdmin = createKnownErrorConstructor(
|
|
InvalidAdminAccessToken,
|
|
"ADMIN_ACCESS_TOKEN_IS_NOT_ADMIN",
|
|
() => [
|
|
401,
|
|
"Admin access token does not have the required permissions to access this project.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ProjectAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationError,
|
|
"PROJECT_AUTHENTICATION_REQUIRED",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ClientAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"CLIENT_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"The publishable client key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ServerAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"SERVER_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"The secret server key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ClientOrServerAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"CLIENT_OR_SERVER_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"Either the publishable client key or the secret server key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ClientOrAdminAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"CLIENT_OR_ADMIN_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"Either the publishable client key or the super secret admin key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const ClientOrServerOrAdminAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"CLIENT_OR_SERVER_OR_ADMIN_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"Either the publishable client key, the secret server key, or the super secret admin key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
/**
|
|
* @deprecated Use InsufficientAccessType instead
|
|
*/
|
|
const AdminAuthenticationRequired = createKnownErrorConstructor(
|
|
ProjectAuthenticationRequired,
|
|
"ADMIN_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"The super secret admin key must be provided.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const ExpectedInternalProject = createKnownErrorConstructor(
|
|
ProjectAuthenticationError,
|
|
"EXPECTED_INTERNAL_PROJECT",
|
|
() => [
|
|
401,
|
|
"The project ID is expected to be internal.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const SessionAuthenticationError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"SESSION_AUTHENTICATION_ERROR",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const InvalidSessionAuthentication = createKnownErrorConstructor(
|
|
SessionAuthenticationError,
|
|
"INVALID_SESSION_AUTHENTICATION",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const InvalidAccessToken = createKnownErrorConstructor(
|
|
InvalidSessionAuthentication,
|
|
"INVALID_ACCESS_TOKEN",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const UnparsableAccessToken = createKnownErrorConstructor(
|
|
InvalidAccessToken,
|
|
"UNPARSABLE_ACCESS_TOKEN",
|
|
() => [
|
|
401,
|
|
"Access token is not parsable.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const AccessTokenExpired = createKnownErrorConstructor(
|
|
InvalidAccessToken,
|
|
"ACCESS_TOKEN_EXPIRED",
|
|
() => [
|
|
401,
|
|
"Access token has expired. Please refresh it and try again.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const InvalidProjectForAccessToken = createKnownErrorConstructor(
|
|
InvalidAccessToken,
|
|
"INVALID_PROJECT_FOR_ACCESS_TOKEN",
|
|
() => [
|
|
401,
|
|
"Access token not valid for this project.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
|
|
const RefreshTokenError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"REFRESH_TOKEN_ERROR",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const RefreshTokenNotFoundOrExpired = createKnownErrorConstructor(
|
|
RefreshTokenError,
|
|
"REFRESH_TOKEN_NOT_FOUND_OR_EXPIRED",
|
|
() => [
|
|
401,
|
|
"Refresh token not found for this project, or the session has expired/been revoked.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const ProviderRejected = createKnownErrorConstructor(
|
|
RefreshTokenError,
|
|
"PROVIDER_REJECTED",
|
|
() => [
|
|
401,
|
|
"The provider refused to refresh their token. This usually means that the provider used to authenticate the user no longer regards this session as valid, and the user must re-authenticate.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const UserEmailAlreadyExists = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_EMAIL_ALREADY_EXISTS",
|
|
() => [
|
|
400,
|
|
"User already exists.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const CannotGetOwnUserWithoutUser = createKnownErrorConstructor(
|
|
KnownError,
|
|
"CANNOT_GET_OWN_USER_WITHOUT_USER",
|
|
() => [
|
|
400,
|
|
"You have specified 'me' as a userId, but did not provide authentication for a user.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const UserIdDoesNotExist = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_ID_DOES_NOT_EXIST",
|
|
(userId: string) => [
|
|
400,
|
|
`The given user with the ID ${userId} does not exist.`,
|
|
{
|
|
user_id: userId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.user_id] as const,
|
|
);
|
|
|
|
const UserNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_NOT_FOUND",
|
|
() => [
|
|
404,
|
|
"User not found.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const ApiKeyNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"API_KEY_NOT_FOUND",
|
|
() => [
|
|
404,
|
|
"API key not found.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const ProjectNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PROJECT_NOT_FOUND",
|
|
(projectId: string) => {
|
|
if (typeof projectId !== "string") throw new StackAssertionError("projectId of KnownErrors.ProjectNotFound must be a string");
|
|
return [
|
|
404,
|
|
`Project ${projectId} not found or is not accessible with the current user.`,
|
|
{
|
|
project_id: projectId,
|
|
},
|
|
] as const;
|
|
},
|
|
(json: any) => [json.project_id] as const,
|
|
);
|
|
|
|
const SignUpNotEnabled = createKnownErrorConstructor(
|
|
KnownError,
|
|
"SIGN_UP_NOT_ENABLED",
|
|
() => [
|
|
400,
|
|
"Creation of new accounts is not enabled for this project. Please ask the project owner to enable it.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const PasswordAuthenticationNotEnabled = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PASSWORD_AUTHENTICATION_NOT_ENABLED",
|
|
() => [
|
|
400,
|
|
"Password authentication is not enabled for this project.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const EmailPasswordMismatch = createKnownErrorConstructor(
|
|
KnownError,
|
|
"EMAIL_PASSWORD_MISMATCH",
|
|
() => [
|
|
400,
|
|
"Wrong e-mail or password.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const RedirectUrlNotWhitelisted = createKnownErrorConstructor(
|
|
KnownError,
|
|
"REDIRECT_URL_NOT_WHITELISTED",
|
|
() => [
|
|
400,
|
|
"Redirect URL not whitelisted.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const PasswordRequirementsNotMet = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PASSWORD_REQUIREMENTS_NOT_MET",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const PasswordTooShort = createKnownErrorConstructor(
|
|
PasswordRequirementsNotMet,
|
|
"PASSWORD_TOO_SHORT",
|
|
(minLength: number) => [
|
|
400,
|
|
`Password too short. Minimum length is ${minLength}.`,
|
|
{
|
|
min_length: minLength,
|
|
},
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.min_length ?? throwErr("min_length not found in PasswordTooShort details"),
|
|
] as const,
|
|
);
|
|
|
|
const PasswordTooLong = createKnownErrorConstructor(
|
|
PasswordRequirementsNotMet,
|
|
"PASSWORD_TOO_LONG",
|
|
(maxLength: number) => [
|
|
400,
|
|
`Password too long. Maximum length is ${maxLength}.`,
|
|
{
|
|
maxLength,
|
|
},
|
|
] as const,
|
|
(json) => [
|
|
(json as any)?.maxLength ?? throwErr("maxLength not found in PasswordTooLong details"),
|
|
] as const,
|
|
);
|
|
|
|
const UserDoesNotHavePassword = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_DOES_NOT_HAVE_PASSWORD",
|
|
() => [
|
|
400,
|
|
"This user does not have password authentication enabled.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const VerificationCodeError = createKnownErrorConstructor(
|
|
KnownError,
|
|
"VERIFICATION_ERROR",
|
|
"inherit",
|
|
"inherit",
|
|
);
|
|
|
|
const VerificationCodeNotFound = createKnownErrorConstructor(
|
|
VerificationCodeError,
|
|
"VERIFICATION_CODE_NOT_FOUND",
|
|
() => [
|
|
404,
|
|
"The verification code does not exist for this project.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const VerificationCodeExpired = createKnownErrorConstructor(
|
|
VerificationCodeError,
|
|
"VERIFICATION_CODE_EXPIRED",
|
|
() => [
|
|
400,
|
|
"The verification code has expired.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const VerificationCodeAlreadyUsed = createKnownErrorConstructor(
|
|
VerificationCodeError,
|
|
"VERIFICATION_CODE_ALREADY_USED",
|
|
() => [
|
|
400,
|
|
"The verification link has already been used.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const PasswordConfirmationMismatch = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PASSWORD_CONFIRMATION_MISMATCH",
|
|
() => [
|
|
400,
|
|
"Passwords do not match.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const EmailAlreadyVerified = createKnownErrorConstructor(
|
|
KnownError,
|
|
"EMAIL_ALREADY_VERIFIED",
|
|
() => [
|
|
400,
|
|
"The e-mail is already verified.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const EmailNotAssociatedWithUser = createKnownErrorConstructor(
|
|
KnownError,
|
|
"EMAIL_NOT_ASSOCIATED_WITH_USER",
|
|
() => [
|
|
400,
|
|
"The e-mail is not associated with a user that could log in with that e-mail.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const EmailIsNotPrimaryEmail = createKnownErrorConstructor(
|
|
KnownError,
|
|
"EMAIL_IS_NOT_PRIMARY_EMAIL",
|
|
(email: string, primaryEmail: string | null) => [
|
|
400,
|
|
`The given e-mail (${email}) must equal the user's primary e-mail (${primaryEmail}).`,
|
|
{
|
|
email,
|
|
primary_email: primaryEmail,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.email, json.primary_email] as const,
|
|
);
|
|
|
|
const PermissionNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"PERMISSION_NOT_FOUND",
|
|
(permissionId: string) => [
|
|
404,
|
|
`Permission "${permissionId}" not found. Make sure you created it on the dashboard.`,
|
|
{
|
|
permission_id: permissionId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.permission_id] as const,
|
|
);
|
|
|
|
const ContainedPermissionNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"CONTAINED_PERMISSION_NOT_FOUND",
|
|
(permissionId: string) => [
|
|
400,
|
|
`Contained permission with ID "${permissionId}" not found. Make sure you created it on the dashboard.`,
|
|
{
|
|
permission_id: permissionId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.permission_id] as const,
|
|
);
|
|
|
|
const TeamNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_NOT_FOUND",
|
|
(teamId: string) => [
|
|
404,
|
|
`Team ${teamId} not found.`,
|
|
{
|
|
team_id: teamId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.team_id] as const,
|
|
);
|
|
|
|
const TeamAlreadyExists = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_ALREADY_EXISTS",
|
|
(teamId: string) => [
|
|
400,
|
|
`Team ${teamId} already exists.`,
|
|
{
|
|
team_id: teamId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.team_id] as const,
|
|
);
|
|
|
|
const TeamMembershipNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_MEMBERSHIP_NOT_FOUND",
|
|
(teamId: string, userId: string) => [
|
|
404,
|
|
`User ${userId} is not found in team ${teamId}.`,
|
|
{
|
|
team_id: teamId,
|
|
user_id: userId,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.team_id, json.user_id] as const,
|
|
);
|
|
|
|
|
|
const EmailTemplateAlreadyExists = createKnownErrorConstructor(
|
|
KnownError,
|
|
"EMAIL_TEMPLATE_ALREADY_EXISTS",
|
|
() => [
|
|
400,
|
|
"Email template already exists.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthConnectionNotConnectedToUser = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_CONNECTION_NOT_CONNECTED_TO_USER",
|
|
() => [
|
|
400,
|
|
"The OAuth connection is not connected to any user.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthConnectionAlreadyConnectedToAnotherUser = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_CONNECTION_ALREADY_CONNECTED_TO_ANOTHER_USER",
|
|
() => [
|
|
400,
|
|
"The OAuth connection is already connected to another user.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthConnectionDoesNotHaveRequiredScope = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_CONNECTION_DOES_NOT_HAVE_REQUIRED_SCOPE",
|
|
() => [
|
|
400,
|
|
"The OAuth connection does not have the required scope.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthExtraScopeNotAvailableWithSharedOAuthKeys = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_EXTRA_SCOPE_NOT_AVAILABLE_WITH_SHARED_OAUTH_KEYS",
|
|
() => [
|
|
400,
|
|
"Extra scopes are not available with shared OAuth keys. Please add your own OAuth keys on the Stack dashboard to use extra scopes.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthAccessTokenNotAvailableWithSharedOAuthKeys = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_ACCESS_TOKEN_NOT_AVAILABLE_WITH_SHARED_OAUTH_KEYS",
|
|
() => [
|
|
400,
|
|
"Access tokens are not available with shared OAuth keys. Please add your own OAuth keys on the Stack dashboard to use access tokens.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const InvalidOAuthClientIdOrSecret = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_OAUTH_CLIENT_ID_OR_SECRET",
|
|
(clientId?: string) => [
|
|
400,
|
|
"The OAuth client ID or secret is invalid. The client ID must be equal to the project ID, and the client secret must be a publishable client key.",
|
|
{
|
|
client_id: clientId ?? null,
|
|
},
|
|
] as const,
|
|
(json: any) => [json.client_id ?? undefined] as const,
|
|
);
|
|
|
|
const InvalidScope = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_SCOPE",
|
|
(scope: string) => [
|
|
400,
|
|
`The scope "${scope}" is not a valid OAuth scope for Stack.`,
|
|
] as const,
|
|
(json: any) => [json.scope] as const,
|
|
);
|
|
|
|
const UserAlreadyConnectedToAnotherOAuthConnection = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_ALREADY_CONNECTED_TO_ANOTHER_OAUTH_CONNECTION",
|
|
() => [
|
|
400,
|
|
"The user is already connected to another OAuth account. Did you maybe selected the wrong account?",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OuterOAuthTimeout = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OUTER_OAUTH_TIMEOUT",
|
|
() => [
|
|
408,
|
|
"The OAuth flow has timed out. Please sign in again.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const OAuthProviderNotFoundOrNotEnabled = createKnownErrorConstructor(
|
|
KnownError,
|
|
"OAUTH_PROVIDER_NOT_FOUND_OR_NOT_ENABLED",
|
|
() => [
|
|
400,
|
|
"The OAuth provider is not found or not enabled.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const MultiFactorAuthenticationRequired = createKnownErrorConstructor(
|
|
KnownError,
|
|
"MULTI_FACTOR_AUTHENTICATION_REQUIRED",
|
|
(attemptCode: string) => [
|
|
400,
|
|
`Multi-factor authentication is required for this user.`,
|
|
{
|
|
attempt_code: attemptCode,
|
|
},
|
|
] as const,
|
|
(json) => [json.attempt_code] as const,
|
|
);
|
|
|
|
const InvalidTotpCode = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_TOTP_CODE",
|
|
() => [
|
|
400,
|
|
"The TOTP code is invalid. Please try again.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const UserAuthenticationRequired = createKnownErrorConstructor(
|
|
KnownError,
|
|
"USER_AUTHENTICATION_REQUIRED",
|
|
() => [
|
|
401,
|
|
"User authentication required for this endpoint.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const TeamMembershipAlreadyExists = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_MEMBERSHIP_ALREADY_EXISTS",
|
|
() => [
|
|
400,
|
|
"Team membership already exists.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
const TeamPermissionRequired = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_PERMISSION_REQUIRED",
|
|
(teamId, userId, permissionId) => [
|
|
401,
|
|
`User ${userId} does not have permission ${permissionId} in team ${teamId}.`,
|
|
{
|
|
team_id: teamId,
|
|
user_id: userId,
|
|
permission_id: permissionId,
|
|
},
|
|
] as const,
|
|
(json) => [json.team_id, json.user_id, json.permission_id] as const,
|
|
);
|
|
|
|
const TeamPermissionNotFound = createKnownErrorConstructor(
|
|
KnownError,
|
|
"TEAM_PERMISSION_NOT_FOUND",
|
|
(teamId, userId, permissionId) => [
|
|
401,
|
|
`User ${userId} does not have permission ${permissionId} in team ${teamId}.`,
|
|
{
|
|
team_id: teamId,
|
|
user_id: userId,
|
|
permission_id: permissionId,
|
|
},
|
|
] as const,
|
|
(json) => [json.team_id, json.user_id, json.permission_id] as const,
|
|
);
|
|
|
|
const InvalidSharedOAuthProviderId = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_SHARED_OAUTH_PROVIDER_ID",
|
|
(providerId) => [
|
|
400,
|
|
`The shared OAuth provider with ID ${providerId} is not valid.`,
|
|
{
|
|
provider_id: providerId,
|
|
},
|
|
] as const,
|
|
(json) => [json.provider_id] as const,
|
|
);
|
|
|
|
const InvalidStandardOAuthProviderId = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_STANDARD_OAUTH_PROVIDER_ID",
|
|
(providerId) => [
|
|
400,
|
|
`The standard OAuth provider with ID ${providerId} is not valid.`,
|
|
{
|
|
provider_id: providerId,
|
|
},
|
|
] as const,
|
|
(json) => [json.provider_id] as const,
|
|
);
|
|
|
|
const InvalidAuthorizationCode = createKnownErrorConstructor(
|
|
KnownError,
|
|
"INVALID_AUTHORIZATION_CODE",
|
|
() => [
|
|
400,
|
|
"The given authorization code is invalid.",
|
|
] as const,
|
|
() => [] as const,
|
|
);
|
|
|
|
export type KnownErrors = {
|
|
[K in keyof typeof KnownErrors]: InstanceType<typeof KnownErrors[K]>;
|
|
};
|
|
|
|
export const KnownErrors = {
|
|
UnsupportedError,
|
|
BodyParsingError,
|
|
SchemaError,
|
|
AllOverloadsFailed,
|
|
ProjectAuthenticationError,
|
|
InvalidProjectAuthentication,
|
|
ProjectKeyWithoutRequestType,
|
|
InvalidRequestType,
|
|
RequestTypeWithoutProjectId,
|
|
ProjectKeyWithoutAccessType,
|
|
InvalidAccessType,
|
|
AccessTypeWithoutProjectId,
|
|
AccessTypeRequired,
|
|
CannotGetOwnUserWithoutUser,
|
|
InsufficientAccessType,
|
|
InvalidPublishableClientKey,
|
|
InvalidSecretServerKey,
|
|
InvalidSuperSecretAdminKey,
|
|
InvalidAdminAccessToken,
|
|
UnparsableAdminAccessToken,
|
|
AdminAccessTokenExpired,
|
|
InvalidProjectForAdminAccessToken,
|
|
AdminAccessTokenIsNotAdmin,
|
|
ProjectAuthenticationRequired,
|
|
ClientAuthenticationRequired,
|
|
ServerAuthenticationRequired,
|
|
ClientOrServerAuthenticationRequired,
|
|
ClientOrAdminAuthenticationRequired,
|
|
ClientOrServerOrAdminAuthenticationRequired,
|
|
AdminAuthenticationRequired,
|
|
ExpectedInternalProject,
|
|
SessionAuthenticationError,
|
|
InvalidSessionAuthentication,
|
|
InvalidAccessToken,
|
|
UnparsableAccessToken,
|
|
AccessTokenExpired,
|
|
InvalidProjectForAccessToken,
|
|
RefreshTokenError,
|
|
ProviderRejected,
|
|
RefreshTokenNotFoundOrExpired,
|
|
UserEmailAlreadyExists,
|
|
UserIdDoesNotExist,
|
|
UserNotFound,
|
|
ApiKeyNotFound,
|
|
ProjectNotFound,
|
|
SignUpNotEnabled,
|
|
PasswordAuthenticationNotEnabled,
|
|
EmailPasswordMismatch,
|
|
RedirectUrlNotWhitelisted,
|
|
PasswordRequirementsNotMet,
|
|
PasswordTooShort,
|
|
PasswordTooLong,
|
|
UserDoesNotHavePassword,
|
|
VerificationCodeError,
|
|
VerificationCodeNotFound,
|
|
VerificationCodeExpired,
|
|
VerificationCodeAlreadyUsed,
|
|
PasswordConfirmationMismatch,
|
|
EmailAlreadyVerified,
|
|
EmailNotAssociatedWithUser,
|
|
EmailIsNotPrimaryEmail,
|
|
PermissionNotFound,
|
|
ContainedPermissionNotFound,
|
|
TeamNotFound,
|
|
TeamMembershipNotFound,
|
|
EmailTemplateAlreadyExists,
|
|
OAuthConnectionNotConnectedToUser,
|
|
OAuthConnectionAlreadyConnectedToAnotherUser,
|
|
OAuthConnectionDoesNotHaveRequiredScope,
|
|
OAuthExtraScopeNotAvailableWithSharedOAuthKeys,
|
|
OAuthAccessTokenNotAvailableWithSharedOAuthKeys,
|
|
InvalidOAuthClientIdOrSecret,
|
|
InvalidScope,
|
|
UserAlreadyConnectedToAnotherOAuthConnection,
|
|
OuterOAuthTimeout,
|
|
OAuthProviderNotFoundOrNotEnabled,
|
|
MultiFactorAuthenticationRequired,
|
|
InvalidTotpCode,
|
|
UserAuthenticationRequired,
|
|
TeamMembershipAlreadyExists,
|
|
TeamPermissionRequired,
|
|
InvalidSharedOAuthProviderId,
|
|
InvalidStandardOAuthProviderId,
|
|
InvalidAuthorizationCode,
|
|
TeamPermissionNotFound,
|
|
} satisfies Record<string, KnownErrorConstructor<any, any>>;
|
|
|
|
|
|
// ensure that all known error codes are unique
|
|
const knownErrorCodes = new Set<string>();
|
|
for (const [_, KnownError] of Object.entries(KnownErrors)) {
|
|
if (knownErrorCodes.has(KnownError.errorCode)) {
|
|
throw new Error(`Duplicate known error code: ${KnownError.errorCode}`);
|
|
}
|
|
knownErrorCodes.add(KnownError.errorCode);
|
|
}
|