diff --git a/e2e/smoke/test/fixtures/tsconfig-decelration/package.json b/e2e/smoke/test/fixtures/tsconfig-decelration/package.json index 7afdeebeaf..5d285600ad 100644 --- a/e2e/smoke/test/fixtures/tsconfig-decelration/package.json +++ b/e2e/smoke/test/fixtures/tsconfig-decelration/package.json @@ -6,7 +6,9 @@ "typecheck": "tsc --project tsconfig.json" }, "dependencies": { + "@better-auth/sso": "workspace:*", + "@better-auth/stripe": "workspace:*", "better-auth": "workspace:*", - "better-sqlite3": "^12.4.1" + "stripe": "^18.5.0" } } diff --git a/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts b/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts new file mode 100644 index 0000000000..ae42a34580 --- /dev/null +++ b/e2e/smoke/test/fixtures/tsconfig-decelration/src/demo.ts @@ -0,0 +1,209 @@ +import { betterAuth } from "better-auth"; +import { + bearer, + admin, + multiSession, + organization, + twoFactor, + oneTap, + oAuthProxy, + openAPI, + customSession, + deviceAuthorization, + lastLoginMethod, +} from "better-auth/plugins"; +import { nextCookies } from "better-auth/next-js"; +import { passkey } from "better-auth/plugins/passkey"; +import { stripe } from "@better-auth/stripe"; +import { sso } from "@better-auth/sso"; +import { Stripe } from "stripe"; + +export const auth = betterAuth({ + appName: "Better Auth Demo", + plugins: [ + organization({}), + twoFactor({}), + passkey(), + openAPI(), + bearer(), + admin({ adminUserIds: ["EXD5zjob2SD6CBWcEQ6OpLRHcyoUbnaB"] }), + multiSession(), + oAuthProxy(), + nextCookies(), + oneTap(), + customSession(async (session) => { + return { + ...session, + user: { + ...session.user, + dd: "test", + }, + }; + }), + stripe({ + stripeClient: new Stripe(process.env.STRIPE_KEY || "sk_test_"), + stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!, + subscription: { + enabled: true, + allowReTrialsForDifferentPlans: true, + plans: () => { + const PRO_PRICE_ID = { + default: + process.env.STRIPE_PRO_PRICE_ID ?? + "price_1RoxnRHmTADgihIt4y8c0lVE", + annual: + process.env.STRIPE_PRO_ANNUAL_PRICE_ID ?? + "price_1RoxnoHmTADgihItzFvVP8KT", + }; + const PLUS_PRICE_ID = { + default: + process.env.STRIPE_PLUS_PRICE_ID ?? + "price_1RoxnJHmTADgihIthZTLmrPn", + annual: + process.env.STRIPE_PLUS_ANNUAL_PRICE_ID ?? + "price_1Roxo5HmTADgihItEbJu5llL", + }; + + return [ + { + name: "Plus", + priceId: PLUS_PRICE_ID.default, + annualDiscountPriceId: PLUS_PRICE_ID.annual, + freeTrial: { + days: 7, + }, + }, + { + name: "Pro", + priceId: PRO_PRICE_ID.default, + annualDiscountPriceId: PRO_PRICE_ID.annual, + freeTrial: { + days: 7, + }, + }, + ]; + }, + }, + }), + sso({ + defaultSSO: [ + { + domain: "http://localhost:3000", + providerId: "sso", + samlConfig: { + issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata", + entryPoint: + "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435", + cert: `-----BEGIN CERTIFICATE----- + MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL + BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx + MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ + HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj + yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g + 0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7 + mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO + aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE + FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm + RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z + FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm + 2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx + UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J + QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9 + vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+ + jzGhYL6m9gFTm/8= + -----END CERTIFICATE-----`, + spMetadata: { + metadata: ` + + + + + + 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 + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + + + Organization Name + Organization DisplayName + http://localhost:3000/ + + + Technical Contact Name + technical_contact@gmail.com + + + Support Contact Name + support_contact@gmail.com + + + `, + }, + idpMetadata: { + entityURL: + "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/metadata", + entityID: + "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435", + redirectURL: + "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso", + singleSignOnService: [ + { + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", + Location: + "https://dummyidp.com/apps/app_01k16v4vb5yytywqjjvv2b3435/sso", + }, + ], + cert: `-----BEGIN CERTIFICATE----- + MIIDBzCCAe+gAwIBAgIUCLBK4f75EXEe4gyroYnVaqLoSp4wDQYJKoZIhvcNAQEL + BQAwEzERMA8GA1UEAwwIZHVtbXlpZHAwHhcNMjQwNTEzMjE1NDE2WhcNMzQwNTEx + MjE1NDE2WjATMREwDwYDVQQDDAhkdW1teWlkcDCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBAKhmgQmWb8NvGhz952XY4SlJlpWIK72RilhOZS9frDYhqWVJ + HsGH9Z7sSzrM/0+YvCyEWuZV9gpMeIaHZxEPDqW3RJ7KG51fn/s/qFvwctf+CZDj + yfGDzYs+XIgf7p56U48EmYeWpB/aUW64gSbnPqrtWmVFBisOfIx5aY3NubtTsn+g + 0XbdX0L57+NgSvPQHXh/GPXA7xCIWm54G5kqjozxbKEFA0DS3yb6oHRQWHqIAM/7 + mJMdUVZNIV1q7c2JIgAl23uDWq+2KTE2R5liP/KjvjwKonVKtTqGqX6ei25rsTHO + aDpBH/LdQK2txgsm7R7+IThWNvUI0TttrmwBqyMCAwEAAaNTMFEwHQYDVR0OBBYE + FD142gxIAJMhpgMkgpzmRNoW9XbEMB8GA1UdIwQYMBaAFD142gxIAJMhpgMkgpzm + RNoW9XbEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADQd6k6z + FIc20GfGHY5C2MFwyGOmP5/UG/JiTq7Zky28G6D0NA0je+GztzXx7VYDfCfHxLcm + 2k5t9nYhb9kVawiLUUDVF6s+yZUXA4gUA3KoTWh1/oRxR3ggW7dKYm9fsNOdQAbx + UUkzp7HLZ45ZlpKUS0hO7es+fPyF5KVw0g0SrtQWwWucnQMAQE9m+B0aOf+92y7J + QkdgdR8Gd/XZ4NZfoOnKV7A1utT4rWxYCgICeRTHx9tly5OhPW4hQr5qOpngcsJ9 + vhr86IjznQXhfj3hql5lA3VbHW04ro37ROIkh2bShDq5dwJJHpYCGrF3MQv8S3m+ + jzGhYL6m9gFTm/8= + -----END CERTIFICATE-----`, + }, + callbackUrl: "/dashboard", + }, + }, + ], + }), + deviceAuthorization({ + expiresIn: "3min", + interval: "5s", + }), + lastLoginMethod(), + ], +}); + +auth.api + .createOrganization({ + body: { + name: "My Org", + slug: "my-org", + }, + }) + .catch(); diff --git a/packages/better-auth/src/api/index.ts b/packages/better-auth/src/api/index.ts index d792e2d4d1..ca7ad2b4e4 100644 --- a/packages/better-auth/src/api/index.ts +++ b/packages/better-auth/src/api/index.ts @@ -6,7 +6,7 @@ import { } from "better-call"; import type { BetterAuthOptions } from "@better-auth/core"; import type { UnionToIntersection } from "../types/helper"; -import { originCheckMiddleware } from "./middlewares/origin-check"; +import { originCheckMiddleware } from "./middlewares"; import { callbackOAuth, forgetPassword, @@ -37,9 +37,9 @@ import { requestPasswordReset, requestPasswordResetCallback, } from "./routes"; -import { ok } from "./routes/ok"; -import { signUpEmail } from "./routes/sign-up"; -import { error } from "./routes/error"; +import { ok } from "./routes"; +import { signUpEmail } from "./routes"; +import { error } from "./routes"; import { type InternalLogger, logger } from "@better-auth/core/env"; import type { BetterAuthPlugin } from "@better-auth/core"; import { onRequestRateLimit } from "./rate-limiter"; diff --git a/packages/better-auth/src/plugins/oauth-proxy/index.ts b/packages/better-auth/src/plugins/oauth-proxy/index.ts index e6915e1f52..e20598efb8 100644 --- a/packages/better-auth/src/plugins/oauth-proxy/index.ts +++ b/packages/better-auth/src/plugins/oauth-proxy/index.ts @@ -21,7 +21,7 @@ function getVenderBaseURL() { return vercel || netlify || render || aws || google || azure; } -interface OAuthProxyOptions { +export interface OAuthProxyOptions { /** * The current URL of the application. * The plugin will attempt to infer the current URL from your environment diff --git a/packages/better-auth/src/plugins/open-api/generator.ts b/packages/better-auth/src/plugins/open-api/generator.ts index a65d930d04..5d7700be0c 100644 --- a/packages/better-auth/src/plugins/open-api/generator.ts +++ b/packages/better-auth/src/plugins/open-api/generator.ts @@ -81,13 +81,13 @@ function getTypeFromZodType(zodType: z.ZodType) { return allowedType.has(type) ? (type as AllowedType) : "string"; } -type FieldSchema = { +export type FieldSchema = { type: DBFieldType; default?: DBFieldAttributeConfig["defaultValue"] | "Generated at runtime"; readOnly?: boolean; }; -type OpenAPIModelSchema = { +export type OpenAPIModelSchema = { type: "object"; properties: Record; required?: string[]; diff --git a/packages/better-auth/src/plugins/open-api/index.ts b/packages/better-auth/src/plugins/open-api/index.ts index 7467d1a729..0be0b6f6ec 100644 --- a/packages/better-auth/src/plugins/open-api/index.ts +++ b/packages/better-auth/src/plugins/open-api/index.ts @@ -6,6 +6,8 @@ import type { LiteralString } from "../../types/helper"; import { APIError } from "../../api"; import { createAuthEndpoint } from "@better-auth/core/api"; +export type { FieldSchema, Path, OpenAPIModelSchema } from "./generator"; + type ScalarTheme = | "alternate" | "default" diff --git a/packages/better-auth/src/plugins/organization/client.ts b/packages/better-auth/src/plugins/organization/client.ts index 79d826d6d9..a65a197f54 100644 --- a/packages/better-auth/src/plugins/organization/client.ts +++ b/packages/better-auth/src/plugins/organization/client.ts @@ -10,7 +10,7 @@ import type { import type { Prettify } from "../../types/helper"; import { type AccessControl, type Role } from "../access"; import type { BetterAuthClientPlugin } from "@better-auth/core"; -import { organization } from "./organization"; +import { type OrganizationPlugin } from "./organization"; import { useAuthQuery } from "../../client"; import { defaultStatements, @@ -129,29 +129,27 @@ export const organizationClient = ( type Schema = CO["schema"]; return { id: "organization", - $InferServerPlugin: {} as ReturnType< - typeof organization<{ - ac: CO["ac"] extends AccessControl - ? CO["ac"] - : AccessControl; - roles: CO["roles"] extends Record - ? CO["roles"] - : { - admin: Role; - member: Role; - owner: Role; - }; - teams: { - enabled: CO["teams"] extends { enabled: true } ? true : false; - }; - schema: Schema; - dynamicAccessControl: { - enabled: CO["dynamicAccessControl"] extends { enabled: true } - ? true - : false; - }; - }> - >, + $InferServerPlugin: {} as OrganizationPlugin<{ + ac: CO["ac"] extends AccessControl + ? CO["ac"] + : AccessControl; + roles: CO["roles"] extends Record + ? CO["roles"] + : { + admin: Role; + member: Role; + owner: Role; + }; + teams: { + enabled: CO["teams"] extends { enabled: true } ? true : false; + }; + schema: Schema; + dynamicAccessControl: { + enabled: CO["dynamicAccessControl"] extends { enabled: true } + ? true + : false; + }; + }>, getActions: ($fetch, _$store, co) => ({ $Infer: { ActiveOrganization: {} as OrganizationReturn, diff --git a/packages/better-auth/src/plugins/organization/organization.ts b/packages/better-auth/src/plugins/organization/organization.ts index e77887ffbc..82d072d840 100644 --- a/packages/better-auth/src/plugins/organization/organization.ts +++ b/packages/better-auth/src/plugins/organization/organization.ts @@ -2,7 +2,7 @@ import { APIError } from "better-call"; import * as z from "zod"; import type { BetterAuthPluginDBSchema } from "@better-auth/core/db"; import { createAuthEndpoint } from "@better-auth/core/api"; -import { getSessionFromCtx } from "../../api/routes"; +import { getSessionFromCtx } from "../../api"; import type { BetterAuthPlugin } from "@better-auth/core"; import { shimContext } from "../../utils/shim"; import { type AccessControl } from "../access"; @@ -50,6 +50,7 @@ import type { InferInvitation, InferMember, InferOrganization, + OrganizationSchema, Team, TeamMember, } from "./schema"; @@ -70,6 +71,211 @@ export function parseRoles(roles: string | string[]): string { return Array.isArray(roles) ? roles.join(",") : roles; } +export type DynamicAccessControlEndpoints = { + createOrgRole: ReturnType>; + deleteOrgRole: ReturnType>; + listOrgRoles: ReturnType>; + getOrgRole: ReturnType>; + updateOrgRole: ReturnType>; +}; + +export type TeamEndpoints = { + createTeam: ReturnType>; + listOrganizationTeams: ReturnType>; + removeTeam: ReturnType>; + updateTeam: ReturnType>; + setActiveTeam: ReturnType>; + listUserTeams: ReturnType>; + listTeamMembers: ReturnType>; + addTeamMember: ReturnType>; + removeTeamMember: ReturnType>; +}; + +export type OrganizationEndpoints = { + createOrganization: ReturnType>; + updateOrganization: ReturnType>; + deleteOrganization: ReturnType>; + setActiveOrganization: ReturnType>; + getFullOrganization: ReturnType>; + listOrganizations: ReturnType>; + createInvitation: ReturnType>; + cancelInvitation: ReturnType>; + acceptInvitation: ReturnType>; + getInvitation: ReturnType>; + rejectInvitation: ReturnType>; + listInvitations: ReturnType>; + getActiveMember: ReturnType>; + checkOrganizationSlug: ReturnType>; + addMember: ReturnType>; + removeMember: ReturnType>; + updateMemberRole: ReturnType>; + leaveOrganization: ReturnType>; + listUserInvitations: ReturnType>; + listMembers: ReturnType>; + getActiveMemberRole: ReturnType>; + hasPermission: ReturnType>; +}; + +const createHasPermission = (options: O) => { + type DefaultStatements = typeof defaultStatements; + type Statements = O["ac"] extends AccessControl + ? S + : DefaultStatements; + type PermissionType = { + [key in keyof Statements]?: Array< + Statements[key] extends readonly unknown[] + ? Statements[key][number] + : never + >; + }; + type PermissionExclusive = + | { + /** + * @deprecated Use `permissions` instead + */ + permission: PermissionType; + permissions?: never; + } + | { + permissions: PermissionType; + permission?: never; + }; + + return createAuthEndpoint( + "/organization/has-permission", + { + method: "POST", + requireHeaders: true, + body: z + .object({ + organizationId: z.string().optional(), + }) + .and( + z.union([ + z.object({ + permission: z.record(z.string(), z.array(z.string())), + permissions: z.undefined(), + }), + z.object({ + permission: z.undefined(), + permissions: z.record(z.string(), z.array(z.string())), + }), + ]), + ), + use: [orgSessionMiddleware], + metadata: { + $Infer: { + body: {} as PermissionExclusive & { + organizationId?: string; + }, + }, + openapi: { + description: "Check if the user has permission", + requestBody: { + content: { + "application/json": { + schema: { + type: "object", + properties: { + permission: { + type: "object", + description: "The permission to check", + deprecated: true, + }, + permissions: { + type: "object", + description: "The permission to check", + }, + }, + required: ["permissions"], + }, + }, + }, + }, + responses: { + "200": { + description: "Success", + content: { + "application/json": { + schema: { + type: "object", + properties: { + error: { + type: "string", + }, + success: { + type: "boolean", + }, + }, + required: ["success"], + }, + }, + }, + }, + }, + }, + }, + }, + async (ctx) => { + const activeOrganizationId = + ctx.body.organizationId || + ctx.context.session.session.activeOrganizationId; + if (!activeOrganizationId) { + throw new APIError("BAD_REQUEST", { + message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION, + }); + } + const adapter = getOrgAdapter(ctx.context, options); + const member = await adapter.findMemberByOrgId({ + userId: ctx.context.session.user.id, + organizationId: activeOrganizationId, + }); + if (!member) { + throw new APIError("UNAUTHORIZED", { + message: + ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION, + }); + } + const result = await hasPermission( + { + role: member.role, + options: options || {}, + permissions: (ctx.body.permissions ?? ctx.body.permission) as any, + organizationId: activeOrganizationId, + }, + ctx, + ); + + return ctx.json({ + error: null, + success: result, + }); + }, + ); +}; + +export type OrganizationPlugin = { + id: "organization"; + endpoints: OrganizationEndpoints & + (O extends { teams: { enabled: true } } ? TeamEndpoints : {}) & + (O extends { dynamicAccessControl: { enabled: true } } + ? DynamicAccessControlEndpoints + : {}); + schema: OrganizationSchema; + $Infer: { + Organization: InferOrganization; + Invitation: InferInvitation; + Member: InferMember; + Team: O["teams"] extends { enabled: true } ? Team : any; + TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any; + ActiveOrganization: Awaited< + ReturnType>> + >; + }; + $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES; + options: O; +}; + /** * Organization plugin for Better Auth. Organization allows you to create teams, members, * and manage access control for your users. @@ -77,15 +283,106 @@ export function parseRoles(roles: string | string[]): string { * @example * ```ts * const auth = betterAuth({ - * plugins: [ - * organization({ - * allowUserToCreateOrganization: true, - * }), - * ], + * plugins: [ + * organization({ + * allowUserToCreateOrganization: true, + * }), + * ], * }); * ``` */ -export const organization = (options?: O) => { +export function organization< + O extends OrganizationOptions & { + teams: { enabled: true }; + }, +>( + options?: O, +): { + id: "organization"; + endpoints: OrganizationEndpoints & TeamEndpoints; + schema: OrganizationSchema; + $Infer: { + Organization: InferOrganization; + Invitation: InferInvitation; + Member: InferMember; + Team: O["teams"] extends { enabled: true } ? Team : any; + TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any; + ActiveOrganization: Awaited< + ReturnType>> + >; + }; + $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES; + options: O; +}; +export function organization< + O extends OrganizationOptions & { + teams: { enabled: true }; + dynamicAccessControl: { enabled: true }; + }, +>( + options?: O, +): { + id: "organization"; + endpoints: OrganizationEndpoints & + TeamEndpoints & + DynamicAccessControlEndpoints; + schema: OrganizationSchema; + $Infer: { + Organization: InferOrganization; + Invitation: InferInvitation; + Member: InferMember; + Team: O["teams"] extends { enabled: true } ? Team : any; + TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any; + ActiveOrganization: Awaited< + ReturnType>> + >; + }; + $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES; + options: O; +}; +export function organization< + O extends OrganizationOptions & { + dynamicAccessControl: { enabled: true }; + }, +>( + options?: O, +): { + id: "organization"; + endpoints: OrganizationEndpoints & DynamicAccessControlEndpoints; + schema: OrganizationSchema; + $Infer: { + Organization: InferOrganization; + Invitation: InferInvitation; + Member: InferMember; + Team: O["teams"] extends { enabled: true } ? Team : any; + TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any; + ActiveOrganization: Awaited< + ReturnType>> + >; + }; + $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES; + options: O; +}; +export function organization( + options?: O, +): { + id: "organization"; + endpoints: OrganizationEndpoints; + schema: OrganizationSchema; + $Infer: { + Organization: InferOrganization; + Invitation: InferInvitation; + Member: InferMember; + Team: O["teams"] extends { enabled: true } ? Team : any; + TeamMember: O["teams"] extends { enabled: true } ? TeamMember : any; + ActiveOrganization: Awaited< + ReturnType>> + >; + }; + $ERROR_CODES: typeof ORGANIZATION_ERROR_CODES; + options: O; +}; +export function organization(options?: O): any { let endpoints = { /** * ### Endpoint @@ -857,153 +1154,11 @@ export const organization = (options?: O) => { }, }); - type DefaultStatements = typeof defaultStatements; - type Statements = O["ac"] extends AccessControl - ? S - : DefaultStatements; - type PermissionType = { - [key in keyof Statements]?: Array< - Statements[key] extends readonly unknown[] - ? Statements[key][number] - : never - >; - }; - type PermissionExclusive = - | { - /** - * @deprecated Use `permissions` instead - */ - permission: PermissionType; - permissions?: never; - } - | { - permissions: PermissionType; - permission?: never; - }; - - type OrganizationEndpoints = (O["dynamicAccessControl"] extends { - enabled: true; - } - ? typeof dynamicAccessControlEndpoints - : {}) & - (O["teams"] extends { enabled: true } ? typeof teamEndpoints : {}) & - typeof endpoints; - return { id: "organization", endpoints: { - ...(api as OrganizationEndpoints), - hasPermission: createAuthEndpoint( - "/organization/has-permission", - { - method: "POST", - requireHeaders: true, - body: z - .object({ - organizationId: z.string().optional(), - }) - .and( - z.union([ - z.object({ - permission: z.record(z.string(), z.array(z.string())), - permissions: z.undefined(), - }), - z.object({ - permission: z.undefined(), - permissions: z.record(z.string(), z.array(z.string())), - }), - ]), - ), - use: [orgSessionMiddleware], - metadata: { - $Infer: { - body: {} as PermissionExclusive & { - organizationId?: string; - }, - }, - openapi: { - description: "Check if the user has permission", - requestBody: { - content: { - "application/json": { - schema: { - type: "object", - properties: { - permission: { - type: "object", - description: "The permission to check", - deprecated: true, - }, - permissions: { - type: "object", - description: "The permission to check", - }, - }, - required: ["permissions"], - }, - }, - }, - }, - responses: { - "200": { - description: "Success", - content: { - "application/json": { - schema: { - type: "object", - properties: { - error: { - type: "string", - }, - success: { - type: "boolean", - }, - }, - required: ["success"], - }, - }, - }, - }, - }, - }, - }, - }, - async (ctx) => { - const activeOrganizationId = - ctx.body.organizationId || - ctx.context.session.session.activeOrganizationId; - if (!activeOrganizationId) { - throw new APIError("BAD_REQUEST", { - message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION, - }); - } - const adapter = getOrgAdapter(ctx.context, options); - const member = await adapter.findMemberByOrgId({ - userId: ctx.context.session.user.id, - organizationId: activeOrganizationId, - }); - if (!member) { - throw new APIError("UNAUTHORIZED", { - message: - ORGANIZATION_ERROR_CODES.USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION, - }); - } - const result = await hasPermission( - { - role: member.role, - options: options || {}, - permissions: (ctx.body.permissions ?? ctx.body.permission) as any, - organizationId: activeOrganizationId, - }, - ctx, - ); - - return ctx.json({ - error: null, - success: result, - }); - }, - ), + ...(api as unknown as OrganizationEndpoints), + hasPermission: createHasPermission(options as O), }, schema: { ...(schema as BetterAuthPluginDBSchema), @@ -1057,4 +1212,4 @@ export const organization = (options?: O) => { $ERROR_CODES: ORGANIZATION_ERROR_CODES, options: options as O, } satisfies BetterAuthPlugin; -}; +} diff --git a/packages/better-auth/src/plugins/organization/schema.ts b/packages/better-auth/src/plugins/organization/schema.ts index fefc8f29d5..2771291179 100644 --- a/packages/better-auth/src/plugins/organization/schema.ts +++ b/packages/better-auth/src/plugins/organization/schema.ts @@ -4,6 +4,516 @@ import type { OrganizationOptions } from "./types"; import type { InferAdditionalFieldsFromPluginOptions } from "../../db"; import type { Prettify } from "better-call"; +// TODO: we need a better way to define plugin schema and endpoints +export type OrganizationSchema = + O["dynamicAccessControl"] extends { enabled: true } + ? { + organizationRole: { + modelName: O["schema"] extends { + organizationRole: { modelName: infer M }; + } + ? M extends string + ? M + : string | undefined + : string | undefined; + fields: { + organizationId: { + type: "string"; + required: true; + references: { + model: "organization"; + field: "id"; + }; + fieldName?: O["schema"] extends { + organizationRole: { + fields: { organizationId: infer F }; + }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + role: { + type: "string"; + required: true; + fieldName?: O["schema"] extends { + organizationRole: { + fields: { role: infer F }; + }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + permission: { + type: "string"; + required: true; + fieldName?: O["schema"] extends { + organizationRole: { + fields: { permission: infer F }; + }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + createdAt: { + type: "date"; + required: true; + defaultValue: Date; + fieldName?: O["schema"] extends { + organizationRole: { + fields: { createdAt: infer F }; + }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + updatedAt: { + type: "date"; + required: false; + fieldName?: O["schema"] extends { + organizationRole: { + fields: { updatedAt: infer F }; + }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & (O["schema"] extends { + organizationRole: { additionalFields: infer F }; + } + ? F + : {}); + }; + } + : {} & (O["teams"] extends { enabled: true } + ? { + team: { + modelName: O["schema"] extends { team: { modelName: infer M } } + ? M extends string + ? M + : string | undefined + : string | undefined; + fields: { + name: { + type: "string"; + required: true; + fieldName?: O["schema"] extends { + team: { fields: { name: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + organizationId: { + type: "string"; + required: true; + references: { + model: "organization"; + field: "id"; + }; + fieldName?: O["schema"] extends { + team: { fields: { organizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + createdAt: { + type: "date"; + required: true; + fieldName?: O["schema"] extends { + team: { fields: { createdAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + updatedAt: { + type: "date"; + required: false; + fieldName?: O["schema"] extends { + team: { fields: { updatedAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & (O["schema"] extends { team: { additionalFields: infer F } } + ? F + : {}); + }; + teamMember: { + modelName: O["schema"] extends { + teamMember: { modelName: infer M }; + } + ? M extends string + ? M + : string | undefined + : string | undefined; + fields: { + teamId: { + type: "string"; + required: true; + references: { + model: "team"; + field: "id"; + }; + fieldName?: O["schema"] extends { + teamMember: { fields: { teamId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + userId: { + type: "string"; + required: true; + references: { + model: "user"; + field: "id"; + }; + fieldName?: O["schema"] extends { + teamMember: { fields: { userId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + createdAt: { + type: "date"; + required: false; + fieldName?: O["schema"] extends { + teamMember: { fields: { createdAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + }; + }; + } + : {}) & { + organization: { + modelName: O["schema"] extends { + organization: { modelName: infer M }; + } + ? M extends string + ? M + : string | undefined + : string | undefined; + fields: { + name: { + type: "string"; + required: true; + sortable: true; + fieldName?: O["schema"] extends { + organization: { fields: { name: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + slug: { + type: "string"; + required: true; + unique: true; + sortable: true; + fieldName?: O["schema"] extends { + organization: { fields: { slug: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + logo: { + type: "string"; + required: false; + fieldName?: O["schema"] extends { + organization: { fields: { logo: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + createdAt: { + type: "date"; + required: true; + fieldName?: O["schema"] extends { + organization: { fields: { createdAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + updatedAt: { + type: "date"; + required: false; + fieldName?: O["schema"] extends { + organization: { fields: { updatedAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + }; + }; + member: { + modelName: O["schema"] extends { member: { modelName: infer M } } + ? M extends string + ? M + : string | undefined + : string | undefined; + fields: { + organizationId: { + type: "string"; + required: true; + references: { + model: "organization"; + field: "id"; + }; + fieldName?: O["schema"] extends { + member: { fields: { organizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + userId: { + type: "string"; + required: true; + references: { + model: "user"; + field: "id"; + }; + fieldName?: O["schema"] extends { + member: { fields: { userId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + role: { + type: "string"; + required: true; + fieldName?: O["schema"] extends { + member: { fields: { role: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + defaultValue: "member"; + }; + createdAt: { + type: "date"; + required: true; + fieldName?: O["schema"] extends { + member: { fields: { createdAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & (O["schema"] extends { member: { additionalFields: infer F } } + ? F + : {}); + }; + invitation: { + modelName: O["schema"] extends { + invitation: { modelName: infer M }; + } + ? M + : string | undefined; + fields: { + organizationId: { + type: "string"; + required: true; + references: { + model: "organization"; + field: "id"; + }; + fieldName?: O["schema"] extends { + invitation: { fields: { organizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + email: { + type: "string"; + required: true; + sortable: true; + fieldName?: O["schema"] extends { + invitation: { fields: { email: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + role: { + type: "string"; + required: true; + sortable: true; + fieldName?: O["schema"] extends { + invitation: { fields: { role: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + status: { + type: "string"; + required: true; + sortable: true; + fieldName?: O["schema"] extends { + invitation: { fields: { status: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + defaultValue: "pending"; + }; + expiresAt: { + type: "date"; + required: false; + fieldName?: O["schema"] extends { + invitation: { fields: { expiresAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + createdAt: { + type: "date"; + required: true; + fieldName?: O["schema"] extends { + invitation: { fields: { createdAt: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + defaultValue: Date; + }; + inviterId: { + type: "string"; + required: true; + references: { + model: "user"; + field: "id"; + }; + fieldName?: O["schema"] extends { + invitation: { fields: { inviterId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & (O["schema"] extends { + invitation: { additionalFields: infer F }; + } + ? F + : {}) & + O extends { teams: { enabled: true } } + ? { + teamId: { + type: "string"; + required: false; + sortable: true; + fieldName?: O["schema"] extends { + invitation: { fields: { teamId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } + : {}; + }; + session: { + fields: { + activeOrganizationId: { + type: "string"; + required: false; + fieldName?: O["schema"] extends { + session: { fields: { activeOrganizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & O["teams"] extends { enabled: true } + ? { + activeTeamId: { + type: "string"; + required: false; + }; + activeOrganizationId: { + type: "string"; + required: false; + fieldName?: O["schema"] extends { + session: { fields: { activeOrganizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } + : { + activeOrganizationId: { + type: "string"; + required: false; + fieldName?: O["schema"] extends { + session: { fields: { activeOrganizationId: infer F } }; + } + ? F extends string + ? F + : string | undefined + : string | undefined; + }; + } & (O["schema"] extends { + session: { additionalFields: infer F }; + } + ? F extends string + ? F + : string | undefined + : {}); + }; + }; + export const role = z.string(); export const invitationStatus = z .enum(["pending", "accepted", "rejected", "canceled"]) diff --git a/packages/better-auth/src/types/types.test.ts b/packages/better-auth/src/types/types.test.ts index 8d7ccd0486..67b41b5e2a 100644 --- a/packages/better-auth/src/types/types.test.ts +++ b/packages/better-auth/src/types/types.test.ts @@ -44,7 +44,7 @@ describe("general types", async (it) => { twoFactorEnabled: boolean | undefined | null; }>(); - expectTypeOf().toEqualTypeOf<{ + expectTypeOf().toMatchObjectType<{ id: string; userId: string; expiresAt: Date; diff --git a/packages/core/src/db/index.ts b/packages/core/src/db/index.ts index 4379d02cd5..e89dcb6e27 100644 --- a/packages/core/src/db/index.ts +++ b/packages/core/src/db/index.ts @@ -4,7 +4,6 @@ import type { DBFieldType, DBPrimitive, BetterAuthDBSchema, - DBPreservedModels, } from "./type"; import type { BetterAuthPluginDBSchema } from "./plugin"; export type { BetterAuthPluginDBSchema } from "./plugin"; @@ -23,7 +22,7 @@ export type { DBPrimitive, BetterAuthDBSchema, DBPreservedModels, -}; +} from "./type"; /** * @deprecated Backport for 1.3.x, we will remove this in 1.4.x diff --git a/packages/core/src/types/init-options.ts b/packages/core/src/types/init-options.ts index c799099c41..b477427436 100644 --- a/packages/core/src/types/init-options.ts +++ b/packages/core/src/types/init-options.ts @@ -6,7 +6,7 @@ import type { DBFieldAttribute, DBPreservedModels, SecondaryStorage, -} from "../db/type"; +} from "../db"; import type { Account, RateLimit, Session, User, Verification } from "../db"; import type { Database as BunDatabase } from "bun:sqlite"; import type { DatabaseSync } from "node:sqlite"; @@ -15,7 +15,7 @@ import type { SocialProviderList, SocialProviders } from "../social-providers"; import type { Logger } from "../env"; import type { AuthContext, GenericEndpointContext } from "./context"; import type { AuthMiddleware } from "../api"; -import type { BetterAuthPlugin } from ".."; +import type { BetterAuthPlugin } from "./plugin"; type KyselyDatabaseType = "postgres" | "mysql" | "sqlite" | "mssql"; type OmitId = Omit; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2da01d535d..c576a0d883 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -352,7 +352,7 @@ importers: version: 12.23.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0) geist: specifier: ^1.4.2 - version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)) + version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)) input-otp: specifier: ^1.4.2 version: 1.4.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -608,7 +608,7 @@ importers: version: 15.8.3(@oramacloud/client@2.1.4)(@tanstack/react-router@1.131.27(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(algoliasearch@5.36.0)(lucide-react@0.542.0(react@19.2.0))(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(tailwindcss@4.1.13) geist: specifier: ^1.4.2 - version: 1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)) + version: 1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)) gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -839,12 +839,18 @@ importers: e2e/smoke/test/fixtures/tsconfig-decelration: dependencies: + '@better-auth/sso': + specifier: workspace:* + version: link:../../../../../packages/sso + '@better-auth/stripe': + specifier: workspace:* + version: link:../../../../../packages/stripe better-auth: specifier: workspace:* version: link:../../../../../packages/better-auth - better-sqlite3: - specifier: ^12.4.1 - version: 12.4.1 + stripe: + specifier: ^18.5.0 + version: 18.5.0(@types/node@24.7.2) e2e/smoke/test/fixtures/tsconfig-exact-optional-property-types: dependencies: @@ -17523,7 +17529,9 @@ snapshots: metro-runtime: 0.83.3 transitivePeerDependencies: - '@babel/core' + - bufferutil - supports-color + - utf-8-validate optional: true '@react-native/normalize-colors@0.74.89': {} @@ -21641,7 +21649,7 @@ snapshots: function-bind@1.1.2: {} - geist@1.4.2(next@16.0.0-beta.0(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)): + geist@1.4.2(next@16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)): dependencies: next: 16.0.0-beta.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.90.0)