UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

1 lines • 26.2 kB
{"version":3,"file":"index.mjs","names":["username","ERROR_CODES"],"sources":["../../../src/plugins/username/index.ts"],"sourcesContent":["import type { BetterAuthPlugin } from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport type { Account, User } from \"@better-auth/core/db\";\nimport { BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { APIError } from \"better-call\";\nimport * as z from \"zod\";\nimport { createEmailVerificationToken } from \"../../api\";\nimport { setSessionCookie } from \"../../cookies\";\nimport { mergeSchema } from \"../../db\";\nimport type { InferOptionSchema } from \"../../types/plugins\";\nimport { USERNAME_ERROR_CODES as ERROR_CODES } from \"./error-codes\";\nimport type { UsernameSchema } from \"./schema\";\nimport { getSchema } from \"./schema\";\n\nexport { USERNAME_ERROR_CODES } from \"./error-codes\";\n\nexport type UsernameOptions = {\n\tschema?: InferOptionSchema<UsernameSchema> | undefined;\n\t/**\n\t * The minimum length of the username\n\t *\n\t * @default 3\n\t */\n\tminUsernameLength?: number | undefined;\n\t/**\n\t * The maximum length of the username\n\t *\n\t * @default 30\n\t */\n\tmaxUsernameLength?: number | undefined;\n\t/**\n\t * A function to validate the username\n\t *\n\t * By default, the username should only contain alphanumeric characters and underscores\n\t */\n\tusernameValidator?:\n\t\t| ((username: string) => boolean | Promise<boolean>)\n\t\t| undefined;\n\t/**\n\t * A function to validate the display username\n\t *\n\t * By default, no validation is applied to display username\n\t */\n\tdisplayUsernameValidator?:\n\t\t| ((displayUsername: string) => boolean | Promise<boolean>)\n\t\t| undefined;\n\t/**\n\t * A function to normalize the username\n\t *\n\t * @default (username) => username.toLowerCase()\n\t */\n\tusernameNormalization?: (((username: string) => string) | false) | undefined;\n\t/**\n\t * A function to normalize the display username\n\t *\n\t * @default false\n\t */\n\tdisplayUsernameNormalization?:\n\t\t| (((displayUsername: string) => string) | false)\n\t\t| undefined;\n\t/**\n\t * The order of validation\n\t *\n\t * @default { username: \"pre-normalization\", displayUsername: \"pre-normalization\" }\n\t */\n\tvalidationOrder?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * The order of username validation\n\t\t\t\t *\n\t\t\t\t * @default \"pre-normalization\"\n\t\t\t\t */\n\t\t\t\tusername?: \"pre-normalization\" | \"post-normalization\";\n\t\t\t\t/**\n\t\t\t\t * The order of display username validation\n\t\t\t\t *\n\t\t\t\t * @default \"pre-normalization\"\n\t\t\t\t */\n\t\t\t\tdisplayUsername?: \"pre-normalization\" | \"post-normalization\";\n\t\t }\n\t\t| undefined;\n};\n\nfunction defaultUsernameValidator(username: string) {\n\treturn /^[a-zA-Z0-9_.]+$/.test(username);\n}\n\nconst signInUsernameBodySchema = z.object({\n\tusername: z.string().meta({ description: \"The username of the user\" }),\n\tpassword: z.string().meta({ description: \"The password of the user\" }),\n\trememberMe: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription: \"Remember the user session\",\n\t\t})\n\t\t.optional(),\n\tcallbackURL: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"The URL to redirect to after email verification\",\n\t\t})\n\t\t.optional(),\n});\n\nconst isUsernameAvailableBodySchema = z.object({\n\tusername: z.string().meta({\n\t\tdescription: \"The username to check\",\n\t}),\n});\n\nexport const username = (options?: UsernameOptions | undefined) => {\n\tconst normalizer = (username: string) => {\n\t\tif (options?.usernameNormalization === false) {\n\t\t\treturn username;\n\t\t}\n\t\tif (options?.usernameNormalization) {\n\t\t\treturn options.usernameNormalization(username);\n\t\t}\n\t\treturn username.toLowerCase();\n\t};\n\n\tconst displayUsernameNormalizer = (displayUsername: string) => {\n\t\treturn options?.displayUsernameNormalization\n\t\t\t? options.displayUsernameNormalization(displayUsername)\n\t\t\t: displayUsername;\n\t};\n\n\treturn {\n\t\tid: \"username\",\n\t\tinit(ctx) {\n\t\t\treturn {\n\t\t\t\toptions: {\n\t\t\t\t\tdatabaseHooks: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tcreate: {\n\t\t\t\t\t\t\t\tasync before(user, context) {\n\t\t\t\t\t\t\t\t\tconst username =\n\t\t\t\t\t\t\t\t\t\t\"username\" in user ? (user.username as string) : null;\n\t\t\t\t\t\t\t\t\tconst displayUsername =\n\t\t\t\t\t\t\t\t\t\t\"displayUsername\" in user\n\t\t\t\t\t\t\t\t\t\t\t? (user.displayUsername as string)\n\t\t\t\t\t\t\t\t\t\t\t: null;\n\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t...(username ? { username: normalizer(username) } : {}),\n\t\t\t\t\t\t\t\t\t\t\t...(displayUsername\n\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisplayUsername:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisplayUsernameNormalizer(displayUsername),\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tupdate: {\n\t\t\t\t\t\t\t\tasync before(user, context) {\n\t\t\t\t\t\t\t\t\tconst username =\n\t\t\t\t\t\t\t\t\t\t\"username\" in user ? (user.username as string) : null;\n\t\t\t\t\t\t\t\t\tconst displayUsername =\n\t\t\t\t\t\t\t\t\t\t\"displayUsername\" in user\n\t\t\t\t\t\t\t\t\t\t\t? (user.displayUsername as string)\n\t\t\t\t\t\t\t\t\t\t\t: null;\n\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\t\t...user,\n\t\t\t\t\t\t\t\t\t\t\t...(username ? { username: normalizer(username) } : {}),\n\t\t\t\t\t\t\t\t\t\t\t...(displayUsername\n\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisplayUsername:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisplayUsernameNormalizer(displayUsername),\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tendpoints: {\n\t\t\tsignInUsername: createAuthEndpoint(\n\t\t\t\t\"/sign-in/username\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: signInUsernameBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tsummary: \"Sign in with username\",\n\t\t\t\t\t\t\tdescription: \"Sign in with username\",\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\ttoken: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Session token for the authenticated session\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/User\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"token\", \"user\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t422: {\n\t\t\t\t\t\t\t\t\tdescription: \"Unprocessable Entity. Validation error\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tmessage: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tif (!ctx.body.username || !ctx.body.password) {\n\t\t\t\t\t\tctx.context.logger.error(\"Username or password not found\");\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst username =\n\t\t\t\t\t\toptions?.validationOrder?.username === \"pre-normalization\"\n\t\t\t\t\t\t\t? normalizer(ctx.body.username)\n\t\t\t\t\t\t\t: ctx.body.username;\n\n\t\t\t\t\tconst minUsernameLength = options?.minUsernameLength || 3;\n\t\t\t\t\tconst maxUsernameLength = options?.maxUsernameLength || 30;\n\n\t\t\t\t\tif (username.length < minUsernameLength) {\n\t\t\t\t\t\tctx.context.logger.error(\"Username too short\", {\n\t\t\t\t\t\t\tusername,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tcode: \"USERNAME_TOO_SHORT\",\n\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_SHORT,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (username.length > maxUsernameLength) {\n\t\t\t\t\t\tctx.context.logger.error(\"Username too long\", {\n\t\t\t\t\t\t\tusername,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_LONG,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst validator =\n\t\t\t\t\t\toptions?.usernameValidator || defaultUsernameValidator;\n\n\t\t\t\t\tconst valid = await validator(username);\n\t\t\t\t\tif (!valid) {\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst user = await ctx.context.adapter.findOne<\n\t\t\t\t\t\tUser & { username: string; displayUsername: string }\n\t\t\t\t\t>({\n\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"username\",\n\t\t\t\t\t\t\t\tvalue: normalizer(username),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!user) {\n\t\t\t\t\t\t// Hash password to prevent timing attacks from revealing valid usernames\n\t\t\t\t\t\t// By hashing passwords for invalid usernames, we ensure consistent response times\n\t\t\t\t\t\tawait ctx.context.password.hash(ctx.body.password);\n\t\t\t\t\t\tctx.context.logger.error(\"User not found\", {\n\t\t\t\t\t\t\tusername,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst account = await ctx.context.adapter.findOne<Account>({\n\t\t\t\t\t\tmodel: \"account\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"providerId\",\n\t\t\t\t\t\t\t\tvalue: \"credential\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (!account) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tconst currentPassword = account?.password;\n\t\t\t\t\tif (!currentPassword) {\n\t\t\t\t\t\tctx.context.logger.error(\"Password not found\", {\n\t\t\t\t\t\t\tusername,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tconst validPassword = await ctx.context.password.verify({\n\t\t\t\t\t\thash: currentPassword,\n\t\t\t\t\t\tpassword: ctx.body.password,\n\t\t\t\t\t});\n\t\t\t\t\tif (!validPassword) {\n\t\t\t\t\t\tctx.context.logger.error(\"Invalid password\");\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tctx.context.options?.emailAndPassword?.requireEmailVerification &&\n\t\t\t\t\t\t!user.emailVerified\n\t\t\t\t\t) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t!ctx.context.options?.emailVerification?.sendVerificationEmail\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\t\t\t\tmessage: ERROR_CODES.EMAIL_NOT_VERIFIED,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (ctx.context.options?.emailVerification?.sendOnSignIn) {\n\t\t\t\t\t\t\tconst token = await createEmailVerificationToken(\n\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\tuser.email,\n\t\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t\tctx.context.options.emailVerification?.expiresIn,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${\n\t\t\t\t\t\t\t\tctx.body.callbackURL || \"/\"\n\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\tawait ctx.context.runInBackgroundOrAwait(\n\t\t\t\t\t\t\t\tctx.context.options.emailVerification.sendVerificationEmail(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tuser: user,\n\t\t\t\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t\t\t\ttoken,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tctx.request,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.EMAIL_NOT_VERIFIED,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst session = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\tctx.body.rememberMe === false,\n\t\t\t\t\t);\n\t\t\t\t\tif (!session) {\n\t\t\t\t\t\treturn ctx.json(null, {\n\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\tmessage: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tawait setSessionCookie(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t{ session, user },\n\t\t\t\t\t\tctx.body.rememberMe === false,\n\t\t\t\t\t);\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\ttoken: session.token,\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\t\temailVerified: user.emailVerified,\n\t\t\t\t\t\t\tusername: user.username,\n\t\t\t\t\t\t\tdisplayUsername: user.displayUsername,\n\t\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\t\timage: user.image,\n\t\t\t\t\t\t\tcreatedAt: user.createdAt,\n\t\t\t\t\t\t\tupdatedAt: user.updatedAt,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\t\t\tisUsernameAvailable: createAuthEndpoint(\n\t\t\t\t\"/is-username-available\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: isUsernameAvailableBodySchema,\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst username = ctx.body.username;\n\t\t\t\t\tif (!username) {\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst minUsernameLength = options?.minUsernameLength || 3;\n\t\t\t\t\tconst maxUsernameLength = options?.maxUsernameLength || 30;\n\n\t\t\t\t\tif (username.length < minUsernameLength) {\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tcode: \"USERNAME_TOO_SHORT\",\n\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_SHORT,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (username.length > maxUsernameLength) {\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_LONG,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst validator =\n\t\t\t\t\t\toptions?.usernameValidator || defaultUsernameValidator;\n\n\t\t\t\t\tconst valid = await validator(username);\n\t\t\t\t\tif (!valid) {\n\t\t\t\t\t\tthrow new APIError(\"UNPROCESSABLE_ENTITY\", {\n\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tconst user = await ctx.context.adapter.findOne<User>({\n\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfield: \"username\",\n\t\t\t\t\t\t\t\tvalue: normalizer(username),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t});\n\t\t\t\t\tif (user) {\n\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\tavailable: false,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tavailable: true,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\tschema: mergeSchema(\n\t\t\tgetSchema({\n\t\t\t\tusername: normalizer,\n\t\t\t\tdisplayUsername: displayUsernameNormalizer,\n\t\t\t}),\n\t\t\toptions?.schema,\n\t\t),\n\t\thooks: {\n\t\t\tbefore: [\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tcontext.path === \"/sign-up/email\" ||\n\t\t\t\t\t\t\tcontext.path === \"/update-user\"\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst username =\n\t\t\t\t\t\t\ttypeof ctx.body.username === \"string\" &&\n\t\t\t\t\t\t\toptions?.validationOrder?.username === \"post-normalization\"\n\t\t\t\t\t\t\t\t? normalizer(ctx.body.username)\n\t\t\t\t\t\t\t\t: ctx.body.username;\n\n\t\t\t\t\t\tif (username !== undefined && typeof username === \"string\") {\n\t\t\t\t\t\t\tconst minUsernameLength = options?.minUsernameLength || 3;\n\t\t\t\t\t\t\tconst maxUsernameLength = options?.maxUsernameLength || 30;\n\t\t\t\t\t\t\tif (username.length < minUsernameLength) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\tcode: \"USERNAME_TOO_SHORT\",\n\t\t\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_SHORT,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (username.length > maxUsernameLength) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_TOO_LONG,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst validator =\n\t\t\t\t\t\t\t\toptions?.usernameValidator || defaultUsernameValidator;\n\n\t\t\t\t\t\t\tconst valid = await validator(username);\n\t\t\t\t\t\t\tif (!valid) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_USERNAME,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst user = await ctx.context.adapter.findOne<User>({\n\t\t\t\t\t\t\t\tmodel: \"user\",\n\t\t\t\t\t\t\t\twhere: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tfield: \"username\",\n\t\t\t\t\t\t\t\t\t\tvalue: username,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst blockChangeSignUp = ctx.path === \"/sign-up/email\" && user;\n\t\t\t\t\t\t\tconst blockChangeUpdateUser =\n\t\t\t\t\t\t\t\tctx.path === \"/update-user\" &&\n\t\t\t\t\t\t\t\tuser &&\n\t\t\t\t\t\t\t\tctx.context.session &&\n\t\t\t\t\t\t\t\tuser.id !== ctx.context.session.session.userId;\n\t\t\t\t\t\t\tif (blockChangeSignUp || blockChangeUpdateUser) {\n\t\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\tmessage: ERROR_CODES.USERNAME_IS_ALREADY_TAKEN,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst displayUsername =\n\t\t\t\t\t\t\ttypeof ctx.body.displayUsername === \"string\" &&\n\t\t\t\t\t\t\toptions?.validationOrder?.displayUsername === \"post-normalization\"\n\t\t\t\t\t\t\t\t? displayUsernameNormalizer(ctx.body.displayUsername)\n\t\t\t\t\t\t\t\t: ctx.body.displayUsername;\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tdisplayUsername !== undefined &&\n\t\t\t\t\t\t\ttypeof displayUsername === \"string\"\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tif (options?.displayUsernameValidator) {\n\t\t\t\t\t\t\t\tconst valid =\n\t\t\t\t\t\t\t\t\tawait options.displayUsernameValidator(displayUsername);\n\t\t\t\t\t\t\t\tif (!valid) {\n\t\t\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\t\t\tmessage: ERROR_CODES.INVALID_DISPLAY_USERNAME,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmatcher(context) {\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tcontext.path === \"/sign-up/email\" ||\n\t\t\t\t\t\t\tcontext.path === \"/update-user\"\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tif (ctx.body.username && !ctx.body.displayUsername) {\n\t\t\t\t\t\t\tctx.body.displayUsername = ctx.body.username;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ctx.body.displayUsername && !ctx.body.username) {\n\t\t\t\t\t\t\tctx.body.username = ctx.body.displayUsername;\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\t\toptions,\n\t\t$ERROR_CODES: ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n"],"mappings":"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"}