@better-auth/oauth-provider 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.
@@ -1,4 +1,4 @@
1
- import { a as ResourceServerMetadata } from "./oauth-7Jc-EFsq.mjs";
1
+ import { a as ResourceServerMetadata } from "./oauth-4vgZlF-I.mjs";
2
2
  import { JWTPayload, JWTVerifyOptions } from "jose";
3
3
  import { Auth } from "better-auth/types";
4
4
 
@@ -1,4 +1,4 @@
1
- import { a as getJwtPlugin, m as handleMcpErrors, o as getOAuthProviderPlugin } from "./utils-D6kv_BUA.mjs";
1
+ import { a as getJwtPlugin, g as handleMcpErrors, o as getOAuthProviderPlugin } from "./utils-BDSjyzic.mjs";
2
2
  import { verifyAccessToken } from "better-auth/oauth2";
3
3
  import { APIError } from "better-call";
4
4
  import { logger } from "@better-auth/core/env";
@@ -16,10 +16,10 @@ const oauthProviderResourceClient = (auth) => {
16
16
  if (!jwtPlugin) jwtPlugin = auth && !(await getOauthProviderPlugin())?.options?.disableJwtPlugin ? getJwtPlugin(await auth.$context) : void 0;
17
17
  return jwtPlugin?.options;
18
18
  };
19
+ const authServerBaseUrl = typeof auth?.options.baseURL === "string" ? auth.options.baseURL : void 0;
19
20
  const getAuthorizationServer = async () => {
20
21
  return (await getJwtPluginOptions())?.jwt?.issuer ?? authServerBaseUrl;
21
22
  };
22
- const authServerBaseUrl = auth?.options.baseURL;
23
23
  const authServerBasePath = auth?.options.basePath;
