better-auth 1.5.4 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/index.d.mts +25 -1
- package/dist/adapters/index.mjs +9 -1
- package/dist/adapters/index.mjs.map +1 -0
- package/dist/api/index.d.mts +36 -10
- package/dist/api/index.mjs +19 -4
- package/dist/api/index.mjs.map +1 -1
- package/dist/api/middlewares/origin-check.mjs +17 -8
- package/dist/api/middlewares/origin-check.mjs.map +1 -1
- package/dist/api/routes/account.d.mts +1 -1
- package/dist/api/routes/email-verification.d.mts +0 -1
- package/dist/api/routes/password.d.mts +1 -0
- package/dist/api/routes/password.mjs +2 -1
- package/dist/api/routes/password.mjs.map +1 -1
- package/dist/api/routes/session.d.mts +0 -1
- package/dist/api/routes/sign-in.d.mts +16 -2
- package/dist/api/routes/sign-in.mjs +10 -2
- package/dist/api/routes/sign-in.mjs.map +1 -1
- package/dist/api/routes/sign-up.d.mts +0 -1
- package/dist/api/routes/sign-up.mjs +3 -2
- package/dist/api/routes/sign-up.mjs.map +1 -1
- package/dist/api/routes/update-session.d.mts +0 -1
- package/dist/api/routes/update-user.d.mts +0 -1
- package/dist/api/to-auth-endpoints.mjs +49 -12
- package/dist/api/to-auth-endpoints.mjs.map +1 -1
- package/dist/auth/full.d.mts +0 -1
- package/dist/auth/minimal.d.mts +0 -1
- package/dist/client/index.d.mts +3 -4
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/path-to-object.d.mts +9 -2
- package/dist/client/query.mjs +3 -2
- package/dist/client/query.mjs.map +1 -1
- package/dist/client/session-refresh.d.mts +11 -3
- package/dist/client/session-refresh.mjs +13 -8
- package/dist/client/session-refresh.mjs.map +1 -1
- package/dist/client/types.d.mts +0 -1
- package/dist/context/create-context.mjs +4 -1
- package/dist/context/create-context.mjs.map +1 -1
- package/dist/context/helpers.mjs +10 -4
- package/dist/context/helpers.mjs.map +1 -1
- package/dist/cookies/index.d.mts +0 -1
- package/dist/cookies/session-store.d.mts +0 -2
- package/dist/db/get-migration.mjs +3 -2
- package/dist/db/get-migration.mjs.map +1 -1
- package/dist/db/index.d.mts +2 -2
- package/dist/db/internal-adapter.d.mts +2 -1
- package/dist/db/internal-adapter.mjs +1 -1
- package/dist/db/internal-adapter.mjs.map +1 -1
- package/dist/db/schema.d.mts +0 -1
- package/dist/db/with-hooks.d.mts +6 -2
- package/dist/db/with-hooks.mjs +72 -31
- package/dist/db/with-hooks.mjs.map +1 -1
- package/dist/index.d.mts +0 -2
- package/dist/integrations/node.d.mts +0 -1
- package/dist/oauth2/link-account.d.mts +0 -1
- package/dist/plugins/admin/access/statement.d.mts +0 -2
- package/dist/plugins/admin/admin.d.mts +0 -1
- package/dist/plugins/admin/client.d.mts +0 -2
- package/dist/plugins/admin/types.d.mts +0 -2
- package/dist/plugins/anonymous/types.d.mts +0 -1
- package/dist/plugins/email-otp/index.mjs +2 -1
- package/dist/plugins/email-otp/index.mjs.map +1 -1
- package/dist/plugins/email-otp/otp-token.mjs +31 -2
- package/dist/plugins/email-otp/otp-token.mjs.map +1 -1
- package/dist/plugins/email-otp/routes.mjs +60 -59
- package/dist/plugins/email-otp/routes.mjs.map +1 -1
- package/dist/plugins/email-otp/types.d.mts +12 -0
- package/dist/plugins/email-otp/utils.mjs +4 -1
- package/dist/plugins/email-otp/utils.mjs.map +1 -1
- package/dist/plugins/generic-oauth/client.d.mts +0 -1
- package/dist/plugins/generic-oauth/index.d.mts +0 -1
- package/dist/plugins/index.d.mts +0 -3
- package/dist/plugins/jwt/types.d.mts +0 -1
- package/dist/plugins/magic-link/index.d.mts +2 -0
- package/dist/plugins/magic-link/index.mjs +5 -3
- package/dist/plugins/magic-link/index.mjs.map +1 -1
- package/dist/plugins/mcp/index.d.mts +0 -1
- package/dist/plugins/oidc-provider/authorize.mjs +13 -4
- package/dist/plugins/oidc-provider/authorize.mjs.map +1 -1
- package/dist/plugins/oidc-provider/error.mjs +12 -2
- package/dist/plugins/oidc-provider/error.mjs.map +1 -1
- package/dist/plugins/oidc-provider/index.d.mts +0 -1
- package/dist/plugins/oidc-provider/types.d.mts +0 -1
- package/dist/plugins/one-time-token/index.d.mts +0 -1
- package/dist/plugins/organization/access/statement.d.mts +0 -2
- package/dist/plugins/organization/adapter.d.mts +0 -2
- package/dist/plugins/organization/adapter.mjs +2 -2
- package/dist/plugins/organization/adapter.mjs.map +1 -1
- package/dist/plugins/organization/client.d.mts +0 -5
- package/dist/plugins/organization/organization.d.mts +0 -2
- package/dist/plugins/organization/permission.d.mts +0 -1
- package/dist/plugins/organization/routes/crud-access-control.d.mts +0 -2
- package/dist/plugins/organization/routes/crud-invites.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-invites.mjs +1 -1
- package/dist/plugins/organization/routes/crud-invites.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-members.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-members.mjs +1 -1
- package/dist/plugins/organization/routes/crud-members.mjs.map +1 -1
- package/dist/plugins/organization/routes/crud-org.d.mts +0 -3
- package/dist/plugins/organization/routes/crud-team.d.mts +2 -3
- package/dist/plugins/organization/routes/crud-team.mjs +18 -14
- package/dist/plugins/organization/routes/crud-team.mjs.map +1 -1
- package/dist/plugins/organization/schema.d.mts +0 -1
- package/dist/plugins/organization/types.d.mts +0 -2
- package/dist/plugins/phone-number/types.d.mts +0 -1
- package/dist/plugins/siwe/index.d.mts +0 -1
- package/dist/plugins/test-utils/types.d.mts +0 -2
- package/dist/plugins/two-factor/client.d.mts +7 -0
- package/dist/plugins/two-factor/client.mjs +5 -1
- package/dist/plugins/two-factor/client.mjs.map +1 -1
- package/dist/plugins/two-factor/index.mjs +7 -1
- package/dist/plugins/two-factor/index.mjs.map +1 -1
- package/dist/plugins/two-factor/otp/index.d.mts +2 -2
- package/dist/plugins/two-factor/otp/index.mjs.map +1 -1
- package/dist/plugins/two-factor/types.d.mts +7 -1
- package/dist/test-utils/test-instance.d.mts +108 -21
- package/dist/types/index.d.mts +0 -1
- package/package.json +13 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud-team.mjs","names":[],"sources":["../../../../src/plugins/organization/routes/crud-team.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport * as z from \"zod\";\nimport { getSessionFromCtx } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport type { InferAdditionalFieldsFromPluginOptions } from \"../../../db\";\nimport { toZodSchema } from \"../../../db\";\nimport type { PrettifyDeep } from \"../../../types/helper\";\nimport { getOrgAdapter } from \"../adapter\";\nimport { orgMiddleware, orgSessionMiddleware } from \"../call\";\nimport { ORGANIZATION_ERROR_CODES } from \"../error-codes\";\nimport { hasPermission } from \"../has-permission\";\nimport { teamSchema } from \"../schema\";\nimport type { OrganizationOptions } from \"../types\";\n\nconst teamBaseSchema = z.object({\n\tname: z.string().meta({\n\t\tdescription: 'The name of the team. Eg: \"my-team\"',\n\t}),\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The organization ID which the team will be created in. Defaults to the active organization. Eg: \"organization-id\"',\n\t\t})\n\t\t.optional(),\n});\n\nexport const createTeam = <O extends OrganizationOptions>(options: O) => {\n\tconst additionalFieldsSchema = toZodSchema({\n\t\tfields: options?.schema?.team?.additionalFields ?? {},\n\t\tisClientSide: true,\n\t});\n\treturn createAuthEndpoint(\n\t\t\"/organization/create-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: z.object({\n\t\t\t\t...teamBaseSchema.shape,\n\t\t\t\t...additionalFieldsSchema.shape,\n\t\t\t}),\n\t\t\tuse: [orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\t$Infer: {\n\t\t\t\t\tbody: {} as z.infer<typeof teamBaseSchema> &\n\t\t\t\t\t\tInferAdditionalFieldsFromPluginOptions<\"team\", O>,\n\t\t\t\t},\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Create a new team within an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team created successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the created team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tname: {\n\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\tdescription: \"Name of the team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tupdatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tasync (ctx) => {\n\t\t\tconst session = await getSessionFromCtx(ctx);\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!session && (ctx.request || ctx.headers)) {\n\t\t\t\tthrow APIError.fromStatus(\"UNAUTHORIZED\");\n\t\t\t}\n\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options as O);\n\t\t\tif (session) {\n\t\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\t\tuserId: session.user.id,\n\t\t\t\t\torganizationId,\n\t\t\t\t});\n\t\t\t\tif (!member) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst canCreate = await hasPermission(\n\t\t\t\t\t{\n\t\t\t\t\t\trole: member.role,\n\t\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\t\tpermissions: {\n\t\t\t\t\t\t\tteam: [\"create\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tif (!canCreate) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst existingTeams = await adapter.listTeams(organizationId);\n\t\t\tconst maximum =\n\t\t\t\ttypeof ctx.context.orgOptions.teams?.maximumTeams === \"function\"\n\t\t\t\t\t? await ctx.context.orgOptions.teams?.maximumTeams(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t)\n\t\t\t\t\t: ctx.context.orgOptions.teams?.maximumTeams;\n\n\t\t\tconst maxTeamsReached = maximum ? existingTeams.length >= maximum : false;\n\t\t\tif (maxTeamsReached) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { name, organizationId: _, ...additionalFields } = ctx.body;\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet teamData = {\n\t\t\t\tname,\n\t\t\t\torganizationId,\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t...additionalFields,\n\t\t\t};\n\n\t\t\t// Run beforeCreateTeam hook\n\t\t\tif (options?.organizationHooks?.beforeCreateTeam) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeCreateTeam({\n\t\t\t\t\tteam: {\n\t\t\t\t\t\tname,\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\t...additionalFields,\n\t\t\t\t\t},\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\tteamData = {\n\t\t\t\t\t\t...teamData,\n\t\t\t\t\t\t...response.data,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst createdTeam = await adapter.createTeam(teamData);\n\n\t\t\t// Run afterCreateTeam hook\n\t\t\tif (options?.organizationHooks?.afterCreateTeam) {\n\t\t\t\tawait options?.organizationHooks.afterCreateTeam({\n\t\t\t\t\tteam: createdTeam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(createdTeam);\n\t\t},\n\t);\n};\n\nconst removeTeamBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: `The team ID of the team to remove. Eg: \"team-id\"`,\n\t}),\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: `The organization ID which the team falls under. If not provided, it will default to the user's active organization. Eg: \"organization-id\"`,\n\t\t})\n\t\t.optional(),\n});\n\nexport const removeTeam = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/remove-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: removeTeamBodySchema,\n\t\t\tuse: [orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Remove a team from an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team removed successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmessage: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Confirmation message indicating successful removal\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"Team removed successfully.\"],\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\trequired: [\"message\"],\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\tasync (ctx) => {\n\t\t\tconst session = await getSessionFromCtx(ctx);\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!session && (ctx.request || ctx.headers)) {\n\t\t\t\tthrow APIError.fromStatus(\"UNAUTHORIZED\");\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tif (session) {\n\t\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\t\tuserId: session.user.id,\n\t\t\t\t\torganizationId,\n\t\t\t\t});\n\n\t\t\t\tif (!member || session.session?.activeTeamId === ctx.body.teamId) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst canRemove = await hasPermission(\n\t\t\t\t\t{\n\t\t\t\t\t\trole: member.role,\n\t\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\t\tpermissions: {\n\t\t\t\t\t\t\tteam: [\"delete\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tif (!canRemove) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId,\n\t\t\t});\n\t\t\tif (!team || team.organizationId !== organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!ctx.context.orgOptions.teams?.allowRemovingAllTeams) {\n\t\t\t\tconst teams = await adapter.listTeams(organizationId);\n\t\t\t\tif (teams.length <= 1) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.UNABLE_TO_REMOVE_LAST_TEAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Run beforeDeleteTeam hook\n\t\t\tif (options?.organizationHooks?.beforeDeleteTeam) {\n\t\t\t\tawait options?.organizationHooks.beforeDeleteTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait adapter.deleteTeam(team.id);\n\n\t\t\t// Run afterDeleteTeam hook\n\t\t\tif (options?.organizationHooks?.afterDeleteTeam) {\n\t\t\t\tawait options?.organizationHooks.afterDeleteTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json({ message: \"Team removed successfully.\" });\n\t\t},\n\t);\n\nexport const updateTeam = <O extends OrganizationOptions>(options: O) => {\n\tconst additionalFieldsSchema = toZodSchema({\n\t\tfields: options?.schema?.team?.additionalFields ?? {},\n\t\tisClientSide: true,\n\t});\n\n\ttype Body = {\n\t\tteamId: string;\n\t\tdata: Partial<\n\t\t\tPrettifyDeep<\n\t\t\t\tOmit<z.infer<typeof teamSchema>, \"id\" | \"createdAt\" | \"updatedAt\">\n\t\t\t> &\n\t\t\t\tInferAdditionalFieldsFromPluginOptions<\"team\", O>\n\t\t>;\n\t};\n\n\treturn createAuthEndpoint(\n\t\t\"/organization/update-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: z.object({\n\t\t\t\tteamId: z.string().meta({\n\t\t\t\t\tdescription: `The ID of the team to be updated. Eg: \"team-id\"`,\n\t\t\t\t}),\n\t\t\t\tdata: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\t...teamSchema.shape,\n\t\t\t\t\t\t...additionalFieldsSchema.shape,\n\t\t\t\t\t})\n\t\t\t\t\t.partial(),\n\t\t\t}),\n\t\t\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\t$Infer: { body: {} as Body },\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Update an existing team in an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team updated successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the updated team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tname: {\n\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\tdescription: \"Updated name of the team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tupdatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.data.organizationId || session.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canUpdate = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: member.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tteam: [\"update\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId,\n\t\t\t});\n\n\t\t\tif (!team || team.organizationId !== organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { name, organizationId: __, ...additionalFields } = ctx.body.data;\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst updates = {\n\t\t\t\tname,\n\t\t\t\t...additionalFields,\n\t\t\t};\n\n\t\t\t// Run beforeUpdateTeam hook\n\t\t\tif (options?.organizationHooks?.beforeUpdateTeam) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeUpdateTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tupdates,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\t// Allow the hook to modify the updates\n\t\t\t\t\tconst modifiedUpdates = response.data;\n\t\t\t\t\tconst updatedTeam = await adapter.updateTeam(\n\t\t\t\t\t\tteam.id,\n\t\t\t\t\t\tmodifiedUpdates,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Run afterUpdateTeam hook\n\t\t\t\t\tif (options?.organizationHooks?.afterUpdateTeam) {\n\t\t\t\t\t\tawait options?.organizationHooks.afterUpdateTeam({\n\t\t\t\t\t\t\tteam: updatedTeam,\n\t\t\t\t\t\t\tuser: session.user,\n\t\t\t\t\t\t\torganization,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn ctx.json(updatedTeam);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst updatedTeam = await adapter.updateTeam(team.id, updates);\n\n\t\t\t// Run afterUpdateTeam hook\n\t\t\tif (options?.organizationHooks?.afterUpdateTeam) {\n\t\t\t\tawait options?.organizationHooks.afterUpdateTeam({\n\t\t\t\t\tteam: updatedTeam,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(updatedTeam);\n\t\t},\n\t);\n};\n\nconst listOrganizationTeamsQuerySchema = z.optional(\n\tz.object({\n\t\torganizationId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: `The organization ID which the teams are under to list. Defaults to the users active organization. Eg: \"organization-id\"`,\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n\nexport const listOrganizationTeams = <O extends OrganizationOptions>(\n\toptions: O,\n) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-teams\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listOrganizationTeamsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List all teams in an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team\",\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\tname: {\n\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\tdescription: \"Name of the team\",\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\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\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\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\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\tupdatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\"Array of team objects within the organization\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst organizationId =\n\t\t\t\tctx.query?.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: organizationId || \"\",\n\t\t\t});\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst teams = await adapter.listTeams(organizationId);\n\t\t\treturn ctx.json(teams);\n\t\t},\n\t);\n\nconst setActiveTeamBodySchema = z.object({\n\tteamId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The team id to set as active. It can be null to unset the active team\",\n\t\t})\n\t\t.nullable()\n\t\t.optional(),\n});\n\nexport const setActiveTeam = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/set-active-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: setActiveTeamBodySchema,\n\t\t\trequireHeaders: true,\n\t\t\tuse: [orgSessionMiddleware, orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Set the active team\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"The team\",\n\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Team\",\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\tasync (ctx) => {\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst session = ctx.context.session;\n\n\t\t\tif (ctx.body.teamId === null) {\n\t\t\t\tconst sessionTeamId = session.session.activeTeamId;\n\t\t\t\tif (!sessionTeamId) {\n\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t}\n\n\t\t\t\tconst updatedSession = await adapter.setActiveTeam(\n\t\t\t\t\tsession.session.token,\n\t\t\t\t\tnull,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: updatedSession,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json(null);\n\t\t\t}\n\n\t\t\tlet teamId: string;\n\n\t\t\tif (!ctx.body.teamId) {\n\t\t\t\tconst sessionTeamId = session.session.activeTeamId;\n\t\t\t\tif (!sessionTeamId) {\n\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t} else {\n\t\t\t\t\tteamId = sessionTeamId;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tteamId = ctx.body.teamId;\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({ teamId });\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst member = await adapter.findTeamMember({\n\t\t\t\tteamId,\n\t\t\t\tuserId: session.user.id,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst updatedSession = await adapter.setActiveTeam(\n\t\t\t\tsession.session.token,\n\t\t\t\tteam.id,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\tsession: updatedSession,\n\t\t\t\tuser: session.user,\n\t\t\t});\n\n\t\t\treturn ctx.json(team);\n\t\t},\n\t);\n\nexport const listUserTeams = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-user-teams\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List all teams that the current user is a part of.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The team\",\n\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Team\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\"Array of team objects within the organization\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst teams = await adapter.listTeamsByUser({\n\t\t\t\tuserId: session.user.id,\n\t\t\t});\n\n\t\t\treturn ctx.json(teams);\n\t\t},\n\t);\n\nconst listTeamMembersQuerySchema = z.optional(\n\tz.object({\n\t\tteamId: z.string().optional().meta({\n\t\t\tdescription:\n\t\t\t\t\"The team whose members we should return. If this is not provided the members of the current active team get returned.\",\n\t\t}),\n\t}),\n);\n\nexport const listTeamMembers = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-team-members\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listTeamMembersQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List the members of the given team.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The team member\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team member\",\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\tuserId: {\n\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\tdescription: \"The user ID of the team member\",\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\tteamId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"The team ID of the team the team member is in\",\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\tcreatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team member was created\",\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\trequired: [\"id\", \"userId\", \"teamId\", \"createdAt\"],\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdescription: \"Array of team member objects within the team\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst teamId = ctx.query?.teamId || session?.session.activeTeamId;\n\t\t\tif (!teamId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst member = await adapter.findTeamMember({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\tteamId,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst members = await adapter.listTeamMembers({\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\treturn ctx.json(members);\n\t\t},\n\t);\n\nconst addTeamMemberBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: \"The team the user should be a member of.\",\n\t}),\n\n\tuserId: z.coerce.string().meta({\n\t\tdescription:\n\t\t\t\"The user Id which represents the user to be added as a member.\",\n\t}),\n});\n\nexport const addTeamMember = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/add-team-member\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: addTeamMemberBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"The newly created member\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team member created successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"The team member\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team member\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuserId: {\n\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\tdescription: \"The user ID of the team member\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tteamId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The team ID of the team the team member is in\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team member was created\",\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\trequired: [\"id\", \"userId\", \"teamId\", \"createdAt\"],\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\n\t\t\tif (!session.session.activeOrganizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst currentMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!currentMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canUpdateMember = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: currentMember.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tmember: [\"update\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canUpdateMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst toBeAddedMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!toBeAddedMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(\n\t\t\t\tsession.session.activeOrganizationId,\n\t\t\t);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst userBeingAdded = await ctx.context.internalAdapter.findUserById(\n\t\t\t\tctx.body.userId,\n\t\t\t);\n\t\t\tif (!userBeingAdded) {\n\t\t\t\tthrow APIError.fromStatus(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"User not found\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Run beforeAddTeamMember hook\n\t\t\tif (options?.organizationHooks?.beforeAddTeamMember) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeAddTeamMember({\n\t\t\t\t\tteamMember: {\n\t\t\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\t\t},\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingAdded,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\t// Allow the hook to modify the data\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst teamMember = await adapter.findOrCreateTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\t// Run afterAddTeamMember hook\n\t\t\tif (options?.organizationHooks?.afterAddTeamMember) {\n\t\t\t\tawait options?.organizationHooks.afterAddTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingAdded,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(teamMember);\n\t\t},\n\t);\n\nconst removeTeamMemberBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: \"The team the user should be removed from.\",\n\t}),\n\n\tuserId: z.coerce.string().meta({\n\t\tdescription: \"The user which should be removed from the team.\",\n\t}),\n});\n\nexport const removeTeamMember = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/remove-team-member\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: removeTeamMemberBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Remove a member from a team\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team member removed successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmessage: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Confirmation message indicating successful removal\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"Team member removed successfully.\"],\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\trequired: [\"message\"],\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\n\t\t\tif (!session.session.activeOrganizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst currentMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!currentMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canDeleteMember = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: currentMember.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tmember: [\"delete\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canDeleteMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst toBeAddedMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!toBeAddedMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId: session.session.activeOrganizationId,\n\t\t\t});\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(\n\t\t\t\tsession.session.activeOrganizationId,\n\t\t\t);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst userBeingRemoved = await ctx.context.internalAdapter.findUserById(\n\t\t\t\tctx.body.userId,\n\t\t\t);\n\t\t\tif (!userBeingRemoved) {\n\t\t\t\tthrow APIError.fromStatus(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"User not found\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst teamMember = await adapter.findTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\tif (!teamMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Run beforeRemoveTeamMember hook\n\t\t\tif (options?.organizationHooks?.beforeRemoveTeamMember) {\n\t\t\t\tawait options?.organizationHooks.beforeRemoveTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingRemoved,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait adapter.removeTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\t// Run afterRemoveTeamMember hook\n\t\t\tif (options?.organizationHooks?.afterRemoveTeamMember) {\n\t\t\t\tawait options?.organizationHooks.afterRemoveTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingRemoved,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json({ message: \"Team member removed successfully.\" });\n\t\t},\n\t);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"crud-team.mjs","names":[],"sources":["../../../../src/plugins/organization/routes/crud-team.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport * as z from \"zod\";\nimport { getSessionFromCtx } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport type { InferAdditionalFieldsFromPluginOptions } from \"../../../db\";\nimport { toZodSchema } from \"../../../db\";\nimport type { PrettifyDeep } from \"../../../types/helper\";\nimport { getOrgAdapter } from \"../adapter\";\nimport { orgMiddleware, orgSessionMiddleware } from \"../call\";\nimport { ORGANIZATION_ERROR_CODES } from \"../error-codes\";\nimport { hasPermission } from \"../has-permission\";\nimport { teamSchema } from \"../schema\";\nimport type { OrganizationOptions } from \"../types\";\n\nconst teamBaseSchema = z.object({\n\tname: z.string().meta({\n\t\tdescription: 'The name of the team. Eg: \"my-team\"',\n\t}),\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t'The organization ID which the team will be created in. Defaults to the active organization. Eg: \"organization-id\"',\n\t\t})\n\t\t.optional(),\n});\n\nexport const createTeam = <O extends OrganizationOptions>(options: O) => {\n\tconst additionalFieldsSchema = toZodSchema({\n\t\tfields: options?.schema?.team?.additionalFields ?? {},\n\t\tisClientSide: true,\n\t});\n\treturn createAuthEndpoint(\n\t\t\"/organization/create-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: z.object({\n\t\t\t\t...teamBaseSchema.shape,\n\t\t\t\t...additionalFieldsSchema.shape,\n\t\t\t}),\n\t\t\tuse: [orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\t$Infer: {\n\t\t\t\t\tbody: {} as z.infer<typeof teamBaseSchema> &\n\t\t\t\t\t\tInferAdditionalFieldsFromPluginOptions<\"team\", O>,\n\t\t\t\t},\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Create a new team within an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team created successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the created team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tname: {\n\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\tdescription: \"Name of the team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tupdatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tasync (ctx) => {\n\t\t\tconst session = await getSessionFromCtx(ctx);\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!session && (ctx.request || ctx.headers)) {\n\t\t\t\tthrow APIError.fromStatus(\"UNAUTHORIZED\");\n\t\t\t}\n\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options as O);\n\t\t\tif (session) {\n\t\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\t\tuserId: session.user.id,\n\t\t\t\t\torganizationId,\n\t\t\t\t});\n\t\t\t\tif (!member) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst canCreate = await hasPermission(\n\t\t\t\t\t{\n\t\t\t\t\t\trole: member.role,\n\t\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\t\tpermissions: {\n\t\t\t\t\t\t\tteam: [\"create\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tif (!canCreate) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst existingTeams = await adapter.listTeams(organizationId);\n\t\t\tconst maximum =\n\t\t\t\ttypeof ctx.context.orgOptions.teams?.maximumTeams === \"function\"\n\t\t\t\t\t? await ctx.context.orgOptions.teams?.maximumTeams(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tctx,\n\t\t\t\t\t\t)\n\t\t\t\t\t: ctx.context.orgOptions.teams?.maximumTeams;\n\n\t\t\tconst maxTeamsReached = maximum ? existingTeams.length >= maximum : false;\n\t\t\tif (maxTeamsReached) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst { name, organizationId: _, ...additionalFields } = ctx.body;\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet teamData = {\n\t\t\t\tname,\n\t\t\t\torganizationId,\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t...additionalFields,\n\t\t\t};\n\n\t\t\t// Run beforeCreateTeam hook\n\t\t\tif (options?.organizationHooks?.beforeCreateTeam) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeCreateTeam({\n\t\t\t\t\tteam: {\n\t\t\t\t\t\tname,\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\t...additionalFields,\n\t\t\t\t\t},\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\tteamData = {\n\t\t\t\t\t\t...teamData,\n\t\t\t\t\t\t...response.data,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst createdTeam = await adapter.createTeam(teamData);\n\n\t\t\t// Run afterCreateTeam hook\n\t\t\tif (options?.organizationHooks?.afterCreateTeam) {\n\t\t\t\tawait options?.organizationHooks.afterCreateTeam({\n\t\t\t\t\tteam: createdTeam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(createdTeam);\n\t\t},\n\t);\n};\n\nconst removeTeamBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: `The team ID of the team to remove. Eg: \"team-id\"`,\n\t}),\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: `The organization ID which the team falls under. If not provided, it will default to the user's active organization. Eg: \"organization-id\"`,\n\t\t})\n\t\t.optional(),\n});\n\nexport const removeTeam = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/remove-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: removeTeamBodySchema,\n\t\t\tuse: [orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Remove a team from an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team removed successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmessage: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Confirmation message indicating successful removal\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"Team removed successfully.\"],\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\trequired: [\"message\"],\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\tasync (ctx) => {\n\t\t\tconst session = await getSessionFromCtx(ctx);\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!session && (ctx.request || ctx.headers)) {\n\t\t\t\tthrow APIError.fromStatus(\"UNAUTHORIZED\");\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tif (session) {\n\t\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\t\tuserId: session.user.id,\n\t\t\t\t\torganizationId,\n\t\t\t\t});\n\n\t\t\t\tif (!member || session.session?.activeTeamId === ctx.body.teamId) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst canRemove = await hasPermission(\n\t\t\t\t\t{\n\t\t\t\t\t\trole: member.role,\n\t\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\t\tpermissions: {\n\t\t\t\t\t\t\tteam: [\"delete\"],\n\t\t\t\t\t\t},\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t},\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tif (!canRemove) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId,\n\t\t\t});\n\t\t\tif (!team || team.organizationId !== organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!ctx.context.orgOptions.teams?.allowRemovingAllTeams) {\n\t\t\t\tconst teams = await adapter.listTeams(organizationId);\n\t\t\t\tif (teams.length <= 1) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tORGANIZATION_ERROR_CODES.UNABLE_TO_REMOVE_LAST_TEAM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Run beforeDeleteTeam hook\n\t\t\tif (options?.organizationHooks?.beforeDeleteTeam) {\n\t\t\t\tawait options?.organizationHooks.beforeDeleteTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait adapter.deleteTeam(team.id);\n\n\t\t\t// Run afterDeleteTeam hook\n\t\t\tif (options?.organizationHooks?.afterDeleteTeam) {\n\t\t\t\tawait options?.organizationHooks.afterDeleteTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: session?.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json({ message: \"Team removed successfully.\" });\n\t\t},\n\t);\n\nexport const updateTeam = <O extends OrganizationOptions>(options: O) => {\n\tconst additionalFieldsSchema = toZodSchema({\n\t\tfields: options?.schema?.team?.additionalFields ?? {},\n\t\tisClientSide: true,\n\t});\n\n\ttype Body = {\n\t\tteamId: string;\n\t\tdata: Partial<\n\t\t\tPrettifyDeep<\n\t\t\t\tOmit<z.infer<typeof teamSchema>, \"id\" | \"createdAt\" | \"updatedAt\">\n\t\t\t> &\n\t\t\t\tInferAdditionalFieldsFromPluginOptions<\"team\", O>\n\t\t>;\n\t};\n\n\treturn createAuthEndpoint(\n\t\t\"/organization/update-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: z.object({\n\t\t\t\tteamId: z.string().meta({\n\t\t\t\t\tdescription: `The ID of the team to be updated. Eg: \"team-id\"`,\n\t\t\t\t}),\n\t\t\t\tdata: z\n\t\t\t\t\t.object({\n\t\t\t\t\t\t...teamSchema.shape,\n\t\t\t\t\t\t...additionalFieldsSchema.shape,\n\t\t\t\t\t})\n\t\t\t\t\t.partial(),\n\t\t\t}),\n\t\t\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t\tmetadata: {\n\t\t\t\t$Infer: { body: {} as Body },\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Update an existing team in an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team updated successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the updated team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tname: {\n\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\tdescription: \"Updated name of the team\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tupdatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.data.organizationId || session.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canUpdate = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: member.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tteam: [\"update\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canUpdate) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId,\n\t\t\t});\n\n\t\t\tif (!team || team.organizationId !== organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { name, organizationId: __, ...additionalFields } = ctx.body.data;\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst updates = {\n\t\t\t\tname,\n\t\t\t\t...additionalFields,\n\t\t\t};\n\n\t\t\t// Run beforeUpdateTeam hook\n\t\t\tif (options?.organizationHooks?.beforeUpdateTeam) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeUpdateTeam({\n\t\t\t\t\tteam,\n\t\t\t\t\tupdates,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\t// Allow the hook to modify the updates\n\t\t\t\t\tconst modifiedUpdates = response.data;\n\t\t\t\t\tconst updatedTeam = await adapter.updateTeam(\n\t\t\t\t\t\tteam.id,\n\t\t\t\t\t\tmodifiedUpdates,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Run afterUpdateTeam hook\n\t\t\t\t\tif (options?.organizationHooks?.afterUpdateTeam) {\n\t\t\t\t\t\tawait options?.organizationHooks.afterUpdateTeam({\n\t\t\t\t\t\t\tteam: updatedTeam,\n\t\t\t\t\t\t\tuser: session.user,\n\t\t\t\t\t\t\torganization,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn ctx.json(updatedTeam);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst updatedTeam = await adapter.updateTeam(team.id, updates);\n\n\t\t\t// Run afterUpdateTeam hook\n\t\t\tif (options?.organizationHooks?.afterUpdateTeam) {\n\t\t\t\tawait options?.organizationHooks.afterUpdateTeam({\n\t\t\t\t\tteam: updatedTeam,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(updatedTeam);\n\t\t},\n\t);\n};\n\nconst listOrganizationTeamsQuerySchema = z.optional(\n\tz.object({\n\t\torganizationId: z\n\t\t\t.string()\n\t\t\t.meta({\n\t\t\t\tdescription: `The organization ID which the teams are under to list. Defaults to the users active organization. Eg: \"organization-id\"`,\n\t\t\t})\n\t\t\t.optional(),\n\t}),\n);\n\nexport const listOrganizationTeams = <O extends OrganizationOptions>(\n\toptions: O,\n) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-teams\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listOrganizationTeamsQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List all teams in an organization\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team\",\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\tname: {\n\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\tdescription: \"Name of the team\",\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\torganizationId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ID of the organization the team belongs to\",\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\tcreatedAt: {\n\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the team was created\",\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\tupdatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team was last updated\",\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\trequired: [\n\t\t\t\t\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"organizationId\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"createdAt\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"updatedAt\",\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\"Array of team objects within the organization\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst organizationId =\n\t\t\t\tctx.query?.organizationId || session?.session.activeOrganizationId;\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst adapter = getOrgAdapter<O>(ctx.context, options);\n\t\t\tconst member = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: organizationId || \"\",\n\t\t\t});\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst teams = await adapter.listTeams(organizationId);\n\t\t\treturn ctx.json(teams);\n\t\t},\n\t);\n\nconst setActiveTeamBodySchema = z.object({\n\tteamId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The team id to set as active. It can be null to unset the active team\",\n\t\t})\n\t\t.nullable()\n\t\t.optional(),\n});\n\nexport const setActiveTeam = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/set-active-team\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: setActiveTeamBodySchema,\n\t\t\trequireHeaders: true,\n\t\t\tuse: [orgSessionMiddleware, orgMiddleware],\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Set the active team\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"The team\",\n\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Team\",\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\tasync (ctx) => {\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst session = ctx.context.session;\n\n\t\t\tif (ctx.body.teamId === null) {\n\t\t\t\tconst sessionTeamId = session.session.activeTeamId;\n\t\t\t\tif (!sessionTeamId) {\n\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t}\n\n\t\t\t\tconst updatedSession = await adapter.setActiveTeam(\n\t\t\t\t\tsession.session.token,\n\t\t\t\t\tnull,\n\t\t\t\t\tctx,\n\t\t\t\t);\n\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: updatedSession,\n\t\t\t\t\tuser: session.user,\n\t\t\t\t});\n\n\t\t\t\treturn ctx.json(null);\n\t\t\t}\n\n\t\t\tlet teamId: string;\n\n\t\t\tif (!ctx.body.teamId) {\n\t\t\t\tconst sessionTeamId = session.session.activeTeamId;\n\t\t\t\tif (!sessionTeamId) {\n\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t} else {\n\t\t\t\t\tteamId = sessionTeamId;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tteamId = ctx.body.teamId;\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({ teamId });\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst member = await adapter.findTeamMember({\n\t\t\t\tteamId,\n\t\t\t\tuserId: session.user.id,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst updatedSession = await adapter.setActiveTeam(\n\t\t\t\tsession.session.token,\n\t\t\t\tteam.id,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\tsession: updatedSession,\n\t\t\t\tuser: session.user,\n\t\t\t});\n\n\t\t\treturn ctx.json(team);\n\t\t},\n\t);\n\nexport const listUserTeams = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-user-teams\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List all teams that the current user is a part of.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The team\",\n\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Team\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\"Array of team objects within the organization\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst teams = await adapter.listTeamsByUser({\n\t\t\t\tuserId: session.user.id,\n\t\t\t});\n\n\t\t\treturn ctx.json(teams);\n\t\t},\n\t);\n\nconst listTeamMembersQuerySchema = z.optional(\n\tz.object({\n\t\tteamId: z.string().optional().meta({\n\t\t\tdescription:\n\t\t\t\t\"The team whose members we should return. If this is not provided the members of the current active team get returned.\",\n\t\t}),\n\t}),\n);\n\nexport const listTeamMembers = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/list-team-members\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\tquery: listTeamMembersQuerySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"List the members of the given team.\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Teams retrieved successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tdescription: \"The team member\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team member\",\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\tuserId: {\n\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\tdescription: \"The user ID of the team member\",\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\tteamId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"The team ID of the team the team member is in\",\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\tcreatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team member was created\",\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\trequired: [\"id\", \"userId\", \"teamId\", \"createdAt\"],\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tdescription: \"Array of team member objects within the team\",\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\t\t\tconst teamId = ctx.query?.teamId || session?.session.activeTeamId;\n\t\t\tif (!teamId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst member = await adapter.findTeamMember({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\tteamId,\n\t\t\t});\n\n\t\t\tif (!member) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst members = await adapter.listTeamMembers({\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\treturn ctx.json(members);\n\t\t},\n\t);\n\nconst addTeamMemberBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: \"The team the user should be a member of.\",\n\t}),\n\n\tuserId: z.coerce.string().meta({\n\t\tdescription:\n\t\t\t\"The user Id which represents the user to be added as a member.\",\n\t}),\n\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The organization ID which the team falls under. If not provided, it will default to the user's active organization.\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const addTeamMember = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/add-team-member\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: addTeamMemberBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"The newly created member\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team member created successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"The team member\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tid: {\n\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\tdescription: \"Unique identifier of the team member\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuserId: {\n\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\tdescription: \"The user ID of the team member\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tteamId: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"The team ID of the team the team member is in\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tcreatedAt: {\n\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\tformat: \"date-time\",\n\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\"Timestamp when the team member was created\",\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\trequired: [\"id\", \"userId\", \"teamId\", \"createdAt\"],\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session.session.activeOrganizationId;\n\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst currentMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!currentMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canUpdateMember = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: currentMember.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tmember: [\"update\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: organizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canUpdateMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst toBeAddedMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!toBeAddedMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst userBeingAdded = await ctx.context.internalAdapter.findUserById(\n\t\t\t\tctx.body.userId,\n\t\t\t);\n\t\t\tif (!userBeingAdded) {\n\t\t\t\tthrow APIError.fromStatus(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"User not found\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Run beforeAddTeamMember hook\n\t\t\tif (options?.organizationHooks?.beforeAddTeamMember) {\n\t\t\t\tconst response = await options?.organizationHooks.beforeAddTeamMember({\n\t\t\t\t\tteamMember: {\n\t\t\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\t\t},\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingAdded,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t\tif (response && typeof response === \"object\" && \"data\" in response) {\n\t\t\t\t\t// Allow the hook to modify the data\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst teamMember = await adapter.findOrCreateTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\t// Run afterAddTeamMember hook\n\t\t\tif (options?.organizationHooks?.afterAddTeamMember) {\n\t\t\t\tawait options?.organizationHooks.afterAddTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingAdded,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json(teamMember);\n\t\t},\n\t);\n\nconst removeTeamMemberBodySchema = z.object({\n\tteamId: z.string().meta({\n\t\tdescription: \"The team the user should be removed from.\",\n\t}),\n\n\tuserId: z.coerce.string().meta({\n\t\tdescription: \"The user which should be removed from the team.\",\n\t}),\n\n\torganizationId: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"The organization ID which the team falls under. If not provided, it will default to the user's active organization.\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const removeTeamMember = <O extends OrganizationOptions>(options: O) =>\n\tcreateAuthEndpoint(\n\t\t\"/organization/remove-team-member\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: removeTeamMemberBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tdescription: \"Remove a member from a team\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Team member removed successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tmessage: {\n\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\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Confirmation message indicating successful removal\",\n\t\t\t\t\t\t\t\t\t\t\t\tenum: [\"Team member removed successfully.\"],\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\trequired: [\"message\"],\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\trequireHeaders: true,\n\t\t\tuse: [orgMiddleware, orgSessionMiddleware],\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tconst session = ctx.context.session;\n\t\t\tconst adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);\n\n\t\t\tconst organizationId =\n\t\t\t\tctx.body.organizationId || session.session.activeOrganizationId;\n\n\t\t\tif (!organizationId) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst currentMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: session.user.id,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!currentMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst canDeleteMember = await hasPermission(\n\t\t\t\t{\n\t\t\t\t\trole: currentMember.role,\n\t\t\t\t\toptions: ctx.context.orgOptions,\n\t\t\t\t\tpermissions: {\n\t\t\t\t\t\tmember: [\"delete\"],\n\t\t\t\t\t},\n\t\t\t\t\torganizationId: organizationId,\n\t\t\t\t},\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (!canDeleteMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"FORBIDDEN\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst toBeAddedMember = await adapter.findMemberByOrgId({\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!toBeAddedMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst team = await adapter.findTeamById({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\torganizationId: organizationId,\n\t\t\t});\n\n\t\t\tif (!team) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.TEAM_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst organization = await adapter.findOrganizationById(organizationId);\n\t\t\tif (!organization) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.ORGANIZATION_NOT_FOUND,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst userBeingRemoved = await ctx.context.internalAdapter.findUserById(\n\t\t\t\tctx.body.userId,\n\t\t\t);\n\t\t\tif (!userBeingRemoved) {\n\t\t\t\tthrow APIError.fromStatus(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"User not found\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst teamMember = await adapter.findTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\tif (!teamMember) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_TEAM,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Run beforeRemoveTeamMember hook\n\t\t\tif (options?.organizationHooks?.beforeRemoveTeamMember) {\n\t\t\t\tawait options?.organizationHooks.beforeRemoveTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingRemoved,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait adapter.removeTeamMember({\n\t\t\t\tteamId: ctx.body.teamId,\n\t\t\t\tuserId: ctx.body.userId,\n\t\t\t});\n\n\t\t\t// Run afterRemoveTeamMember hook\n\t\t\tif (options?.organizationHooks?.afterRemoveTeamMember) {\n\t\t\t\tawait options?.organizationHooks.afterRemoveTeamMember({\n\t\t\t\t\tteamMember,\n\t\t\t\t\tteam,\n\t\t\t\t\tuser: userBeingRemoved,\n\t\t\t\t\torganization,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn ctx.json({ message: \"Team member removed successfully.\" });\n\t\t},\n\t);\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FieldAttributeToObject, RemoveFieldsWithReturnedFalse } from "../../db/field.mjs";
|
|
2
|
-
import "../../db/index.mjs";
|
|
3
2
|
import { OrganizationOptions } from "./types.mjs";
|
|
4
3
|
import { BetterAuthPluginDBSchema, DBFieldAttribute } from "@better-auth/core/db";
|
|
5
4
|
import { Prettify } from "better-call";
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Session as Session$1, User as User$1 } from "../../types/models.mjs";
|
|
2
|
-
import "../../types/index.mjs";
|
|
3
2
|
import { AccessControl, Role } from "../access/types.mjs";
|
|
4
|
-
import "../access/index.mjs";
|
|
5
3
|
import { Invitation, Member, Organization, OrganizationRole, Team, TeamMember } from "./schema.mjs";
|
|
6
4
|
import { AuthContext, Awaitable, GenericEndpointContext } from "@better-auth/core";
|
|
7
5
|
import { DBFieldAttribute } from "@better-auth/core/db";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { InferOptionSchema } from "../../types/plugins.mjs";
|
|
2
|
-
import "../../types/index.mjs";
|
|
3
2
|
import { WalletAddressSchema, schema } from "./schema.mjs";
|
|
4
3
|
import { ENSLookupArgs, ENSLookupResult, SIWEVerifyMessageArgs } from "./types.mjs";
|
|
5
4
|
import * as better_call0 from "better-call";
|
|
@@ -9,6 +9,13 @@ import * as _better_fetch_fetch0 from "@better-fetch/fetch";
|
|
|
9
9
|
|
|
10
10
|
//#region src/plugins/two-factor/client.d.ts
|
|
11
11
|
declare const twoFactorClient: (options?: {
|
|
12
|
+
/**
|
|
13
|
+
* the page to redirect if a user needs to verify
|
|
14
|
+
* their two factor
|
|
15
|
+
*
|
|
16
|
+
* @warning This causes a full page reload when used.
|
|
17
|
+
*/
|
|
18
|
+
twoFactorPage?: string;
|
|
12
19
|
/**
|
|
13
20
|
* a redirect function to call if a user needs to verify
|
|
14
21
|
* their two factor
|
|
@@ -24,7 +24,11 @@ const twoFactorClient = (options) => {
|
|
|
24
24
|
name: "two-factor",
|
|
25
25
|
hooks: { async onSuccess(context) {
|
|
26
26
|
if (context.data?.twoFactorRedirect) {
|
|
27
|
-
if (options?.onTwoFactorRedirect)
|
|
27
|
+
if (options?.onTwoFactorRedirect) {
|
|
28
|
+
await options.onTwoFactorRedirect();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (options?.twoFactorPage && typeof window !== "undefined") window.location.href = options.twoFactorPage;
|
|
28
32
|
}
|
|
29
33
|
} }
|
|
30
34
|
}],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/two-factor/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { twoFactor as twoFa } from \".\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\n\nexport * from \"./error-code\";\n\nexport const twoFactorClient = (\n\toptions?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * a redirect function to call if a user needs to verify\n\t\t\t\t * their two factor\n\t\t\t\t */\n\t\t\t\tonTwoFactorRedirect?: () => void | Promise<void>;\n\t\t }\n\t\t| undefined,\n) => {\n\treturn {\n\t\tid: \"two-factor\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof twoFa>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path.startsWith(\"/two-factor/\"),\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t\tpathMethods: {\n\t\t\t\"/two-factor/disable\": \"POST\",\n\t\t\t\"/two-factor/enable\": \"POST\",\n\t\t\t\"/two-factor/send-otp\": \"POST\",\n\t\t\t\"/two-factor/generate-backup-codes\": \"POST\",\n\t\t\t\"/two-factor/get-totp-uri\": \"POST\",\n\t\t\t\"/two-factor/verify-totp\": \"POST\",\n\t\t\t\"/two-factor/verify-otp\": \"POST\",\n\t\t\t\"/two-factor/verify-backup-code\": \"POST\",\n\t\t},\n\t\tfetchPlugins: [\n\t\t\t{\n\t\t\t\tid: \"two-factor\",\n\t\t\t\tname: \"two-factor\",\n\t\t\t\thooks: {\n\t\t\t\t\tasync onSuccess(context) {\n\t\t\t\t\t\tif (context.data?.twoFactorRedirect) {\n\t\t\t\t\t\t\tif (options?.onTwoFactorRedirect) {\n\t\t\t\t\t\t\t\tawait options.onTwoFactorRedirect();\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\t$ERROR_CODES: TWO_FACTOR_ERROR_CODES,\n\t} satisfies BetterAuthClientPlugin;\n};\n\nexport type * from \"./backup-codes\";\nexport type * from \"./otp\";\nexport type * from \"./totp\";\nexport type * from \"./types\";\n"],"mappings":";;;AAMA,MAAa,mBACZ,
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/two-factor/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { twoFactor as twoFa } from \".\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\n\nexport * from \"./error-code\";\n\nexport const twoFactorClient = (\n\toptions?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * the page to redirect if a user needs to verify\n\t\t\t\t * their two factor\n\t\t\t\t *\n\t\t\t\t * @warning This causes a full page reload when used.\n\t\t\t\t */\n\t\t\t\ttwoFactorPage?: string;\n\t\t\t\t/**\n\t\t\t\t * a redirect function to call if a user needs to verify\n\t\t\t\t * their two factor\n\t\t\t\t */\n\t\t\t\tonTwoFactorRedirect?: () => void | Promise<void>;\n\t\t }\n\t\t| undefined,\n) => {\n\treturn {\n\t\tid: \"two-factor\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof twoFa>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path.startsWith(\"/two-factor/\"),\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t\tpathMethods: {\n\t\t\t\"/two-factor/disable\": \"POST\",\n\t\t\t\"/two-factor/enable\": \"POST\",\n\t\t\t\"/two-factor/send-otp\": \"POST\",\n\t\t\t\"/two-factor/generate-backup-codes\": \"POST\",\n\t\t\t\"/two-factor/get-totp-uri\": \"POST\",\n\t\t\t\"/two-factor/verify-totp\": \"POST\",\n\t\t\t\"/two-factor/verify-otp\": \"POST\",\n\t\t\t\"/two-factor/verify-backup-code\": \"POST\",\n\t\t},\n\t\tfetchPlugins: [\n\t\t\t{\n\t\t\t\tid: \"two-factor\",\n\t\t\t\tname: \"two-factor\",\n\t\t\t\thooks: {\n\t\t\t\t\tasync onSuccess(context) {\n\t\t\t\t\t\tif (context.data?.twoFactorRedirect) {\n\t\t\t\t\t\t\tif (options?.onTwoFactorRedirect) {\n\t\t\t\t\t\t\t\tawait options.onTwoFactorRedirect();\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// fallback for when `onTwoFactorRedirect` is not used and only `twoFactorPage` is provided\n\t\t\t\t\t\t\tif (options?.twoFactorPage && typeof window !== \"undefined\") {\n\t\t\t\t\t\t\t\twindow.location.href = options.twoFactorPage;\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\t$ERROR_CODES: TWO_FACTOR_ERROR_CODES,\n\t} satisfies BetterAuthClientPlugin;\n};\n\nexport type * from \"./backup-codes\";\nexport type * from \"./otp\";\nexport type * from \"./totp\";\nexport type * from \"./types\";\n"],"mappings":";;;AAMA,MAAa,mBACZ,YAgBI;AACJ,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EACtB,eAAe,CACd;GACC,UAAU,SAAS,KAAK,WAAW,eAAe;GAClD,QAAQ;GACR,CACD;EACD,aAAa;GACZ,uBAAuB;GACvB,sBAAsB;GACtB,wBAAwB;GACxB,qCAAqC;GACrC,4BAA4B;GAC5B,2BAA2B;GAC3B,0BAA0B;GAC1B,kCAAkC;GAClC;EACD,cAAc,CACb;GACC,IAAI;GACJ,MAAM;GACN,OAAO,EACN,MAAM,UAAU,SAAS;AACxB,QAAI,QAAQ,MAAM,mBAAmB;AACpC,SAAI,SAAS,qBAAqB;AACjC,YAAM,QAAQ,qBAAqB;AACnC;;AAID,SAAI,SAAS,iBAAiB,OAAO,WAAW,YAC/C,QAAO,SAAS,OAAO,QAAQ;;MAIlC;GACD,CACD;EACD,cAAc;EACd"}
|
|
@@ -211,7 +211,13 @@ const twoFactor = (options) => {
|
|
|
211
211
|
return ctx.json({ twoFactorRedirect: true });
|
|
212
212
|
})
|
|
213
213
|
}] },
|
|
214
|
-
schema: mergeSchema(schema,
|
|
214
|
+
schema: mergeSchema(schema, {
|
|
215
|
+
...options?.schema,
|
|
216
|
+
twoFactor: {
|
|
217
|
+
...options?.schema?.twoFactor,
|
|
218
|
+
...options?.twoFactorTable ? { modelName: options.twoFactorTable } : {}
|
|
219
|
+
}
|
|
220
|
+
}),
|
|
215
221
|
rateLimit: [{
|
|
216
222
|
pathMatcher(path) {
|
|
217
223
|
return path.startsWith("/two-factor/");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/two-factor/index.ts"],"sourcesContent":["import type { BetterAuthPlugin } from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../api\";\nimport {\n\tdeleteSessionCookie,\n\texpireCookie,\n\tsetSessionCookie,\n} from \"../../cookies\";\nimport { symmetricEncrypt } from \"../../crypto\";\nimport { generateRandomString } from \"../../crypto/random\";\nimport { mergeSchema } from \"../../db/schema\";\nimport { validatePassword } from \"../../utils/password\";\nimport type { BackupCodeOptions } from \"./backup-codes\";\nimport { backupCode2fa, generateBackupCodes } from \"./backup-codes\";\nimport {\n\tTRUST_DEVICE_COOKIE_MAX_AGE,\n\tTRUST_DEVICE_COOKIE_NAME,\n\tTWO_FACTOR_COOKIE_NAME,\n} from \"./constant\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\nimport { otp2fa } from \"./otp\";\nimport { schema } from \"./schema\";\nimport { totp2fa } from \"./totp\";\nimport type { TwoFactorOptions, UserWithTwoFactor } from \"./types\";\n\nexport * from \"./error-code\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\t\"two-factor\": {\n\t\t\tcreator: typeof twoFactor;\n\t\t};\n\t}\n}\n\nconst enableTwoFactorBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n\tissuer: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Custom issuer for the TOTP URI\",\n\t\t})\n\t\t.optional(),\n});\n\nconst disableTwoFactorBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nexport const twoFactor = <O extends TwoFactorOptions>(options?: O) => {\n\tconst opts = {\n\t\ttwoFactorTable: \"twoFactor\",\n\t};\n\tconst trustDeviceMaxAge =\n\t\toptions?.trustDeviceMaxAge ?? TRUST_DEVICE_COOKIE_MAX_AGE;\n\tconst backupCodeOptions = {\n\t\tstoreBackupCodes: \"encrypted\",\n\t\t...options?.backupCodeOptions,\n\t} satisfies BackupCodeOptions;\n\tconst totp = totp2fa(options?.totpOptions);\n\tconst backupCode = backupCode2fa(backupCodeOptions);\n\tconst otp = otp2fa(options?.otpOptions);\n\n\treturn {\n\t\tid: \"two-factor\",\n\t\tendpoints: {\n\t\t\t...totp.endpoints,\n\t\t\t...otp.endpoints,\n\t\t\t...backupCode.endpoints,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/enable`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.enableTwoFactor`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.enable`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-enable)\n\t\t\t */\n\t\t\tenableTwoFactor: createAuthEndpoint(\n\t\t\t\t\"/two-factor/enable\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: enableTwoFactorBodySchema,\n\t\t\t\t\tuse: [sessionMiddleware],\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tsummary: \"Enable two factor authentication\",\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Use this endpoint to enable two factor authentication. This will generate a TOTP URI and backup codes. Once the user verifies the TOTP URI, the two factor authentication will be enabled.\",\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: \"Successful response\",\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\ttotpURI: {\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: \"TOTP URI\",\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\tbackupCodes: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\titems: {\n\t\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\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Backup codes\",\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\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\t\t\tconst { password, issuer } = ctx.body;\n\t\t\t\t\tconst isPasswordValid = await validatePassword(ctx, {\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t});\n\t\t\t\t\tif (!isPasswordValid) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_PASSWORD,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst secret = generateRandomString(32);\n\t\t\t\t\tconst encryptedSecret = await symmetricEncrypt({\n\t\t\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\t\t\tdata: secret,\n\t\t\t\t\t});\n\t\t\t\t\tconst backupCodes = await generateBackupCodes(\n\t\t\t\t\t\tctx.context.secretConfig,\n\t\t\t\t\t\tbackupCodeOptions,\n\t\t\t\t\t);\n\t\t\t\t\tif (options?.skipVerificationOnEnable) {\n\t\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\t\tupdatedUser.id,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\tctx.context.session.session,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Update the session cookie with the new user data\n\t\t\t\t\t\t */\n\t\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t//remove current session\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\t\tctx.context.session.session.token,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t//delete existing two factor\n\t\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\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],\n\t\t\t\t\t});\n\n\t\t\t\t\tawait ctx.context.adapter.create({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tsecret: encryptedSecret,\n\t\t\t\t\t\t\tbackupCodes: backupCodes.encryptedBackupCodes,\n\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\t\t\tdigits: options?.totpOptions?.digits || 6,\n\t\t\t\t\t\tperiod: options?.totpOptions?.period,\n\t\t\t\t\t}).url(issuer || options?.issuer || ctx.context.appName, user.email);\n\t\t\t\t\treturn ctx.json({ totpURI, backupCodes: backupCodes.backupCodes });\n\t\t\t\t},\n\t\t\t),\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/disable`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.disableTwoFactor`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.disable`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-disable)\n\t\t\t */\n\t\t\tdisableTwoFactor: createAuthEndpoint(\n\t\t\t\t\"/two-factor/disable\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: disableTwoFactorBodySchema,\n\t\t\t\t\tuse: [sessionMiddleware],\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tsummary: \"Disable two factor authentication\",\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Use this endpoint to disable two factor authentication.\",\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: \"Successful response\",\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\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\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\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\t\t\tconst { password } = ctx.body;\n\t\t\t\t\tconst isPasswordValid = await validatePassword(ctx, {\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t});\n\t\t\t\t\tif (!isPasswordValid) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_PASSWORD,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttwoFactorEnabled: false,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\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: updatedUser.id,\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\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\tupdatedUser.id,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tctx.context.session.session,\n\t\t\t\t\t);\n\t\t\t\t\t/**\n\t\t\t\t\t * Update the session cookie with the new user data\n\t\t\t\t\t */\n\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t});\n\t\t\t\t\t//remove current session\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\tctx.context.session.session.token,\n\t\t\t\t\t);\n\t\t\t\t\tconst disableTrustCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst disableTrustValue = await ctx.getSignedCookie(\n\t\t\t\t\t\tdisableTrustCookie.name,\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t);\n\t\t\t\t\tif (disableTrustValue) {\n\t\t\t\t\t\tconst [, trustId] = disableTrustValue.split(\"!\");\n\t\t\t\t\t\tif (trustId) {\n\t\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t\ttrustId,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\texpireCookie(ctx, disableTrustCookie);\n\t\t\t\t\t}\n\t\t\t\t\treturn ctx.json({ status: true });\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\toptions: options as NoInfer<O>,\n\t\thooks: {\n\t\t\tafter: [\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-in/email\" ||\n\t\t\t\t\t\t\tcontext.path === \"/sign-in/username\" ||\n\t\t\t\t\t\t\tcontext.path === \"/sign-in/phone-number\"\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 data = ctx.context.newSession;\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!data?.user.twoFactorEnabled) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst trustDeviceCookieAttrs = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Check for trust device cookie\n\t\t\t\t\t\tconst trustDeviceCookie = await ctx.getSignedCookie(\n\t\t\t\t\t\t\ttrustDeviceCookieAttrs.name,\n\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (trustDeviceCookie) {\n\t\t\t\t\t\t\tconst [token, trustIdentifier] = trustDeviceCookie.split(\"!\");\n\t\t\t\t\t\t\tif (token && trustIdentifier) {\n\t\t\t\t\t\t\t\tconst expectedToken = await createHMAC(\n\t\t\t\t\t\t\t\t\t\"SHA-256\",\n\t\t\t\t\t\t\t\t\t\"base64urlnopad\",\n\t\t\t\t\t\t\t\t).sign(\n\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t`${data.user.id}!${trustIdentifier}`,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tif (token === expectedToken) {\n\t\t\t\t\t\t\t\t\t// HMAC is valid; verify the server-side record\n\t\t\t\t\t\t\t\t\tconst verificationRecord =\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t\t\t\t\t\ttrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tverificationRecord &&\n\t\t\t\t\t\t\t\t\t\tverificationRecord.value === data.user.id &&\n\t\t\t\t\t\t\t\t\t\tverificationRecord.expiresAt > new Date()\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t\t\t\t\ttrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tconst newTrustIdentifier = `trust-device-${generateRandomString(32)}`;\n\t\t\t\t\t\t\t\t\t\tconst newToken = await createHMAC(\n\t\t\t\t\t\t\t\t\t\t\t\"SHA-256\",\n\t\t\t\t\t\t\t\t\t\t\t\"base64urlnopad\",\n\t\t\t\t\t\t\t\t\t\t).sign(\n\t\t\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t\t\t`${data.user.id}!${newTrustIdentifier}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\t\t\t\t\t\tvalue: data.user.id,\n\t\t\t\t\t\t\t\t\t\t\tidentifier: newTrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t\texpiresAt: new Date(\n\t\t\t\t\t\t\t\t\t\t\t\tDate.now() + trustDeviceMaxAge * 1000,\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\tconst newTrustDeviceCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\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\tawait ctx.setSignedCookie(\n\t\t\t\t\t\t\t\t\t\t\tnewTrustDeviceCookie.name,\n\t\t\t\t\t\t\t\t\t\t\t`${newToken}!${newTrustIdentifier}`,\n\t\t\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t\t\ttrustDeviceCookieAttrs.attributes,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\treturn;\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\texpireCookie(ctx, trustDeviceCookieAttrs);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * remove the session cookie. It's set by the sign in credential\n\t\t\t\t\t\t */\n\t\t\t\t\t\tdeleteSessionCookie(ctx, true);\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(data.session.token);\n\t\t\t\t\t\tconst maxAge = options?.twoFactorCookieMaxAge ?? 10 * 60; // 10 minutes\n\t\t\t\t\t\tconst twoFactorCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\tTWO_FACTOR_COOKIE_NAME,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmaxAge,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst identifier = `2fa-${generateRandomString(20)}`;\n\t\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\t\tvalue: data.user.id,\n\t\t\t\t\t\t\tidentifier,\n\t\t\t\t\t\t\texpiresAt: new Date(Date.now() + maxAge * 1000),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait ctx.setSignedCookie(\n\t\t\t\t\t\t\ttwoFactorCookie.name,\n\t\t\t\t\t\t\tidentifier,\n\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\ttwoFactorCookie.attributes,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\ttwoFactorRedirect: true,\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\tschema: mergeSchema(schema, options?.schema),\n\t\trateLimit: [\n\t\t\t{\n\t\t\t\tpathMatcher(path) {\n\t\t\t\t\treturn path.startsWith(\"/two-factor/\");\n\t\t\t\t},\n\t\t\t\twindow: 10,\n\t\t\t\tmax: 3,\n\t\t\t},\n\t\t],\n\t\t$ERROR_CODES: TWO_FACTOR_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport * from \"./client\";\nexport * from \"./types\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/two-factor/index.ts"],"sourcesContent":["import type { BetterAuthPlugin } from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../api\";\nimport {\n\tdeleteSessionCookie,\n\texpireCookie,\n\tsetSessionCookie,\n} from \"../../cookies\";\nimport { symmetricEncrypt } from \"../../crypto\";\nimport { generateRandomString } from \"../../crypto/random\";\nimport { mergeSchema } from \"../../db/schema\";\nimport { validatePassword } from \"../../utils/password\";\nimport type { BackupCodeOptions } from \"./backup-codes\";\nimport { backupCode2fa, generateBackupCodes } from \"./backup-codes\";\nimport {\n\tTRUST_DEVICE_COOKIE_MAX_AGE,\n\tTRUST_DEVICE_COOKIE_NAME,\n\tTWO_FACTOR_COOKIE_NAME,\n} from \"./constant\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\nimport { otp2fa } from \"./otp\";\nimport { schema } from \"./schema\";\nimport { totp2fa } from \"./totp\";\nimport type { TwoFactorOptions, UserWithTwoFactor } from \"./types\";\n\nexport * from \"./error-code\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\t\"two-factor\": {\n\t\t\tcreator: typeof twoFactor;\n\t\t};\n\t}\n}\n\nconst enableTwoFactorBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n\tissuer: z\n\t\t.string()\n\t\t.meta({\n\t\t\tdescription: \"Custom issuer for the TOTP URI\",\n\t\t})\n\t\t.optional(),\n});\n\nconst disableTwoFactorBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nexport const twoFactor = <O extends TwoFactorOptions>(options?: O) => {\n\tconst opts = {\n\t\ttwoFactorTable: \"twoFactor\",\n\t};\n\tconst trustDeviceMaxAge =\n\t\toptions?.trustDeviceMaxAge ?? TRUST_DEVICE_COOKIE_MAX_AGE;\n\tconst backupCodeOptions = {\n\t\tstoreBackupCodes: \"encrypted\",\n\t\t...options?.backupCodeOptions,\n\t} satisfies BackupCodeOptions;\n\tconst totp = totp2fa(options?.totpOptions);\n\tconst backupCode = backupCode2fa(backupCodeOptions);\n\tconst otp = otp2fa(options?.otpOptions);\n\n\treturn {\n\t\tid: \"two-factor\",\n\t\tendpoints: {\n\t\t\t...totp.endpoints,\n\t\t\t...otp.endpoints,\n\t\t\t...backupCode.endpoints,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/enable`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.enableTwoFactor`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.enable`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-enable)\n\t\t\t */\n\t\t\tenableTwoFactor: createAuthEndpoint(\n\t\t\t\t\"/two-factor/enable\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: enableTwoFactorBodySchema,\n\t\t\t\t\tuse: [sessionMiddleware],\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tsummary: \"Enable two factor authentication\",\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Use this endpoint to enable two factor authentication. This will generate a TOTP URI and backup codes. Once the user verifies the TOTP URI, the two factor authentication will be enabled.\",\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: \"Successful response\",\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\ttotpURI: {\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: \"TOTP URI\",\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\tbackupCodes: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\titems: {\n\t\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\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Backup codes\",\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\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\t\t\tconst { password, issuer } = ctx.body;\n\t\t\t\t\tconst isPasswordValid = await validatePassword(ctx, {\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t});\n\t\t\t\t\tif (!isPasswordValid) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_PASSWORD,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst secret = generateRandomString(32);\n\t\t\t\t\tconst encryptedSecret = await symmetricEncrypt({\n\t\t\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\t\t\tdata: secret,\n\t\t\t\t\t});\n\t\t\t\t\tconst backupCodes = await generateBackupCodes(\n\t\t\t\t\t\tctx.context.secretConfig,\n\t\t\t\t\t\tbackupCodeOptions,\n\t\t\t\t\t);\n\t\t\t\t\tif (options?.skipVerificationOnEnable) {\n\t\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\t\tupdatedUser.id,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\tctx.context.session.session,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Update the session cookie with the new user data\n\t\t\t\t\t\t */\n\t\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t//remove current session\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\t\tctx.context.session.session.token,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t//delete existing two factor\n\t\t\t\t\tawait ctx.context.adapter.deleteMany({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\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],\n\t\t\t\t\t});\n\n\t\t\t\t\tawait ctx.context.adapter.create({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tsecret: encryptedSecret,\n\t\t\t\t\t\t\tbackupCodes: backupCodes.encryptedBackupCodes,\n\t\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\t\t\tdigits: options?.totpOptions?.digits || 6,\n\t\t\t\t\t\tperiod: options?.totpOptions?.period,\n\t\t\t\t\t}).url(issuer || options?.issuer || ctx.context.appName, user.email);\n\t\t\t\t\treturn ctx.json({ totpURI, backupCodes: backupCodes.backupCodes });\n\t\t\t\t},\n\t\t\t),\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/disable`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.disableTwoFactor`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.disable`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-disable)\n\t\t\t */\n\t\t\tdisableTwoFactor: createAuthEndpoint(\n\t\t\t\t\"/two-factor/disable\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: disableTwoFactorBodySchema,\n\t\t\t\t\tuse: [sessionMiddleware],\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\tsummary: \"Disable two factor authentication\",\n\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\"Use this endpoint to disable two factor authentication.\",\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: \"Successful response\",\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\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\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\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\t\t\tconst { password } = ctx.body;\n\t\t\t\t\tconst isPasswordValid = await validatePassword(ctx, {\n\t\t\t\t\t\tpassword,\n\t\t\t\t\t\tuserId: user.id,\n\t\t\t\t\t});\n\t\t\t\t\tif (!isPasswordValid) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.INVALID_PASSWORD,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\tuser.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttwoFactorEnabled: false,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.adapter.delete({\n\t\t\t\t\t\tmodel: opts.twoFactorTable,\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: updatedUser.id,\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\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\tupdatedUser.id,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tctx.context.session.session,\n\t\t\t\t\t);\n\t\t\t\t\t/**\n\t\t\t\t\t * Update the session cookie with the new user data\n\t\t\t\t\t */\n\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t});\n\t\t\t\t\t//remove current session\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\tctx.context.session.session.token,\n\t\t\t\t\t);\n\t\t\t\t\tconst disableTrustCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst disableTrustValue = await ctx.getSignedCookie(\n\t\t\t\t\t\tdisableTrustCookie.name,\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t);\n\t\t\t\t\tif (disableTrustValue) {\n\t\t\t\t\t\tconst [, trustId] = disableTrustValue.split(\"!\");\n\t\t\t\t\t\tif (trustId) {\n\t\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t\ttrustId,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\texpireCookie(ctx, disableTrustCookie);\n\t\t\t\t\t}\n\t\t\t\t\treturn ctx.json({ status: true });\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\toptions: options as NoInfer<O>,\n\t\thooks: {\n\t\t\tafter: [\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-in/email\" ||\n\t\t\t\t\t\t\tcontext.path === \"/sign-in/username\" ||\n\t\t\t\t\t\t\tcontext.path === \"/sign-in/phone-number\"\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 data = ctx.context.newSession;\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!data?.user.twoFactorEnabled) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst trustDeviceCookieAttrs = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Check for trust device cookie\n\t\t\t\t\t\tconst trustDeviceCookie = await ctx.getSignedCookie(\n\t\t\t\t\t\t\ttrustDeviceCookieAttrs.name,\n\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (trustDeviceCookie) {\n\t\t\t\t\t\t\tconst [token, trustIdentifier] = trustDeviceCookie.split(\"!\");\n\t\t\t\t\t\t\tif (token && trustIdentifier) {\n\t\t\t\t\t\t\t\tconst expectedToken = await createHMAC(\n\t\t\t\t\t\t\t\t\t\"SHA-256\",\n\t\t\t\t\t\t\t\t\t\"base64urlnopad\",\n\t\t\t\t\t\t\t\t).sign(\n\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t`${data.user.id}!${trustIdentifier}`,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tif (token === expectedToken) {\n\t\t\t\t\t\t\t\t\t// HMAC is valid; verify the server-side record\n\t\t\t\t\t\t\t\t\tconst verificationRecord =\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t\t\t\t\t\t\ttrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\t\t\tverificationRecord &&\n\t\t\t\t\t\t\t\t\t\tverificationRecord.value === data.user.id &&\n\t\t\t\t\t\t\t\t\t\tverificationRecord.expiresAt > new Date()\n\t\t\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t\t\t\t\t\ttrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tconst newTrustIdentifier = `trust-device-${generateRandomString(32)}`;\n\t\t\t\t\t\t\t\t\t\tconst newToken = await createHMAC(\n\t\t\t\t\t\t\t\t\t\t\t\"SHA-256\",\n\t\t\t\t\t\t\t\t\t\t\t\"base64urlnopad\",\n\t\t\t\t\t\t\t\t\t\t).sign(\n\t\t\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t\t\t`${data.user.id}!${newTrustIdentifier}`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\t\t\t\t\t\tvalue: data.user.id,\n\t\t\t\t\t\t\t\t\t\t\tidentifier: newTrustIdentifier,\n\t\t\t\t\t\t\t\t\t\t\texpiresAt: new Date(\n\t\t\t\t\t\t\t\t\t\t\t\tDate.now() + trustDeviceMaxAge * 1000,\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\tconst newTrustDeviceCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tmaxAge: trustDeviceMaxAge,\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\tawait ctx.setSignedCookie(\n\t\t\t\t\t\t\t\t\t\t\tnewTrustDeviceCookie.name,\n\t\t\t\t\t\t\t\t\t\t\t`${newToken}!${newTrustIdentifier}`,\n\t\t\t\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\t\t\t\ttrustDeviceCookieAttrs.attributes,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\treturn;\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\texpireCookie(ctx, trustDeviceCookieAttrs);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * remove the session cookie. It's set by the sign in credential\n\t\t\t\t\t\t */\n\t\t\t\t\t\tdeleteSessionCookie(ctx, true);\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(data.session.token);\n\t\t\t\t\t\tconst maxAge = options?.twoFactorCookieMaxAge ?? 10 * 60; // 10 minutes\n\t\t\t\t\t\tconst twoFactorCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\t\tTWO_FACTOR_COOKIE_NAME,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmaxAge,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst identifier = `2fa-${generateRandomString(20)}`;\n\t\t\t\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\t\t\t\tvalue: data.user.id,\n\t\t\t\t\t\t\tidentifier,\n\t\t\t\t\t\t\texpiresAt: new Date(Date.now() + maxAge * 1000),\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait ctx.setSignedCookie(\n\t\t\t\t\t\t\ttwoFactorCookie.name,\n\t\t\t\t\t\t\tidentifier,\n\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\ttwoFactorCookie.attributes,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\ttwoFactorRedirect: true,\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\tschema: mergeSchema(schema, {\n\t\t\t...options?.schema,\n\t\t\ttwoFactor: {\n\t\t\t\t...options?.schema?.twoFactor,\n\t\t\t\t...(options?.twoFactorTable\n\t\t\t\t\t? { modelName: options.twoFactorTable }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t}),\n\t\trateLimit: [\n\t\t\t{\n\t\t\t\tpathMatcher(path) {\n\t\t\t\t\treturn path.startsWith(\"/two-factor/\");\n\t\t\t\t},\n\t\t\t\twindow: 10,\n\t\t\t\tmax: 3,\n\t\t\t},\n\t\t],\n\t\t$ERROR_CODES: TWO_FACTOR_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport * from \"./client\";\nexport * from \"./types\";\n"],"mappings":"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"}
|
|
@@ -67,7 +67,7 @@ declare const otp2fa: (options?: OTPOptions | undefined) => {
|
|
|
67
67
|
* ### API Methods
|
|
68
68
|
*
|
|
69
69
|
* **server:**
|
|
70
|
-
* `auth.api.
|
|
70
|
+
* `auth.api.sendTwoFactorOTP`
|
|
71
71
|
*
|
|
72
72
|
* **client:**
|
|
73
73
|
* `authClient.twoFactor.sendOtp`
|
|
@@ -113,7 +113,7 @@ declare const otp2fa: (options?: OTPOptions | undefined) => {
|
|
|
113
113
|
* ### API Methods
|
|
114
114
|
*
|
|
115
115
|
* **server:**
|
|
116
|
-
* `auth.api.
|
|
116
|
+
* `auth.api.verifyTwoFactorOTP`
|
|
117
117
|
*
|
|
118
118
|
* **client:**
|
|
119
119
|
* `authClient.twoFactor.verifyOtp`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/otp/index.ts"],"sourcesContent":["import type { Awaitable, GenericEndpointContext } from \"@better-auth/core\";\nimport { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport * as z from \"zod\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport {\n\tconstantTimeEqual,\n\tgenerateRandomString,\n\tsymmetricDecrypt,\n\tsymmetricEncrypt,\n} from \"../../../crypto\";\nimport { parseUserOutput } from \"../../../db/schema\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type { TwoFactorProvider, UserWithTwoFactor } from \"../types\";\nimport { defaultKeyHasher } from \"../utils\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport interface OTPOptions {\n\t/**\n\t * How long the opt will be valid for in\n\t * minutes\n\t *\n\t * @default \"3 mins\"\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Number of digits for the OTP code\n\t *\n\t * @default 6\n\t */\n\tdigits?: number | undefined;\n\t/**\n\t * Send the otp to the user\n\t *\n\t * @param user - The user to send the otp to\n\t * @param otp - The otp to send\n\t * @param request - The request object\n\t * @returns void | Promise<void>\n\t */\n\tsendOTP?:\n\t\t| ((\n\t\t\t\t/**\n\t\t\t\t * The user to send the otp to\n\t\t\t\t * @type UserWithTwoFactor\n\t\t\t\t * @default UserWithTwoFactors\n\t\t\t\t */\n\t\t\t\tdata: {\n\t\t\t\t\tuser: UserWithTwoFactor;\n\t\t\t\t\totp: string;\n\t\t\t\t},\n\t\t\t\t/**\n\t\t\t\t * The request object\n\t\t\t\t */\n\t\t\t\tctx?: GenericEndpointContext,\n\t\t ) => Awaitable<void>)\n\t\t| undefined;\n\t/**\n\t * The number of allowed attempts for the OTP\n\t *\n\t * @default 5\n\t */\n\tallowedAttempts?: number | undefined;\n\tstoreOTP?:\n\t\t| (\n\t\t\t\t| \"plain\"\n\t\t\t\t| \"encrypted\"\n\t\t\t\t| \"hashed\"\n\t\t\t\t| { hash: (token: string) => Promise<string> }\n\t\t\t\t| {\n\t\t\t\t\t\tencrypt: (token: string) => Promise<string>;\n\t\t\t\t\t\tdecrypt: (token: string) => Promise<string>;\n\t\t\t\t }\n\t\t )\n\t\t| undefined;\n}\n\nconst verifyOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z.boolean().optional().meta({\n\t\tdescription:\n\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t}),\n});\n\nconst send2FaOTPBodySchema = z\n\t.object({\n\t\t/**\n\t\t * if true, the device will be trusted\n\t\t * for 30 days. It'll be refreshed on\n\t\t * every sign in request within this time.\n\t\t */\n\t\ttrustDevice: z.boolean().optional().meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t}),\n\t})\n\t.optional();\n\n/**\n * The otp adapter is created from the totp adapter.\n */\nexport const otp2fa = (options?: OTPOptions | undefined) => {\n\tconst opts = {\n\t\tstoreOTP: \"plain\",\n\t\tdigits: 6,\n\t\t...options,\n\t\tperiod: (options?.period || 3) * 60 * 1000,\n\t};\n\n\tasync function storeOTP(ctx: GenericEndpointContext, otp: string) {\n\t\tif (opts.storeOTP === \"hashed\") {\n\t\t\treturn await defaultKeyHasher(otp);\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\t\treturn await opts.storeOTP.hash(otp);\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"encrypt\" in opts.storeOTP) {\n\t\t\treturn await opts.storeOTP.encrypt(otp);\n\t\t}\n\t\tif (opts.storeOTP === \"encrypted\") {\n\t\t\treturn await symmetricEncrypt({\n\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\tdata: otp,\n\t\t\t});\n\t\t}\n\t\treturn otp;\n\t}\n\n\tasync function decryptOrHashForComparison(\n\t\tctx: GenericEndpointContext,\n\t\tstoredOtp: string,\n\t\tuserInput: string,\n\t): Promise<[string, string]> {\n\t\tif (opts.storeOTP === \"hashed\") {\n\t\t\t// For hashed storage: hash the user input and compare with stored hash\n\t\t\treturn [storedOtp, await defaultKeyHasher(userInput)];\n\t\t}\n\t\tif (opts.storeOTP === \"encrypted\") {\n\t\t\t// For encrypted storage: decrypt stored value and compare with plain input\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\tdata: storedOtp,\n\t\t\t});\n\t\t\treturn [decrypted, userInput];\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"encrypt\" in opts.storeOTP) {\n\t\t\tconst decrypted = await opts.storeOTP.decrypt(storedOtp);\n\t\t\treturn [decrypted, userInput];\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\t\t// For custom hash: hash the user input and compare with stored hash\n\t\t\treturn [storedOtp, await opts.storeOTP.hash(userInput)];\n\t\t}\n\t\t// Plain storage: compare directly\n\t\treturn [storedOtp, userInput];\n\t}\n\n\t/**\n\t * Generate OTP and send it to the user.\n\t */\n\tconst send2FaOTP = createAuthEndpoint(\n\t\t\"/two-factor/send-otp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: send2FaOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Send two factor OTP\",\n\t\t\t\t\tdescription: \"Send two factor OTP to the user\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\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},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!options || !options.sendOTP) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"send otp isn't configured. Please configure the send otp function on otp options.\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"otp isn't configured\",\n\t\t\t\t\tcode: \"OTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, key } = await verifyTwoFactor(ctx);\n\t\t\tconst code = generateRandomString(opts.digits, \"0-9\");\n\t\t\tconst hashedCode = await storeOTP(ctx, code);\n\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\tvalue: `${hashedCode}:0`,\n\t\t\t\tidentifier: `2fa-otp-${key}`,\n\t\t\t\texpiresAt: new Date(Date.now() + opts.period),\n\t\t\t});\n\t\t\tconst sendOTPResult = options.sendOTP(\n\t\t\t\t{ user: session.user as UserWithTwoFactor, otp: code },\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (sendOTPResult instanceof Promise) {\n\t\t\t\tawait ctx.context.runInBackgroundOrAwait(\n\t\t\t\t\tsendOTPResult.catch((e: unknown) => {\n\t\t\t\t\t\tctx.context.logger.error(\"Failed to send two-factor OTP\", e);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ status: true });\n\t\t},\n\t);\n\n\tconst verifyOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-otp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor OTP\",\n\t\t\t\t\tdescription: \"Verify two factor OTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Two-factor OTP verified successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttoken: {\n\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\tdescription:\n\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},\n\t\t\t\t\t\t\t\t\t\t\tuser: {\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\tid: {\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: \"Unique identifier of the 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\temail: {\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\tformat: \"email\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's email address\",\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\temailVerified: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Whether the email is verified\",\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\tname: {\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\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's name\",\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\timage: {\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\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's profile image URL\",\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\tcreatedAt: {\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the user was created\",\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\tupdatedAt: {\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\tformat: \"date-time\",\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\"Timestamp when the user was last updated\",\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: [\"id\", \"createdAt\", \"updatedAt\"],\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The authenticated user object\",\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\trequired: [\"token\", \"user\"],\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\tasync (ctx) => {\n\t\t\tconst { session, key, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst toCheckOtp =\n\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t);\n\t\t\tconst [otp, counter] = toCheckOtp?.value?.split(\":\") ?? [];\n\t\t\tif (!toCheckOtp || toCheckOtp.expiresAt < new Date()) {\n\t\t\t\tif (toCheckOtp) {\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst allowedAttempts = options?.allowedAttempts || 5;\n\t\t\tif (parseInt(counter!) >= allowedAttempts) {\n\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [storedValue, inputValue] = await decryptOrHashForComparison(\n\t\t\t\tctx,\n\t\t\t\totp!,\n\t\t\t\tctx.body.code,\n\t\t\t);\n\t\t\tconst isCodeValid = constantTimeEqual(\n\t\t\t\tnew TextEncoder().encode(storedValue),\n\t\t\t\tnew TextEncoder().encode(inputValue),\n\t\t\t);\n\t\t\tif (isCodeValid) {\n\t\t\t\tif (!session.user.twoFactorEnabled) {\n\t\t\t\t\tif (!session.session) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\tsession.user.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\tsession.user.id,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tsession.session,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\tsession.session.token,\n\t\t\t\t\t);\n\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t});\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\ttoken: newSession.token,\n\t\t\t\t\t\tuser: parseUserOutput(ctx.context.options, updatedUser),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn valid(ctx);\n\t\t\t} else {\n\t\t\t\tawait ctx.context.internalAdapter.updateVerificationByIdentifier(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: `${otp}:${(parseInt(counter!, 10) || 0) + 1}`,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"otp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/send-otp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.send2FaOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.sendOtp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-send-otp)\n\t\t\t */\n\t\t\tsendTwoFactorOTP: send2FaOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-otp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyOtp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-verify-otp)\n\t\t\t */\n\t\t\tverifyTwoFactorOTP: verifyOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/otp/index.ts"],"sourcesContent":["import type { Awaitable, GenericEndpointContext } from \"@better-auth/core\";\nimport { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport * as z from \"zod\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport {\n\tconstantTimeEqual,\n\tgenerateRandomString,\n\tsymmetricDecrypt,\n\tsymmetricEncrypt,\n} from \"../../../crypto\";\nimport { parseUserOutput } from \"../../../db/schema\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type { TwoFactorProvider, UserWithTwoFactor } from \"../types\";\nimport { defaultKeyHasher } from \"../utils\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport interface OTPOptions {\n\t/**\n\t * How long the opt will be valid for in\n\t * minutes\n\t *\n\t * @default \"3 mins\"\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Number of digits for the OTP code\n\t *\n\t * @default 6\n\t */\n\tdigits?: number | undefined;\n\t/**\n\t * Send the otp to the user\n\t *\n\t * @param user - The user to send the otp to\n\t * @param otp - The otp to send\n\t * @param request - The request object\n\t * @returns void | Promise<void>\n\t */\n\tsendOTP?:\n\t\t| ((\n\t\t\t\t/**\n\t\t\t\t * The user to send the otp to\n\t\t\t\t * @type UserWithTwoFactor\n\t\t\t\t * @default UserWithTwoFactors\n\t\t\t\t */\n\t\t\t\tdata: {\n\t\t\t\t\tuser: UserWithTwoFactor;\n\t\t\t\t\totp: string;\n\t\t\t\t},\n\t\t\t\t/**\n\t\t\t\t * The request object\n\t\t\t\t */\n\t\t\t\tctx?: GenericEndpointContext,\n\t\t ) => Awaitable<void>)\n\t\t| undefined;\n\t/**\n\t * The number of allowed attempts for the OTP\n\t *\n\t * @default 5\n\t */\n\tallowedAttempts?: number | undefined;\n\tstoreOTP?:\n\t\t| (\n\t\t\t\t| \"plain\"\n\t\t\t\t| \"encrypted\"\n\t\t\t\t| \"hashed\"\n\t\t\t\t| { hash: (token: string) => Promise<string> }\n\t\t\t\t| {\n\t\t\t\t\t\tencrypt: (token: string) => Promise<string>;\n\t\t\t\t\t\tdecrypt: (token: string) => Promise<string>;\n\t\t\t\t }\n\t\t )\n\t\t| undefined;\n}\n\nconst verifyOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z.boolean().optional().meta({\n\t\tdescription:\n\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t}),\n});\n\nconst send2FaOTPBodySchema = z\n\t.object({\n\t\t/**\n\t\t * if true, the device will be trusted\n\t\t * for 30 days. It'll be refreshed on\n\t\t * every sign in request within this time.\n\t\t */\n\t\ttrustDevice: z.boolean().optional().meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t}),\n\t})\n\t.optional();\n\n/**\n * The otp adapter is created from the totp adapter.\n */\nexport const otp2fa = (options?: OTPOptions | undefined) => {\n\tconst opts = {\n\t\tstoreOTP: \"plain\",\n\t\tdigits: 6,\n\t\t...options,\n\t\tperiod: (options?.period || 3) * 60 * 1000,\n\t};\n\n\tasync function storeOTP(ctx: GenericEndpointContext, otp: string) {\n\t\tif (opts.storeOTP === \"hashed\") {\n\t\t\treturn await defaultKeyHasher(otp);\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\t\treturn await opts.storeOTP.hash(otp);\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"encrypt\" in opts.storeOTP) {\n\t\t\treturn await opts.storeOTP.encrypt(otp);\n\t\t}\n\t\tif (opts.storeOTP === \"encrypted\") {\n\t\t\treturn await symmetricEncrypt({\n\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\tdata: otp,\n\t\t\t});\n\t\t}\n\t\treturn otp;\n\t}\n\n\tasync function decryptOrHashForComparison(\n\t\tctx: GenericEndpointContext,\n\t\tstoredOtp: string,\n\t\tuserInput: string,\n\t): Promise<[string, string]> {\n\t\tif (opts.storeOTP === \"hashed\") {\n\t\t\t// For hashed storage: hash the user input and compare with stored hash\n\t\t\treturn [storedOtp, await defaultKeyHasher(userInput)];\n\t\t}\n\t\tif (opts.storeOTP === \"encrypted\") {\n\t\t\t// For encrypted storage: decrypt stored value and compare with plain input\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secretConfig,\n\t\t\t\tdata: storedOtp,\n\t\t\t});\n\t\t\treturn [decrypted, userInput];\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"encrypt\" in opts.storeOTP) {\n\t\t\tconst decrypted = await opts.storeOTP.decrypt(storedOtp);\n\t\t\treturn [decrypted, userInput];\n\t\t}\n\t\tif (typeof opts.storeOTP === \"object\" && \"hash\" in opts.storeOTP) {\n\t\t\t// For custom hash: hash the user input and compare with stored hash\n\t\t\treturn [storedOtp, await opts.storeOTP.hash(userInput)];\n\t\t}\n\t\t// Plain storage: compare directly\n\t\treturn [storedOtp, userInput];\n\t}\n\n\t/**\n\t * Generate OTP and send it to the user.\n\t */\n\tconst send2FaOTP = createAuthEndpoint(\n\t\t\"/two-factor/send-otp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: send2FaOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Send two factor OTP\",\n\t\t\t\t\tdescription: \"Send two factor OTP to the user\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\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},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (!options || !options.sendOTP) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"send otp isn't configured. Please configure the send otp function on otp options.\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"otp isn't configured\",\n\t\t\t\t\tcode: \"OTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, key } = await verifyTwoFactor(ctx);\n\t\t\tconst code = generateRandomString(opts.digits, \"0-9\");\n\t\t\tconst hashedCode = await storeOTP(ctx, code);\n\t\t\tawait ctx.context.internalAdapter.createVerificationValue({\n\t\t\t\tvalue: `${hashedCode}:0`,\n\t\t\t\tidentifier: `2fa-otp-${key}`,\n\t\t\t\texpiresAt: new Date(Date.now() + opts.period),\n\t\t\t});\n\t\t\tconst sendOTPResult = options.sendOTP(\n\t\t\t\t{ user: session.user as UserWithTwoFactor, otp: code },\n\t\t\t\tctx,\n\t\t\t);\n\t\t\tif (sendOTPResult instanceof Promise) {\n\t\t\t\tawait ctx.context.runInBackgroundOrAwait(\n\t\t\t\t\tsendOTPResult.catch((e: unknown) => {\n\t\t\t\t\t\tctx.context.logger.error(\"Failed to send two-factor OTP\", e);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn ctx.json({ status: true });\n\t\t},\n\t);\n\n\tconst verifyOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-otp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor OTP\",\n\t\t\t\t\tdescription: \"Verify two factor OTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Two-factor OTP verified successfully\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttoken: {\n\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\tdescription:\n\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},\n\t\t\t\t\t\t\t\t\t\t\tuser: {\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\tid: {\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: \"Unique identifier of the 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\temail: {\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\tformat: \"email\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's email address\",\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\temailVerified: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Whether the email is verified\",\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\tname: {\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\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's name\",\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\timage: {\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\tformat: \"uri\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"User's profile image URL\",\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\tcreatedAt: {\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\tformat: \"date-time\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"Timestamp when the user was created\",\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\tupdatedAt: {\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\tformat: \"date-time\",\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\"Timestamp when the user was last updated\",\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: [\"id\", \"createdAt\", \"updatedAt\"],\n\t\t\t\t\t\t\t\t\t\t\t\tdescription: \"The authenticated user object\",\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\trequired: [\"token\", \"user\"],\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\tasync (ctx) => {\n\t\t\tconst { session, key, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst toCheckOtp =\n\t\t\t\tawait ctx.context.internalAdapter.findVerificationValue(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t);\n\t\t\tconst [otp, counter] = toCheckOtp?.value?.split(\":\") ?? [];\n\t\t\tif (!toCheckOtp || toCheckOtp.expiresAt < new Date()) {\n\t\t\t\tif (toCheckOtp) {\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst allowedAttempts = options?.allowedAttempts || 5;\n\t\t\tif (parseInt(counter!) >= allowedAttempts) {\n\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationByIdentifier(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst [storedValue, inputValue] = await decryptOrHashForComparison(\n\t\t\t\tctx,\n\t\t\t\totp!,\n\t\t\t\tctx.body.code,\n\t\t\t);\n\t\t\tconst isCodeValid = constantTimeEqual(\n\t\t\t\tnew TextEncoder().encode(storedValue),\n\t\t\t\tnew TextEncoder().encode(inputValue),\n\t\t\t);\n\t\t\tif (isCodeValid) {\n\t\t\t\tif (!session.user.twoFactorEnabled) {\n\t\t\t\t\tif (!session.session) {\n\t\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\t\tBASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\t\tsession.user.id,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tconst newSession = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\t\tsession.user.id,\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\tsession.session,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\tsession.session.token,\n\t\t\t\t\t);\n\t\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\t\tsession: newSession,\n\t\t\t\t\t\tuser: updatedUser,\n\t\t\t\t\t});\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\ttoken: newSession.token,\n\t\t\t\t\t\tuser: parseUserOutput(ctx.context.options, updatedUser),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn valid(ctx);\n\t\t\t} else {\n\t\t\t\tawait ctx.context.internalAdapter.updateVerificationByIdentifier(\n\t\t\t\t\t`2fa-otp-${key}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: `${otp}:${(parseInt(counter!, 10) || 0) + 1}`,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"otp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/send-otp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.sendTwoFactorOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.sendOtp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-send-otp)\n\t\t\t */\n\t\t\tsendTwoFactorOTP: send2FaOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-otp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyTwoFactorOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyOtp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#api-method-two-factor-verify-otp)\n\t\t\t */\n\t\t\tverifyTwoFactorOTP: verifyOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { User } from "../../types/models.mjs";
|
|
2
2
|
import { InferOptionSchema } from "../../types/plugins.mjs";
|
|
3
|
-
import "../../types/index.mjs";
|
|
4
3
|
import { BackupCodeOptions } from "./backup-codes/index.mjs";
|
|
5
4
|
import { OTPOptions } from "./otp/index.mjs";
|
|
6
5
|
import { schema } from "./schema.mjs";
|
|
@@ -13,6 +12,13 @@ interface TwoFactorOptions {
|
|
|
13
12
|
* Application Name
|
|
14
13
|
*/
|
|
15
14
|
issuer?: string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The name of the table that stores the two factor
|
|
17
|
+
* authentication data.
|
|
18
|
+
*
|
|
19
|
+
* @default "twoFactor"
|
|
20
|
+
*/
|
|
21
|
+
twoFactorTable?: string | undefined;
|
|
16
22
|
/**
|
|
17
23
|
* TOTP OPtions
|
|
18
24
|
*/
|