24
24
  return {
25
25
  id: "oauth-provider-resource-client",
@@ -1 +1 @@
1
- {"version":3,"file":"client-resource.mjs","names":[],"sources":["../src/client-resource.ts"],"sourcesContent":["import { logger } from \"@better-auth/core/env\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport { verifyAccessToken } from \"better-auth/oauth2\";\nimport type { Auth, BetterAuthClientPlugin } from \"better-auth/types\";\nimport { APIError } from \"better-call\";\nimport type { JWTPayload, JWTVerifyOptions } from \"jose\";\nimport { handleMcpErrors } from \"./mcp\";\nimport type { ResourceServerMetadata } from \"./types/oauth\";\nimport { getJwtPlugin, getOAuthProviderPlugin } from \"./utils\";\n\nexport const oauthProviderResourceClient = <T extends Auth | undefined>(\n\tauth?: T,\n) => {\n\tlet oauthProviderPlugin:\n\t\t| ReturnType<typeof getOAuthProviderPlugin>\n\t\t| undefined;\n\tconst getOauthProviderPlugin = async () => {\n\t\tif (!oauthProviderPlugin) {\n\t\t\toauthProviderPlugin = auth\n\t\t\t\t? getOAuthProviderPlugin(await auth.$context)\n\t\t\t\t: undefined;\n\t\t}\n\t\treturn oauthProviderPlugin;\n\t};\n\tlet jwtPlugin: ReturnType<typeof getJwtPlugin> | undefined;\n\tconst getJwtPluginOptions = async () => {\n\t\tif (!jwtPlugin) {\n\t\t\tjwtPlugin =\n\t\t\t\tauth && !(await getOauthProviderPlugin())?.options?.disableJwtPlugin\n\t\t\t\t\t? getJwtPlugin(await auth.$context)\n\t\t\t\t\t: undefined;\n\t\t}\n\t\treturn jwtPlugin?.options;\n\t};\n\tconst getAuthorizationServer = async () => {\n\t\tconst jwtPluginOptions = await getJwtPluginOptions();\n\t\treturn jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;\n\t};\n\tconst authServerBaseUrl = auth?.options.baseURL;\n\tconst authServerBasePath = auth?.options.basePath;\n\n\treturn {\n\t\tid: \"oauth-provider-resource-client\",\n\t\tgetActions() {\n\t\t\treturn {\n\t\t\t\t/**\n\t\t\t\t * Performs verification of an access token for your APIs. Can perform\n\t\t\t\t * local verification using `jwksUrl` by default. Can also be configured\n\t\t\t\t * for remote introspection using `remoteVerify` if a confidential client\n\t\t\t\t * is set up for this API.\n\t\t\t\t *\n\t\t\t\t * The optional auth parameter can fill known values automatically.\n\t\t\t\t */\n\t\t\t\tverifyAccessToken: (async (\n\t\t\t\t\ttoken: string | undefined,\n\t\t\t\t\topts?: {\n\t\t\t\t\t\tverifyOptions?: JWTVerifyOptions &\n\t\t\t\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\t\t\t\tscopes?: string[];\n\t\t\t\t\t\tjwksUrl?: string;\n\t\t\t\t\t\tremoteVerify?: VerifyAccessTokenRemote;\n\t\t\t\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\t\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t\t\t\t\t},\n\t\t\t\t): Promise<JWTPayload> => {\n\t\t\t\t\tconst jwtPluginOptions = await getJwtPluginOptions();\n\t\t\t\t\tconst audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;\n\t\t\t\t\tconst issuer =\n\t\t\t\t\t\topts?.verifyOptions?.issuer ??\n\t\t\t\t\t\tjwtPluginOptions?.jwt?.issuer ??\n\t\t\t\t\t\tauthServerBaseUrl;\n\t\t\t\t\tif (!audience) {\n\t\t\t\t\t\tthrow Error(\"please define opts.verifyOptions.audience\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!issuer) {\n\t\t\t\t\t\tthrow Error(\"please define opts.verifyOptions.issuer\");\n\t\t\t\t\t}\n\t\t\t\t\tconst jwksUrl =\n\t\t\t\t\t\topts?.jwksUrl ??\n\t\t\t\t\t\tjwtPluginOptions?.jwks?.remoteUrl ??\n\t\t\t\t\t\t(authServerBaseUrl\n\t\t\t\t\t\t\t? `${authServerBaseUrl + (authServerBasePath ?? \"\")}${jwtPluginOptions?.jwks?.jwksPath ?? \"/jwks\"}`\n\t\t\t\t\t\t\t: undefined);\n\t\t\t\t\tconst introspectUrl =\n\t\t\t\t\t\topts?.remoteVerify?.introspectUrl ??\n\t\t\t\t\t\t(authServerBaseUrl\n\t\t\t\t\t\t\t? `${authServerBaseUrl}${authServerBasePath ?? \"\"}/oauth2/introspect`\n\t\t\t\t\t\t\t: undefined);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!token?.length) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\tmessage: \"missing authorization header\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn await verifyAccessToken(token, {\n\t\t\t\t\t\t\t...opts,\n\t\t\t\t\t\t\tjwksUrl,\n\t\t\t\t\t\t\tverifyOptions: {\n\t\t\t\t\t\t\t\t...opts?.verifyOptions,\n\t\t\t\t\t\t\t\taudience,\n\t\t\t\t\t\t\t\tissuer,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tremoteVerify:\n\t\t\t\t\t\t\t\topts?.remoteVerify && introspectUrl\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t...opts.remoteVerify,\n\t\t\t\t\t\t\t\t\t\t\tintrospectUrl,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow handleMcpErrors(error, audience, {\n\t\t\t\t\t\t\tresourceMetadataMappings: opts?.resourceMetadataMappings,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}) as VerifyAccessTokenOutput<T>,\n\t\t\t\t/**\n\t\t\t\t * An authorization server does not typically publish\n\t\t\t\t * the `/.well-known/oauth-protected-resource` themselves.\n\t\t\t\t * Thus, we provide a client-only endpoint to help set up\n\t\t\t\t * your protected resource metadata.\n\t\t\t\t *\n\t\t\t\t * The optional auth parameter can fill known values automatically.\n\t\t\t\t *\n\t\t\t\t * @see https://datatracker.ietf.org/doc/html/rfc8414#section-2\n\t\t\t\t */\n\t\t\t\tgetProtectedResourceMetadata: (async (\n\t\t\t\t\toverrides: Partial<ResourceServerMetadata> | undefined,\n\t\t\t\t\topts:\n\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\texternalScopes?: string[];\n\t\t\t\t\t\t }\n\t\t\t\t\t\t| undefined,\n\t\t\t\t): Promise<ResourceServerMetadata> => {\n\t\t\t\t\tconst resource = overrides?.resource ?? authServerBaseUrl;\n\t\t\t\t\tconst oauthProviderOptions = (await getOauthProviderPlugin())\n\t\t\t\t\t\t?.options;\n\t\t\t\t\tif (!resource) {\n\t\t\t\t\t\tthrow Error(\"missing required resource\");\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\toauthProviderOptions?.scopes &&\n\t\t\t\t\t\topts?.externalScopes &&\n\t\t\t\t\t\t(overrides?.authorization_servers?.length ?? 0) <= 1\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\"external scopes should not be provided with one authorization server\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Resource server should not mention specific scopes\n\t\t\t\t\tif (overrides?.scopes_supported) {\n\t\t\t\t\t\tconst allValidScopes = new Set([\n\t\t\t\t\t\t\t...(oauthProviderOptions?.scopes ?? []),\n\t\t\t\t\t\t\t...(opts?.externalScopes ?? []),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tfor (const sc of overrides.scopes_supported) {\n\t\t\t\t\t\t\tif (sc === \"openid\") {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t\"Only the Auth Server should utilize the openid scope\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ([\"profile\", \"email\", \"phone\", \"address\"].includes(sc)) {\n\t\t\t\t\t\t\t\tif (!opts?.silenceWarnings?.oidcScopes) {\n\t\t\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t\t\t`\"${sc}\" is typically restricted for the authorization server, a resource server typically shouldn't handle this scope`,\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\tif (!allValidScopes.has(sc)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`Unsupported scope ${sc}. If external, please add to \"externalScopes\"`,\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\n\t\t\t\t\tconst authorizationServer = await getAuthorizationServer();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tresource,\n\t\t\t\t\t\tauthorization_servers: authorizationServer\n\t\t\t\t\t\t\t? [authorizationServer]\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t...overrides,\n\t\t\t\t\t};\n\t\t\t\t}) as ProtectedResourceMetadataOutput<T>,\n\t\t\t};\n\t\t},\n\t} satisfies BetterAuthClientPlugin;\n};\n\nexport interface VerifyAccessTokenRemote {\n\t/** Full url of the introspect endpoint. Should end with `/oauth2/introspect` */\n\tintrospectUrl: string;\n\t/** Client Secret */\n\tclientId: string;\n\t/** Client Secret */\n\tclientSecret: string;\n\t/**\n\t * Forces remote verification of a token.\n\t * This ensures attached session (if applicable)\n\t * is also still active.\n\t */\n\tforce?: boolean;\n}\n\ntype VerifyAccessTokenOutput<T> = T extends Auth\n\t? (\n\t\t\ttoken: string | undefined,\n\t\t\topts?: VerifyAccessTokenAuthOpts,\n\t\t) => Promise<JWTPayload>\n\t: (\n\t\t\ttoken: string | undefined,\n\t\t\topts: VerifyAccessTokenNoAuthOpts,\n\t\t) => Promise<JWTPayload>;\ntype VerifyAccessTokenAuthOpts = {\n\tverifyOptions?: JWTVerifyOptions &\n\t\tRequired<Pick<JWTVerifyOptions, \"audience\">>;\n\tscopes?: string[];\n\tjwksUrl?: string;\n\tremoteVerify?: VerifyAccessTokenRemote;\n\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\tresourceMetadataMappings?: Record<string, string>;\n};\ntype VerifyAccessTokenNoAuthOpts =\n\t| {\n\t\t\tverifyOptions: JWTVerifyOptions &\n\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\tscopes?: string[];\n\t\t\tjwksUrl: string;\n\t\t\tremoteVerify?: VerifyAccessTokenRemote;\n\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t }\n\t| {\n\t\t\tverifyOptions: JWTVerifyOptions &\n\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\tscopes?: string[];\n\t\t\tjwksUrl?: string;\n\t\t\tremoteVerify: VerifyAccessTokenRemote;\n\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t };\n\ntype ProtectedResourceMetadataOutput<T> = T extends Auth\n\t? (\n\t\t\toverrides?: Partial<ResourceServerMetadata>,\n\t\t\topts?: {\n\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t};\n\t\t\t\texternalScopes?: string[];\n\t\t\t},\n\t\t) => Promise<ResourceServerMetadata>\n\t: (\n\t\t\toverrides: ResourceServerMetadata,\n\t\t\topts?: {\n\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t};\n\t\t\t\texternalScopes?: string[];\n\t\t\t},\n\t\t) => Promise<ResourceServerMetadata>;\n"],"mappings":";;;;;;;AAUA,MAAa,+BACZ,SACI;CACJ,IAAI;CAGJ,MAAM,yBAAyB,YAAY;AAC1C,MAAI,CAAC,oBACJ,uBAAsB,OACnB,uBAAuB,MAAM,KAAK,SAAS,GAC3C;AAEJ,SAAO;;CAER,IAAI;CACJ,MAAM,sBAAsB,YAAY;AACvC,MAAI,CAAC,UACJ,aACC,QAAQ,EAAE,MAAM,wBAAwB,GAAG,SAAS,mBACjD,aAAa,MAAM,KAAK,SAAS,GACjC;AAEL,SAAO,WAAW;;CAEnB,MAAM,yBAAyB,YAAY;AAE1C,UADyB,MAAM,qBAAqB,GAC3B,KAAK,UAAU;;CAEzC,MAAM,oBAAoB,MAAM,QAAQ;CACxC,MAAM,qBAAqB,MAAM,QAAQ;AAEzC,QAAO;EACN,IAAI;EACJ,aAAa;AACZ,UAAO;IASN,oBAAoB,OACnB,OACA,SASyB;KACzB,MAAM,mBAAmB,MAAM,qBAAqB;KACpD,MAAM,WAAW,MAAM,eAAe,YAAY;KAClD,MAAM,SACL,MAAM,eAAe,UACrB,kBAAkB,KAAK,UACvB;AACD,SAAI,CAAC,SACJ,OAAM,MAAM,4CAA4C;AAEzD,SAAI,CAAC,OACJ,OAAM,MAAM,0CAA0C;KAEvD,MAAM,UACL,MAAM,WACN,kBAAkB,MAAM,cACvB,oBACE,GAAG,qBAAqB,sBAAsB,MAAM,kBAAkB,MAAM,YAAY,YACxF;KACJ,MAAM,gBACL,MAAM,cAAc,kBACnB,oBACE,GAAG,oBAAoB,sBAAsB,GAAG,sBAChD;AAEJ,SAAI;AACH,UAAI,CAAC,OAAO,OACX,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,gCACT,CAAC;AAEH,aAAO,MAAM,kBAAkB,OAAO;OACrC,GAAG;OACH;OACA,eAAe;QACd,GAAG,MAAM;QACT;QACA;QACA;OACD,cACC,MAAM,gBAAgB,gBACnB;QACA,GAAG,KAAK;QACR;QACA,GACA;OACJ,CAAC;cACM,OAAO;AACf,YAAM,gBAAgB,OAAO,UAAU,EACtC,0BAA0B,MAAM,0BAChC,CAAC;;;IAaJ,+BAA+B,OAC9B,WACA,SAQqC;KACrC,MAAM,WAAW,WAAW,YAAY;KACxC,MAAM,wBAAwB,MAAM,wBAAwB,GACzD;AACH,SAAI,CAAC,SACJ,OAAM,MAAM,4BAA4B;AAEzC,SACC,sBAAsB,UACtB,MAAM,mBACL,WAAW,uBAAuB,UAAU,MAAM,EAEnD,OAAM,IAAI,gBACT,uEACA;AAGF,SAAI,WAAW,kBAAkB;MAChC,MAAM,iBAAiB,IAAI,IAAI,CAC9B,GAAI,sBAAsB,UAAU,EAAE,EACtC,GAAI,MAAM,kBAAkB,EAAE,CAC9B,CAAC;AACF,WAAK,MAAM,MAAM,UAAU,kBAAkB;AAC5C,WAAI,OAAO,SACV,OAAM,IAAI,gBACT,uDACA;AAEF,WAAI;QAAC;QAAW;QAAS;QAAS;QAAU,CAAC,SAAS,GAAG,EACxD;YAAI,CAAC,MAAM,iBAAiB,WAC3B,QAAO,KACN,IAAI,GAAG,iHACP;;AAGH,WAAI,CAAC,eAAe,IAAI,GAAG,CAC1B,OAAM,IAAI,gBACT,qBAAqB,GAAG,+CACxB;;;KAKJ,MAAM,sBAAsB,MAAM,wBAAwB;AAE1D,YAAO;MACN;MACA,uBAAuB,sBACpB,CAAC,oBAAoB,GACrB;MACH,GAAG;MACH;;IAEF;;EAEF"}
1
+ {"version":3,"file":"client-resource.mjs","names":[],"sources":["../src/client-resource.ts"],"sourcesContent":["import { logger } from \"@better-auth/core/env\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport { verifyAccessToken } from \"better-auth/oauth2\";\nimport type { Auth, BetterAuthClientPlugin } from \"better-auth/types\";\nimport { APIError } from \"better-call\";\nimport type { JWTPayload, JWTVerifyOptions } from \"jose\";\nimport { handleMcpErrors } from \"./mcp\";\nimport type { ResourceServerMetadata } from \"./types/oauth\";\nimport { getJwtPlugin, getOAuthProviderPlugin } from \"./utils\";\n\nexport const oauthProviderResourceClient = <T extends Auth | undefined>(\n\tauth?: T,\n) => {\n\tlet oauthProviderPlugin:\n\t\t| ReturnType<typeof getOAuthProviderPlugin>\n\t\t| undefined;\n\tconst getOauthProviderPlugin = async () => {\n\t\tif (!oauthProviderPlugin) {\n\t\t\toauthProviderPlugin = auth\n\t\t\t\t? getOAuthProviderPlugin(await auth.$context)\n\t\t\t\t: undefined;\n\t\t}\n\t\treturn oauthProviderPlugin;\n\t};\n\tlet jwtPlugin: ReturnType<typeof getJwtPlugin> | undefined;\n\tconst getJwtPluginOptions = async () => {\n\t\tif (!jwtPlugin) {\n\t\t\tjwtPlugin =\n\t\t\t\tauth && !(await getOauthProviderPlugin())?.options?.disableJwtPlugin\n\t\t\t\t\t? getJwtPlugin(await auth.$context)\n\t\t\t\t\t: undefined;\n\t\t}\n\t\treturn jwtPlugin?.options;\n\t};\n\tconst authServerBaseUrl =\n\t\ttypeof auth?.options.baseURL === \"string\"\n\t\t\t? auth.options.baseURL\n\t\t\t: undefined;\n\tconst getAuthorizationServer = async (): Promise<string | undefined> => {\n\t\tconst jwtPluginOptions = await getJwtPluginOptions();\n\t\treturn jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;\n\t};\n\tconst authServerBasePath = auth?.options.basePath;\n\n\treturn {\n\t\tid: \"oauth-provider-resource-client\",\n\t\tgetActions() {\n\t\t\treturn {\n\t\t\t\t/**\n\t\t\t\t * Performs verification of an access token for your APIs. Can perform\n\t\t\t\t * local verification using `jwksUrl` by default. Can also be configured\n\t\t\t\t * for remote introspection using `remoteVerify` if a confidential client\n\t\t\t\t * is set up for this API.\n\t\t\t\t *\n\t\t\t\t * The optional auth parameter can fill known values automatically.\n\t\t\t\t */\n\t\t\t\tverifyAccessToken: (async (\n\t\t\t\t\ttoken: string | undefined,\n\t\t\t\t\topts?: {\n\t\t\t\t\t\tverifyOptions?: JWTVerifyOptions &\n\t\t\t\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\t\t\t\tscopes?: string[];\n\t\t\t\t\t\tjwksUrl?: string;\n\t\t\t\t\t\tremoteVerify?: VerifyAccessTokenRemote;\n\t\t\t\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\t\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t\t\t\t\t},\n\t\t\t\t): Promise<JWTPayload> => {\n\t\t\t\t\tconst jwtPluginOptions = await getJwtPluginOptions();\n\t\t\t\t\tconst audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;\n\t\t\t\t\tconst issuer =\n\t\t\t\t\t\topts?.verifyOptions?.issuer ??\n\t\t\t\t\t\tjwtPluginOptions?.jwt?.issuer ??\n\t\t\t\t\t\tauthServerBaseUrl;\n\t\t\t\t\tif (!audience) {\n\t\t\t\t\t\tthrow Error(\"please define opts.verifyOptions.audience\");\n\t\t\t\t\t}\n\t\t\t\t\tif (!issuer) {\n\t\t\t\t\t\tthrow Error(\"please define opts.verifyOptions.issuer\");\n\t\t\t\t\t}\n\t\t\t\t\tconst jwksUrl =\n\t\t\t\t\t\topts?.jwksUrl ??\n\t\t\t\t\t\tjwtPluginOptions?.jwks?.remoteUrl ??\n\t\t\t\t\t\t(authServerBaseUrl\n\t\t\t\t\t\t\t? `${authServerBaseUrl + (authServerBasePath ?? \"\")}${jwtPluginOptions?.jwks?.jwksPath ?? \"/jwks\"}`\n\t\t\t\t\t\t\t: undefined);\n\t\t\t\t\tconst introspectUrl =\n\t\t\t\t\t\topts?.remoteVerify?.introspectUrl ??\n\t\t\t\t\t\t(authServerBaseUrl\n\t\t\t\t\t\t\t? `${authServerBaseUrl}${authServerBasePath ?? \"\"}/oauth2/introspect`\n\t\t\t\t\t\t\t: undefined);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!token?.length) {\n\t\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\t\t\t\t\tmessage: \"missing authorization header\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn await verifyAccessToken(token, {\n\t\t\t\t\t\t\t...opts,\n\t\t\t\t\t\t\tjwksUrl,\n\t\t\t\t\t\t\tverifyOptions: {\n\t\t\t\t\t\t\t\t...opts?.verifyOptions,\n\t\t\t\t\t\t\t\taudience,\n\t\t\t\t\t\t\t\tissuer,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tremoteVerify:\n\t\t\t\t\t\t\t\topts?.remoteVerify && introspectUrl\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t...opts.remoteVerify,\n\t\t\t\t\t\t\t\t\t\t\tintrospectUrl,\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow handleMcpErrors(error, audience, {\n\t\t\t\t\t\t\tresourceMetadataMappings: opts?.resourceMetadataMappings,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}) as VerifyAccessTokenOutput<T>,\n\t\t\t\t/**\n\t\t\t\t * An authorization server does not typically publish\n\t\t\t\t * the `/.well-known/oauth-protected-resource` themselves.\n\t\t\t\t * Thus, we provide a client-only endpoint to help set up\n\t\t\t\t * your protected resource metadata.\n\t\t\t\t *\n\t\t\t\t * The optional auth parameter can fill known values automatically.\n\t\t\t\t *\n\t\t\t\t * @see https://datatracker.ietf.org/doc/html/rfc8414#section-2\n\t\t\t\t */\n\t\t\t\tgetProtectedResourceMetadata: (async (\n\t\t\t\t\toverrides: Partial<ResourceServerMetadata> | undefined,\n\t\t\t\t\topts:\n\t\t\t\t\t\t| {\n\t\t\t\t\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\texternalScopes?: string[];\n\t\t\t\t\t\t }\n\t\t\t\t\t\t| undefined,\n\t\t\t\t): Promise<ResourceServerMetadata> => {\n\t\t\t\t\tconst resource = overrides?.resource ?? authServerBaseUrl;\n\t\t\t\t\tconst oauthProviderOptions = (await getOauthProviderPlugin())\n\t\t\t\t\t\t?.options;\n\t\t\t\t\tif (!resource) {\n\t\t\t\t\t\tthrow Error(\"missing required resource\");\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\toauthProviderOptions?.scopes &&\n\t\t\t\t\t\topts?.externalScopes &&\n\t\t\t\t\t\t(overrides?.authorization_servers?.length ?? 0) <= 1\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\"external scopes should not be provided with one authorization server\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\t// Resource server should not mention specific scopes\n\t\t\t\t\tif (overrides?.scopes_supported) {\n\t\t\t\t\t\tconst allValidScopes = new Set([\n\t\t\t\t\t\t\t...(oauthProviderOptions?.scopes ?? []),\n\t\t\t\t\t\t\t...(opts?.externalScopes ?? []),\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tfor (const sc of overrides.scopes_supported) {\n\t\t\t\t\t\t\tif (sc === \"openid\") {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t\"Only the Auth Server should utilize the openid scope\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif ([\"profile\", \"email\", \"phone\", \"address\"].includes(sc)) {\n\t\t\t\t\t\t\t\tif (!opts?.silenceWarnings?.oidcScopes) {\n\t\t\t\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t\t\t\t`\"${sc}\" is typically restricted for the authorization server, a resource server typically shouldn't handle this scope`,\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\tif (!allValidScopes.has(sc)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`Unsupported scope ${sc}. If external, please add to \"externalScopes\"`,\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\n\t\t\t\t\tconst authorizationServer = await getAuthorizationServer();\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tresource,\n\t\t\t\t\t\tauthorization_servers: authorizationServer\n\t\t\t\t\t\t\t? [authorizationServer]\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t...overrides,\n\t\t\t\t\t};\n\t\t\t\t}) as ProtectedResourceMetadataOutput<T>,\n\t\t\t};\n\t\t},\n\t} satisfies BetterAuthClientPlugin;\n};\n\nexport interface VerifyAccessTokenRemote {\n\t/** Full url of the introspect endpoint. Should end with `/oauth2/introspect` */\n\tintrospectUrl: string;\n\t/** Client Secret */\n\tclientId: string;\n\t/** Client Secret */\n\tclientSecret: string;\n\t/**\n\t * Forces remote verification of a token.\n\t * This ensures attached session (if applicable)\n\t * is also still active.\n\t */\n\tforce?: boolean;\n}\n\ntype VerifyAccessTokenOutput<T> = T extends Auth\n\t? (\n\t\t\ttoken: string | undefined,\n\t\t\topts?: VerifyAccessTokenAuthOpts,\n\t\t) => Promise<JWTPayload>\n\t: (\n\t\t\ttoken: string | undefined,\n\t\t\topts: VerifyAccessTokenNoAuthOpts,\n\t\t) => Promise<JWTPayload>;\ntype VerifyAccessTokenAuthOpts = {\n\tverifyOptions?: JWTVerifyOptions &\n\t\tRequired<Pick<JWTVerifyOptions, \"audience\">>;\n\tscopes?: string[];\n\tjwksUrl?: string;\n\tremoteVerify?: VerifyAccessTokenRemote;\n\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\tresourceMetadataMappings?: Record<string, string>;\n};\ntype VerifyAccessTokenNoAuthOpts =\n\t| {\n\t\t\tverifyOptions: JWTVerifyOptions &\n\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\tscopes?: string[];\n\t\t\tjwksUrl: string;\n\t\t\tremoteVerify?: VerifyAccessTokenRemote;\n\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t }\n\t| {\n\t\t\tverifyOptions: JWTVerifyOptions &\n\t\t\t\tRequired<Pick<JWTVerifyOptions, \"audience\" | \"issuer\">>;\n\t\t\tscopes?: string[];\n\t\t\tjwksUrl?: string;\n\t\t\tremoteVerify: VerifyAccessTokenRemote;\n\t\t\t/** Maps non-url (ie urn, client) resources to resource_metadata */\n\t\t\tresourceMetadataMappings?: Record<string, string>;\n\t };\n\ntype ProtectedResourceMetadataOutput<T> = T extends Auth\n\t? (\n\t\t\toverrides?: Partial<ResourceServerMetadata>,\n\t\t\topts?: {\n\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t};\n\t\t\t\texternalScopes?: string[];\n\t\t\t},\n\t\t) => Promise<ResourceServerMetadata>\n\t: (\n\t\t\toverrides: ResourceServerMetadata,\n\t\t\topts?: {\n\t\t\t\tsilenceWarnings?: {\n\t\t\t\t\toidcScopes?: boolean;\n\t\t\t\t};\n\t\t\t\texternalScopes?: string[];\n\t\t\t},\n\t\t) => Promise<ResourceServerMetadata>;\n"],"mappings":";;;;;;;AAUA,MAAa,+BACZ,SACI;CACJ,IAAI;CAGJ,MAAM,yBAAyB,YAAY;AAC1C,MAAI,CAAC,oBACJ,uBAAsB,OACnB,uBAAuB,MAAM,KAAK,SAAS,GAC3C;AAEJ,SAAO;;CAER,IAAI;CACJ,MAAM,sBAAsB,YAAY;AACvC,MAAI,CAAC,UACJ,aACC,QAAQ,EAAE,MAAM,wBAAwB,GAAG,SAAS,mBACjD,aAAa,MAAM,KAAK,SAAS,GACjC;AAEL,SAAO,WAAW;;CAEnB,MAAM,oBACL,OAAO,MAAM,QAAQ,YAAY,WAC9B,KAAK,QAAQ,UACb;CACJ,MAAM,yBAAyB,YAAyC;AAEvE,UADyB,MAAM,qBAAqB,GAC3B,KAAK,UAAU;;CAEzC,MAAM,qBAAqB,MAAM,QAAQ;AAEzC,QAAO;EACN,IAAI;EACJ,aAAa;AACZ,UAAO;IASN,oBAAoB,OACnB,OACA,SASyB;KACzB,MAAM,mBAAmB,MAAM,qBAAqB;KACpD,MAAM,WAAW,MAAM,eAAe,YAAY;KAClD,MAAM,SACL,MAAM,eAAe,UACrB,kBAAkB,KAAK,UACvB;AACD,SAAI,CAAC,SACJ,OAAM,MAAM,4CAA4C;AAEzD,SAAI,CAAC,OACJ,OAAM,MAAM,0CAA0C;KAEvD,MAAM,UACL,MAAM,WACN,kBAAkB,MAAM,cACvB,oBACE,GAAG,qBAAqB,sBAAsB,MAAM,kBAAkB,MAAM,YAAY,YACxF;KACJ,MAAM,gBACL,MAAM,cAAc,kBACnB,oBACE,GAAG,oBAAoB,sBAAsB,GAAG,sBAChD;AAEJ,SAAI;AACH,UAAI,CAAC,OAAO,OACX,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,gCACT,CAAC;AAEH,aAAO,MAAM,kBAAkB,OAAO;OACrC,GAAG;OACH;OACA,eAAe;QACd,GAAG,MAAM;QACT;QACA;QACA;OACD,cACC,MAAM,gBAAgB,gBACnB;QACA,GAAG,KAAK;QACR;QACA,GACA;OACJ,CAAC;cACM,OAAO;AACf,YAAM,gBAAgB,OAAO,UAAU,EACtC,0BAA0B,MAAM,0BAChC,CAAC;;;IAaJ,+BAA+B,OAC9B,WACA,SAQqC;KACrC,MAAM,WAAW,WAAW,YAAY;KACxC,MAAM,wBAAwB,MAAM,wBAAwB,GACzD;AACH,SAAI,CAAC,SACJ,OAAM,MAAM,4BAA4B;AAEzC,SACC,sBAAsB,UACtB,MAAM,mBACL,WAAW,uBAAuB,UAAU,MAAM,EAEnD,OAAM,IAAI,gBACT,uEACA;AAGF,SAAI,WAAW,kBAAkB;MAChC,MAAM,iBAAiB,IAAI,IAAI,CAC9B,GAAI,sBAAsB,UAAU,EAAE,EACtC,GAAI,MAAM,kBAAkB,EAAE,CAC9B,CAAC;AACF,WAAK,MAAM,MAAM,UAAU,kBAAkB;AAC5C,WAAI,OAAO,SACV,OAAM,IAAI,gBACT,uDACA;AAEF,WAAI;QAAC;QAAW;QAAS;QAAS;QAAU,CAAC,SAAS,GAAG,EACxD;YAAI,CAAC,MAAM,iBAAiB,WAC3B,QAAO,KACN,IAAI,GAAG,iHACP;;AAGH,WAAI,CAAC,eAAe,IAAI,GAAG,CAC1B,OAAM,IAAI,gBACT,qBAAqB,GAAG,+CACxB;;;KAKJ,MAAM,sBAAsB,MAAM,wBAAwB;AAE1D,YAAO;MACN;MACA,uBAAuB,sBACpB,CAAC,oBAAoB,GACrB;MACH,GAAG;MACH;;IAEF;;EAEF"}
package/dist/client.d.mts CHANGED
@@ -1,5 +1,4 @@
1
- import "./oauth-7Jc-EFsq.mjs";
2
- import { n as oauthProvider } from "./oauth-C_QoLKZA.mjs";
1
+ import { n as oauthProvider } from "./oauth-E89Dh-ZC.mjs";
3
2
  import * as _better_fetch_fetch0 from "@better-fetch/fetch";
4
3
 
5
4
  //#region src/client.d.ts
package/dist/client.mjs CHANGED
@@ -23,10 +23,9 @@ const oauthProviderClient = () => {
23
23
  const headers = ctx.headers;
24
24
  const body = typeof ctx.body === "string" ? headers.get("content-type") === "application/x-www-form-urlencoded" ? Object.fromEntries(new URLSearchParams(ctx.body)) : safeJSONParse(ctx.body ?? "{}") : ctx.body;
25
25
  if (body?.oauth_query) return;
26
- const pathname = typeof ctx.url === "string" ? new URL(ctx.url).pathname : ctx.url.pathname;
27
- if (pathname.endsWith("/sign-in/email") || pathname.endsWith("/sign-in/social") || pathname.endsWith("/sign-in/oauth2") || pathname.endsWith("/oauth2/consent") || pathname.endsWith("/oauth2/continue")) ctx.body = JSON.stringify({
26
+ if (typeof window !== "undefined" && window?.location?.search && !(ctx.method === "GET" || ctx.method === "DELETE")) ctx.body = JSON.stringify({
28
27
  ...body,
29
- oauth_query: typeof window !== "undefined" ? parseSignedQuery(window?.location?.search) : void 0
28
+ oauth_query: parseSignedQuery(window.location.search)
30
29
  });
31
30
  } }
32
31
  }],
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { safeJSONParse } from \"@better-auth/core/utils/json\";\nimport type { BetterAuthClientPlugin } from \"better-auth/types\";\nimport type { oauthProvider } from \"./oauth\";\n\nfunction parseSignedQuery(search: string) {\n\tconst params = new URLSearchParams(search);\n\tif (params.has(\"sig\")) {\n\t\tconst signedParams = new URLSearchParams();\n\t\tfor (const [key, value] of params.entries()) {\n\t\t\tsignedParams.append(key, value);\n\t\t\tif (key === \"sig\") break;\n\t\t}\n\t\treturn signedParams.toString();\n\t}\n}\n\nexport const oauthProviderClient = () => {\n\treturn {\n\t\tid: \"oauth-provider-client\",\n\t\tfetchPlugins: [\n\t\t\t{\n\t\t\t\tid: \"oauth-provider-signin\",\n\t\t\t\tname: \"oauth-provider-signin\",\n\t\t\t\tdescription: \"Adds the current page query to oauth requests\",\n\t\t\t\thooks: {\n\t\t\t\t\tasync onRequest(ctx) {\n\t\t\t\t\t\tconst headers = ctx.headers;\n\t\t\t\t\t\tconst body =\n\t\t\t\t\t\t\ttypeof ctx.body === \"string\"\n\t\t\t\t\t\t\t\t? headers.get(\"content-type\") ===\n\t\t\t\t\t\t\t\t\t\"application/x-www-form-urlencoded\"\n\t\t\t\t\t\t\t\t\t? Object.fromEntries(new URLSearchParams(ctx.body))\n\t\t\t\t\t\t\t\t\t: safeJSONParse<Record<string, unknown>>(ctx.body ?? \"{}\")\n\t\t\t\t\t\t\t\t: ctx.body;\n\t\t\t\t\t\tif (body?.oauth_query) return;\n\t\t\t\t\t\tconst pathname =\n\t\t\t\t\t\t\ttypeof ctx.url === \"string\"\n\t\t\t\t\t\t\t\t? new URL(ctx.url).pathname\n\t\t\t\t\t\t\t\t: ctx.url.pathname;\n\t\t\t\t\t\t// Should only need to run for /sign-in/email, /sign-in/social, /sign-in/oauth2, /oauth2/consent, /oauth2/continue\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tpathname.endsWith(\"/sign-in/email\") ||\n\t\t\t\t\t\t\tpathname.endsWith(\"/sign-in/social\") ||\n\t\t\t\t\t\t\tpathname.endsWith(\"/sign-in/oauth2\") ||\n\t\t\t\t\t\t\tpathname.endsWith(\"/oauth2/consent\") ||\n\t\t\t\t\t\t\tpathname.endsWith(\"/oauth2/continue\")\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tctx.body = JSON.stringify({\n\t\t\t\t\t\t\t\t...body,\n\t\t\t\t\t\t\t\toauth_query:\n\t\t\t\t\t\t\t\t\ttypeof window !== \"undefined\"\n\t\t\t\t\t\t\t\t\t\t? parseSignedQuery(window?.location?.search)\n\t\t\t\t\t\t\t\t\t\t: undefined,\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$InferServerPlugin: {} as ReturnType<typeof oauthProvider>,\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;AAIA,SAAS,iBAAiB,QAAgB;CACzC,MAAM,SAAS,IAAI,gBAAgB,OAAO;AAC1C,KAAI,OAAO,IAAI,MAAM,EAAE;EACtB,MAAM,eAAe,IAAI,iBAAiB;AAC1C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,EAAE;AAC5C,gBAAa,OAAO,KAAK,MAAM;AAC/B,OAAI,QAAQ,MAAO;;AAEpB,SAAO,aAAa,UAAU;;;AAIhC,MAAa,4BAA4B;AACxC,QAAO;EACN,IAAI;EACJ,cAAc,CACb;GACC,IAAI;GACJ,MAAM;GACN,aAAa;GACb,OAAO,EACN,MAAM,UAAU,KAAK;IACpB,MAAM,UAAU,IAAI;IACpB,MAAM,OACL,OAAO,IAAI,SAAS,WACjB,QAAQ,IAAI,eAAe,KAC5B,sCACE,OAAO,YAAY,IAAI,gBAAgB,IAAI,KAAK,CAAC,GACjD,cAAuC,IAAI,QAAQ,KAAK,GACzD,IAAI;AACR,QAAI,MAAM,YAAa;IACvB,MAAM,WACL,OAAO,IAAI,QAAQ,WAChB,IAAI,IAAI,IAAI,IAAI,CAAC,WACjB,IAAI,IAAI;AAEZ,QACC,SAAS,SAAS,iBAAiB,IACnC,SAAS,SAAS,kBAAkB,IACpC,SAAS,SAAS,kBAAkB,IACpC,SAAS,SAAS,kBAAkB,IACpC,SAAS,SAAS,mBAAmB,CAErC,KAAI,OAAO,KAAK,UAAU;KACzB,GAAG;KACH,aACC,OAAO,WAAW,cACf,iBAAiB,QAAQ,UAAU,OAAO,GAC1C;KACJ,CAAC;MAGJ;GACD,CACD;EACD,oBAAoB,EAAE;EACtB"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { safeJSONParse } from \"@better-auth/core/utils/json\";\nimport type { BetterAuthClientPlugin } from \"better-auth/types\";\nimport type { oauthProvider } from \"./oauth\";\n\nfunction parseSignedQuery(search: string) {\n\tconst params = new URLSearchParams(search);\n\tif (params.has(\"sig\")) {\n\t\tconst signedParams = new URLSearchParams();\n\t\tfor (const [key, value] of params.entries()) {\n\t\t\tsignedParams.append(key, value);\n\t\t\tif (key === \"sig\") break;\n\t\t}\n\t\treturn signedParams.toString();\n\t}\n}\n\nexport const oauthProviderClient = () => {\n\treturn {\n\t\tid: \"oauth-provider-client\",\n\t\tfetchPlugins: [\n\t\t\t{\n\t\t\t\tid: \"oauth-provider-signin\",\n\t\t\t\tname: \"oauth-provider-signin\",\n\t\t\t\tdescription: \"Adds the current page query to oauth requests\",\n\t\t\t\thooks: {\n\t\t\t\t\tasync onRequest(ctx) {\n\t\t\t\t\t\tconst headers = ctx.headers;\n\t\t\t\t\t\tconst body =\n\t\t\t\t\t\t\ttypeof ctx.body === \"string\"\n\t\t\t\t\t\t\t\t? headers.get(\"content-type\") ===\n\t\t\t\t\t\t\t\t\t\"application/x-www-form-urlencoded\"\n\t\t\t\t\t\t\t\t\t? Object.fromEntries(new URLSearchParams(ctx.body))\n\t\t\t\t\t\t\t\t\t: safeJSONParse<Record<string, unknown>>(ctx.body ?? \"{}\")\n\t\t\t\t\t\t\t\t: ctx.body;\n\t\t\t\t\t\tif (body?.oauth_query) return;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof window !== \"undefined\" &&\n\t\t\t\t\t\t\twindow?.location?.search &&\n\t\t\t\t\t\t\t!(ctx.method === \"GET\" || ctx.method === \"DELETE\")\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tctx.body = JSON.stringify({\n\t\t\t\t\t\t\t\t...body,\n\t\t\t\t\t\t\t\toauth_query: parseSignedQuery(window.location.search),\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$InferServerPlugin: {} as ReturnType<typeof oauthProvider>,\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;AAIA,SAAS,iBAAiB,QAAgB;CACzC,MAAM,SAAS,IAAI,gBAAgB,OAAO;AAC1C,KAAI,OAAO,IAAI,MAAM,EAAE;EACtB,MAAM,eAAe,IAAI,iBAAiB;AAC1C,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,SAAS,EAAE;AAC5C,gBAAa,OAAO,KAAK,MAAM;AAC/B,OAAI,QAAQ,MAAO;;AAEpB,SAAO,aAAa,UAAU;;;AAIhC,MAAa,4BAA4B;AACxC,QAAO;EACN,IAAI;EACJ,cAAc,CACb;GACC,IAAI;GACJ,MAAM;GACN,aAAa;GACb,OAAO,EACN,MAAM,UAAU,KAAK;IACpB,MAAM,UAAU,IAAI;IACpB,MAAM,OACL,OAAO,IAAI,SAAS,WACjB,QAAQ,IAAI,eAAe,KAC5B,sCACE,OAAO,YAAY,IAAI,gBAAgB,IAAI,KAAK,CAAC,GACjD,cAAuC,IAAI,QAAQ,KAAK,GACzD,IAAI;AACR,QAAI,MAAM,YAAa;AACvB,QACC,OAAO,WAAW,eAClB,QAAQ,UAAU,UAClB,EAAE,IAAI,WAAW,SAAS,IAAI,WAAW,UAEzC,KAAI,OAAO,KAAK,UAAU;KACzB,GAAG;KACH,aAAa,iBAAiB,OAAO,SAAS,OAAO;KACrD,CAAC;MAGJ;GACD,CACD;EACD,oBAAoB,EAAE;EACtB"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as Awaitable, a as ResourceServerMetadata, c as OAuthConsent, d as OAuthRefreshToken, f as Prompt, g as VerificationValue, h as StoreTokenType, i as OIDCMetadata, l as OAuthOpaqueAccessToken, m as Scope, n as GrantType, o as AuthorizePrompt, p as SchemaClient, r as OAuthClient, s as OAuthAuthorizationQuery, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-7Jc-EFsq.mjs";
2
- import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-C_QoLKZA.mjs";
1
+ import { _ as Awaitable, a as ResourceServerMetadata, c as OAuthConsent, d as OAuthRefreshToken, f as Prompt, g as VerificationValue, h as StoreTokenType, i as OIDCMetadata, l as OAuthOpaqueAccessToken, m as Scope, n as GrantType, o as AuthorizePrompt, p as SchemaClient, r as OAuthClient, s as OAuthAuthorizationQuery, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-4vgZlF-I.mjs";
2
+ import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-E89Dh-ZC.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import { a as getJwtPlugin, c as isPKCERequired, d as storeClientSecret, f as storeToken, h as mcpHandler, i as getClient, l as parseClientMetadata, n as decryptStoredClientSecret, p as validateClientCredentials, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as parsePrompt } from "./utils-D6kv_BUA.mjs";
1
+ import { _ as mcpHandler, a as getJwtPlugin, c as isPKCERequired, d as resolveSubjectIdentifier, f as storeClientSecret, h as verifyOAuthQueryParams, i as getClient, l as parseClientMetadata, m as validateClientCredentials, n as decryptStoredClientSecret, p as storeToken, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as parsePrompt } from "./utils-BDSjyzic.mjs";
2
2
  import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
3
3
  import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
4
4
  import { APIError as APIError$1 } from "better-call";
5
- import { constantTimeEqual, generateRandomString, makeSignature } from "better-auth/crypto";
5
+ import { isBrowserFetchRequest } from "@better-auth/core/utils/fetch-metadata";
6
+ import { generateRandomString, makeSignature } from "better-auth/crypto";
6
7
  import { defineRequestState } from "@better-auth/core/context";
7
8
  import { logger } from "@better-auth/core/env";
8
9
  import { BetterAuthError } from "@better-auth/core/error";
@@ -206,6 +207,13 @@ async function userInfoEndpoint(ctx, opts) {
206
207
  error: "invalid_request"
207
208
  });
208
209
  const baseUserClaims = userNormalClaims(user, scopes ?? []);
210
+ if (opts.pairwiseSecret) {
211
+ const clientId = jwt.client_id ?? jwt.azp;
212
+ if (clientId) {
213
+ const client = await getClient(ctx, opts, clientId);
214
+ if (client) baseUserClaims.sub = await resolveSubjectIdentifier(user.id, client, opts);
215
+ }
216
+ }
209
217
  const additionalInfoUserClaims = opts.customUserInfoClaims && scopes?.length ? await opts.customUserInfoClaims({
210
218
  user,
211
219
  scopes,
@@ -277,6 +285,7 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId,
277
285
  const iat = Math.floor(Date.now() / 1e3);
278
286
  const exp = iat + (opts.idTokenExpiresIn ?? 36e3);
279
287
  const userClaims = userNormalClaims(user, scopes);
288
+ const resolvedSub = await resolveSubjectIdentifier(user.id, client, opts);
280
289
  const authTimeSec = authTime != null ? Math.floor(authTime.getTime() / 1e3) : void 0;
281
290
  const acr = "urn:mace:incommon:iap:bronze";
282
291
  const customClaims = opts.customIdTokenClaims ? await opts.customIdTokenClaims({
@@ -291,7 +300,7 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId,
291
300
  auth_time: authTimeSec,
292
301
  acr,
293
302
  iss: jwtPluginOptions?.jwt?.issuer ?? ctx.context.baseURL,
294
- sub: user.id,
303
+ sub: resolvedSub,
295
304
  aud: client.clientId,
296
305
  nonce,
297
306
  iat,
@@ -908,6 +917,21 @@ async function validateAccessToken(ctx, opts, token, clientId) {
908
917
  error: "invalid_request"
909
918
  });
910
919
  }
920
+ /**
921
+ * Resolves pairwise sub on an introspection payload.
922
+ * Applied at the presentation layer so internal validation functions
923
+ * keep real user.id (needed for user lookup in /userinfo).
924
+ */
925
+ async function resolveIntrospectionSub(opts, payload, client) {
926
+ if (payload.active && payload.sub) {
927
+ const resolvedSub = await resolveSubjectIdentifier(payload.sub, client, opts);
928
+ return {
929
+ ...payload,
930
+ sub: resolvedSub
931
+ };
932
+ }
933
+ return payload;
934
+ }
911
935
  async function introspectEndpoint(ctx, opts) {
912
936
  let { client_id, client_secret, token, token_type_hint } = ctx.body;
913
937
  const authorization = ctx.request?.headers.get("authorization") || null;
@@ -928,7 +952,7 @@ async function introspectEndpoint(ctx, opts) {
928
952
  const client = await validateClientCredentials(ctx, opts, client_id, client_secret);
929
953
  try {
930
954
  if (token_type_hint === void 0 || token_type_hint === "access_token") try {
931
- return await validateAccessToken(ctx, opts, token, client.clientId);
955
+ return resolveIntrospectionSub(opts, await validateAccessToken(ctx, opts, token, client.clientId), client);
932
956
  } catch (error) {
933
957
  if (error instanceof APIError$1) {
934
958
  if (token_type_hint === "access_token") throw error;
@@ -936,7 +960,7 @@ async function introspectEndpoint(ctx, opts) {
936
960
  else throw new Error(error);
937
961
  }
938
962
  if (token_type_hint === void 0 || token_type_hint === "refresh_token") try {
939
- return await validateRefreshToken(ctx, opts, (await decodeRefreshToken(opts, token)).token, client.clientId);
963
+ return resolveIntrospectionSub(opts, await validateRefreshToken(ctx, opts, (await decodeRefreshToken(opts, token)).token, client.clientId), client);
940
964
  } catch (error) {
941
965
  if (error instanceof APIError$1) {
942
966
  if (token_type_hint === "refresh_token") throw error;
@@ -1073,6 +1097,14 @@ async function rpInitiatedLogoutEndpoint(ctx, opts) {
1073
1097
  }
1074
1098
  }
1075
1099
 
1100
+ //#endregion
1101
+ //#region src/middleware/index.ts
1102
+ const publicSessionMiddleware = (opts) => createAuthMiddleware(async (ctx) => {
1103
+ if (!opts.allowPublicClientPrelogin) throw new APIError("BAD_REQUEST");
1104
+ const query = ctx.body.oauth_query;
1105
+ if (!await verifyOAuthQueryParams(query, ctx.context.secret)) throw new APIError("UNAUTHORIZED", { error: "invalid_signature" });
1106
+ });
1107
+
1076
1108
  //#endregion
1077
1109
  //#region src/register.ts
1078
1110
  async function registerEndpoint(ctx, opts) {
@@ -1116,6 +1148,22 @@ async function checkOAuthClient(client, opts, settings) {
1116
1148
  error: "invalid_client_metadata",
1117
1149
  error_description: "When 'authorization_code' grant type is used, 'code' response type must be included"
1118
1150
  });
1151
+ if (client.subject_type !== void 0) {
1152
+ if (client.subject_type !== "public" && client.subject_type !== "pairwise") throw new APIError("BAD_REQUEST", {
1153
+ error: "invalid_client_metadata",
1154
+ error_description: `subject_type must be "public" or "pairwise"`
1155
+ });
1156
+ if (client.subject_type === "pairwise" && !opts.pairwiseSecret) throw new APIError("BAD_REQUEST", {
1157
+ error: "invalid_client_metadata",
1158
+ error_description: "pairwise subject_type requires server pairwiseSecret configuration"
1159
+ });
1160
+ if (client.subject_type === "pairwise" && client.redirect_uris && client.redirect_uris.length > 1) {
1161
+ if (new Set(client.redirect_uris.map((uri) => new URL(uri).host)).size > 1) throw new APIError("BAD_REQUEST", {
1162
+ error: "invalid_client_metadata",
1163
+ error_description: "pairwise clients with redirect_uris on different hosts require a sector_identifier_uri, which is not yet supported. All redirect_uris must share the same host."
1164
+ });
1165
+ }
1166
+ }
1119
1167
  const requestedScopes = (client?.scope)?.split(" ").filter((v) => v.length);
1120
1168
  const allowedScopes = settings?.isRegister ? opts.clientRegistrationAllowedScopes ?? opts.scopes : opts.scopes;
1121
1169
  if (allowedScopes) {
@@ -1182,7 +1230,7 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
1182
1230
  * @returns
1183
1231
  */
1184
1232
  function oauthToSchema(input) {
1185
- const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks: _jwks, jwks_uri: _jwksUri, software_id: softwareId, software_version: softwareVersion, software_statement: softwareStatement, redirect_uris: redirectUris, post_logout_redirect_uris: postLogoutRedirectUris, token_endpoint_auth_method: tokenEndpointAuthMethod, grant_types: grantTypes, response_types: responseTypes, public: _public, type, disabled, skip_consent: skipConsent, enable_end_session: enableEndSession, require_pkce: requirePKCE, reference_id: referenceId, metadata: inputMetadata, ...rest } = input;
1233
+ const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks: _jwks, jwks_uri: _jwksUri, software_id: softwareId, software_version: softwareVersion, software_statement: softwareStatement, redirect_uris: redirectUris, post_logout_redirect_uris: postLogoutRedirectUris, token_endpoint_auth_method: tokenEndpointAuthMethod, grant_types: grantTypes, response_types: responseTypes, public: _public, type, disabled, skip_consent: skipConsent, enable_end_session: enableEndSession, require_pkce: requirePKCE, subject_type: subjectType, reference_id: referenceId, metadata: inputMetadata, ...rest } = input;
1186
1234
  const expiresAt = _expiresAt ? /* @__PURE__ */ new Date(_expiresAt * 1e3) : void 0;
1187
1235
  const createdAt = _createdAt ? /* @__PURE__ */ new Date(_createdAt * 1e3) : void 0;
1188
1236
  const scopes = _scope?.split(" ");
@@ -1217,6 +1265,7 @@ function oauthToSchema(input) {
1217
1265
  skipConsent,
1218
1266
  enableEndSession,
1219
1267
  requirePKCE,
1268
+ subjectType,
1220
1269
  referenceId,
1221
1270
  metadata: Object.keys(metadataObj).length ? JSON.stringify(metadataObj) : void 0
1222
1271
  };
@@ -1228,7 +1277,7 @@ function oauthToSchema(input) {
1228
1277
  * @returns
1229
1278
  */
1230
1279
  function schemaToOAuth(input) {
1231
- const { clientId, clientSecret, disabled, scopes, userId, createdAt, updatedAt: _updatedAt, expiresAt, name, uri, icon, contacts, tos, policy, softwareId, softwareVersion, softwareStatement, redirectUris, postLogoutRedirectUris, tokenEndpointAuthMethod, grantTypes, responseTypes, public: _public, type, skipConsent, enableEndSession, requirePKCE, referenceId, metadata } = input;
1280
+ const { clientId, clientSecret, disabled, scopes, userId, createdAt, updatedAt: _updatedAt, expiresAt, name, uri, icon, contacts, tos, policy, softwareId, softwareVersion, softwareStatement, redirectUris, postLogoutRedirectUris, tokenEndpointAuthMethod, grantTypes, responseTypes, public: _public, type, skipConsent, enableEndSession, requirePKCE, subjectType, referenceId, metadata } = input;
1232
1281
  const _expiresAt = expiresAt ? Math.round(new Date(expiresAt).getTime() / 1e3) : void 0;
1233
1282
  const _createdAt = createdAt ? Math.round(new Date(createdAt).getTime() / 1e3) : void 0;
1234
1283
  const _scopes = scopes?.join(" ");
@@ -1249,7 +1298,7 @@ function schemaToOAuth(input) {
1249
1298
  software_id: softwareId ?? void 0,
1250
1299
  software_version: softwareVersion ?? void 0,
1251
1300
  software_statement: softwareStatement ?? void 0,
1252
- redirect_uris: redirectUris ?? void 0,
1301
+ redirect_uris: redirectUris ?? [],
1253
1302
  post_logout_redirect_uris: postLogoutRedirectUris ?? void 0,
1254
1303
  token_endpoint_auth_method: tokenEndpointAuthMethod ?? void 0,
1255
1304
  grant_types: grantTypes ?? void 0,
@@ -1260,6 +1309,7 @@ function schemaToOAuth(input) {
1260
1309
  skip_consent: skipConsent ?? void 0,
1261
1310
  enable_end_session: enableEndSession ?? void 0,
1262
1311
  require_pkce: requirePKCE ?? void 0,
1312
+ subject_type: subjectType ?? void 0,
1263
1313
  reference_id: referenceId ?? void 0
1264
1314
  };
1265
1315
  }
@@ -1335,8 +1385,8 @@ async function getClientEndpoint(ctx, opts) {
1335
1385
  * Provides public client fields for any logged-in user.
1336
1386
  * This is commonly used to display information on login flow pages.
1337
1387
  */
1338
- async function getClientPublicEndpoint(ctx, opts) {
1339
- const client = await getClient(ctx, opts, ctx.query.client_id);
1388
+ async function getClientPublicEndpoint(ctx, opts, clientId) {
1389
+ const client = await getClient(ctx, opts, clientId);
1340
1390
  if (!client) throw new APIError("NOT_FOUND", {
1341
1391
  error_description: "client not found",
1342
1392
  error: "not_found"
@@ -1572,6 +1622,7 @@ const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/creat
1572
1622
  skip_consent: z.boolean().optional(),
1573
1623
  enable_end_session: z.boolean().optional(),
1574
1624
  require_pkce: z.boolean().optional(),
1625
+ subject_type: z.enum(["public", "pairwise"]).optional(),
1575
1626
  metadata: z.record(z.string(), z.unknown()).optional()
1576
1627
  }),
1577
1628
  metadata: {
@@ -1899,9 +1950,22 @@ const getOAuthClientPublic = (opts) => createAuthEndpoint("/oauth2/public-client
1899
1950
  method: "GET",
1900
1951
  use: [sessionMiddleware],
1901
1952
  query: z.object({ client_id: z.string() }),
1902
- metadata: { openapi: { description: "Gets publically available client fields" } }
1953
+ metadata: { openapi: { description: "Gets publicly available client fields" } }
1903
1954
  }, async (ctx) => {
1904
- return getClientPublicEndpoint(ctx, opts);
1955
+ const clientId = ctx.query.client_id;
1956
+ return getClientPublicEndpoint(ctx, opts, clientId);
1957
+ });
1958
+ const getOAuthClientPublicPrelogin = (opts) => createAuthEndpoint("/oauth2/public-client-prelogin", {
1959
+ method: "POST",
1960
+ use: [publicSessionMiddleware(opts)],
1961
+ body: z.object({
1962
+ client_id: z.string(),
1963
+ oauth_query: z.string().optional()
1964
+ }),
1965
+ metadata: { openapi: { description: "Gets publicly available client fields (prior to login)" } }
1966
+ }, async (ctx) => {
1967
+ const clientId = ctx.body.client_id;
1968
+ return getClientPublicEndpoint(ctx, opts, clientId);
1905
1969
  });
1906
1970
  const getOAuthClients = (opts) => createAuthEndpoint("/oauth2/get-clients", {
1907
1971
  method: "GET",
@@ -2366,6 +2430,10 @@ const schema = {
2366
2430
  type: "boolean",
2367
2431
  required: false
2368
2432
  },
2433
+ subjectType: {
2434
+ type: "string",
2435
+ required: false
2436
+ },
2369
2437
  scopes: {
2370
2438
  type: "string[]",
2371
2439
  required: false
@@ -2662,6 +2730,7 @@ const oauthProvider = (options) => {
2662
2730
  claims: Array.from(claims),
2663
2731
  clientRegistrationAllowedScopes
2664
2732
  };
2733
+ if (opts.pairwiseSecret && opts.pairwiseSecret.length < 32) throw new BetterAuthError("pairwiseSecret must be at least 32 characters long for adequate HMAC-SHA256 security");
2665
2734
  if (opts.grantTypes && opts.grantTypes.includes("refresh_token") && !opts.grantTypes.includes("authorization_code")) throw new BetterAuthError("refresh_token grant requires authorization_code grant");
2666
2735
  if (opts.disableJwtPlugin && (opts.storeClientSecret === "hashed" || typeof opts.storeClientSecret === "object" && "hash" in opts.storeClientSecret)) throw new BetterAuthError("unable to store hashed secrets because id tokens will be signed with secret");
2667
2736
  if (!opts.disableJwtPlugin && (opts.storeClientSecret === "encrypted" || typeof opts.storeClientSecret === "object" && ("encrypt" in opts.storeClientSecret || "decrypt" in opts.storeClientSecret))) throw new BetterAuthError("encryption method not recommended, please use 'hashed' or the 'hash' function");
@@ -2684,15 +2753,11 @@ const oauthProvider = (options) => {
2684
2753
  },
2685
2754
  handler: createAuthMiddleware(async (ctx) => {
2686
2755
  const query = ctx.body.oauth_query;
2687
- let queryParams = new URLSearchParams(query);
2688
- const sig = queryParams.get("sig");
2689
- const exp = Number(queryParams.get("exp"));
2756
+ if (!await verifyOAuthQueryParams(query, ctx.context.secret)) throw new APIError("BAD_REQUEST", { error: "invalid_signature" });
2757
+ const queryParams = new URLSearchParams(query);
2690
2758
  queryParams.delete("sig");
2691
- queryParams = new URLSearchParams(queryParams);
2692
- const verifySig = await makeSignature(queryParams.toString(), ctx.context.secret);
2693
- if (!sig || !constantTimeEqual(sig, verifySig) || /* @__PURE__ */ new Date(exp * 1e3) < /* @__PURE__ */ new Date()) throw new APIError("BAD_REQUEST", { error: "invalid_signature" });
2694
2759
  queryParams.delete("exp");
2695
- await oAuthState.set({ query: new URLSearchParams(queryParams).toString() });
2760
+ await oAuthState.set({ query: queryParams.toString() });
2696
2761
  if (ctx.path === "/sign-in/social" || ctx.path === "/sign-in/oauth2") {
2697
2762
  if (ctx.body.additionalData?.query) return;
2698
2763
  if (!ctx.body.additionalData) ctx.body.additionalData = {};
@@ -3371,7 +3436,8 @@ const oauthProvider = (options) => {
3371
3436
  "web",
3372
3437
  "native",
3373
3438
  "user-agent-based"
3374
- ]).optional()
3439
+ ]).optional(),
3440
+ subject_type: z.enum(["public", "pairwise"]).optional()
3375
3441
  }),
3376
3442
  metadata: { openapi: {
3377
3443
  description: "Register an OAuth2 application",
@@ -3515,6 +3581,7 @@ const oauthProvider = (options) => {
3515
3581
  createOAuthClient: createOAuthClient(opts),
3516
3582
  getOAuthClient: getOAuthClient(opts),
3517
3583
  getOAuthClientPublic: getOAuthClientPublic(opts),
3584
+ getOAuthClientPublicPrelogin: getOAuthClientPublicPrelogin(opts),
3518
3585
  getOAuthClients: getOAuthClients(opts),
3519
3586
  adminUpdateOAuthClient: adminUpdateOAuthClient(opts),
3520
3587
  updateOAuthClient: updateOAuthClient(opts),
@@ -3576,12 +3643,17 @@ function formatErrorURL(url, error, description, state, iss) {
3576
3643
  return `${url}${url.includes("?") ? "&" : "?"}${searchParams.toString()}`;
3577
3644
  }
3578
3645
  const handleRedirect = (ctx, uri) => {
3579
- if (ctx.headers?.get("accept")?.includes("application/json")) return {
3646
+ const fromFetch = isBrowserFetchRequest(ctx.request?.headers);
3647
+ const acceptJson = ctx.headers?.get("accept")?.includes("application/json");
3648
+ if (fromFetch || acceptJson) return {
3580
3649
  redirect: true,
3581
3650
  url: uri.toString()
3582
3651
  };
3583
3652
  else throw ctx.redirect(uri);
3584
3653
  };
3654
+ function redirectWithPromptNoneError(ctx, opts, query, error, description) {
3655
+ return handleRedirect(ctx, formatErrorURL(query.redirect_uri, error, description, query.state, getIssuer(ctx, opts)));
3656
+ }
3585
3657
  /**
3586
3658
  * Validates that the issuer URL
3587
3659
  * - MUST use HTTPS scheme (HTTP allowed for localhost in dev)
@@ -3633,6 +3705,7 @@ async function authorizeEndpoint(ctx, opts, settings) {
3633
3705
  if (!query.client_id) throw ctx.redirect(getErrorURL(ctx, "invalid_client", "client_id is required"));
3634
3706
  if (!query.response_type) throw ctx.redirect(getErrorURL(ctx, "invalid_request", "response_type is required"));
3635
3707
  const promptSet = ctx.query?.prompt ? parsePrompt(ctx.query?.prompt) : void 0;
3708
+ const promptNone = promptSet?.has("none") ?? false;
3636
3709
  if (promptSet?.has("select_account") && !opts.selectAccount?.page) throw ctx.redirect(getErrorURL(ctx, `unsupported_prompt_select_account`, "unsupported prompt type"));
3637
3710
  if (!(query.response_type === "code")) throw ctx.redirect(getErrorURL(ctx, "unsupported_response_type", "unsupported response type"));
3638
3711
  const client = await getClient(ctx, opts, query.client_id);
@@ -3660,7 +3733,10 @@ async function authorizeEndpoint(ctx, opts, settings) {
3660
3733
  if (!["S256"].includes(query.code_challenge_method)) throw ctx.redirect(formatErrorURL(query.redirect_uri, "invalid_request", "invalid code_challenge method, only S256 is supported", query.state, getIssuer(ctx, opts)));
3661
3734
  }
3662
3735
  const session = await getSessionFromCtx(ctx);
3663
- if (!session || promptSet?.has("login") || promptSet?.has("create")) return redirectWithPromptCode(ctx, opts, promptSet?.has("create") ? "create" : "login");
3736
+ if (!session || promptSet?.has("login") || promptSet?.has("create")) {
3737
+ if (promptNone) return redirectWithPromptNoneError(ctx, opts, query, "login_required", "authentication required");
3738
+ return redirectWithPromptCode(ctx, opts, promptSet?.has("create") ? "create" : "login");
3739
+ }
3664
3740
  if (settings?.isAuthorize && promptSet?.has("select_account")) return redirectWithPromptCode(ctx, opts, "select_account");
3665
3741
  if (settings?.isAuthorize && opts.selectAccount) {
3666
3742
  if (await opts.selectAccount.shouldRedirect({
@@ -3668,7 +3744,10 @@ async function authorizeEndpoint(ctx, opts, settings) {
3668
3744
  user: session.user,
3669
3745
  session: session.session,
3670
3746
  scopes: requestedScopes
3671
- })) return redirectWithPromptCode(ctx, opts, "select_account");
3747
+ })) {
3748
+ if (promptNone) return redirectWithPromptNoneError(ctx, opts, query, "account_selection_required", "End-User account selection is required");
3749
+ return redirectWithPromptCode(ctx, opts, "select_account");
3750
+ }
3672
3751
  }
3673
3752
  if (opts.signup?.shouldRedirect) {
3674
3753
  const signupRedirect = await opts.signup.shouldRedirect({
@@ -3677,7 +3756,10 @@ async function authorizeEndpoint(ctx, opts, settings) {
3677
3756
  session: session.session,
3678
3757
  scopes: requestedScopes
3679
3758
  });
3680
- if (signupRedirect) return redirectWithPromptCode(ctx, opts, "create", typeof signupRedirect === "string" ? signupRedirect : void 0);
3759
+ if (signupRedirect) {
3760
+ if (promptNone) return redirectWithPromptNoneError(ctx, opts, query, "interaction_required", "End-User interaction is required");
3761
+ return redirectWithPromptCode(ctx, opts, "create", typeof signupRedirect === "string" ? signupRedirect : void 0);
3762
+ }
3681
3763
  }
3682
3764
  if (!settings?.postLogin && opts.postLogin) {
3683
3765
  if (await opts.postLogin.shouldRedirect({
@@ -3685,7 +3767,10 @@ async function authorizeEndpoint(ctx, opts, settings) {
3685
3767
  user: session.user,
3686
3768
  session: session.session,
3687
3769
  scopes: requestedScopes
3688
- })) return redirectWithPromptCode(ctx, opts, "post_login");
3770
+ })) {
3771
+ if (promptNone) return redirectWithPromptNoneError(ctx, opts, query, "interaction_required", "End-User interaction is required");
3772
+ return redirectWithPromptCode(ctx, opts, "post_login");
3773
+ }
3689
3774
  }
3690
3775
  if (promptSet?.has("consent")) return redirectWithPromptCode(ctx, opts, "consent");
3691
3776
  const referenceId = await opts.postLogin?.consentReferenceId?.({
@@ -3718,7 +3803,10 @@ async function authorizeEndpoint(ctx, opts, settings) {
3718
3803
  }] : []
3719
3804
  ]
3720
3805
  });
3721
- if (!consent || !requestedScopes.every((val) => consent.scopes.includes(val))) return redirectWithPromptCode(ctx, opts, "consent");
3806
+ if (!consent || !requestedScopes.every((val) => consent.scopes.includes(val))) {
3807
+ if (promptNone) return redirectWithPromptNoneError(ctx, opts, query, "consent_required", "End-User consent is required");
3808
+ return redirectWithPromptCode(ctx, opts, "consent");
3809
+ }
3722
3810
  return redirectWithAuthorizationCode(ctx, opts, {
3723
3811
  query,
3724
3812
  clientId: client.clientId,
@@ -3818,7 +3906,7 @@ function oidcServerMetadata(ctx, opts) {
3818
3906
  }),
3819
3907
  claims_supported: opts?.advertisedMetadata?.claims_supported ?? opts?.claims ?? [],
3820
3908
  userinfo_endpoint: `${baseURL}/oauth2/userinfo`,
3821
- subject_types_supported: ["public"],
3909
+ subject_types_supported: opts.pairwiseSecret ? ["public", "pairwise"] : ["public"],
3822
3910
  id_token_signing_alg_values_supported: jwtPluginOptions?.jwks?.keyPairConfig?.alg ? [jwtPluginOptions?.jwks?.keyPairConfig?.alg] : opts.disableJwtPlugin ? ["HS256"] : ["EdDSA"],
3823
3911
  end_session_endpoint: `${baseURL}/oauth2/end-session`,
3824
3912
  acr_values_supported: ["urn:mace:incommon:iap:bronze"],
@@ -3826,7 +3914,8 @@ function oidcServerMetadata(ctx, opts) {
3826
3914
  "login",
3827
3915
  "consent",
3828
3916
  "create",
3829
- "select_account"
3917
+ "select_account",
3918
+ "none"
3830
3919
  ]
3831
3920
  };
3832
3921
  }