@absolutejs/auth 0.56.2 → 0.56.3

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.
Files changed (174) hide show
  1. package/dist/cli/migrate.js.map +1 -1
  2. package/dist/index.js +15 -1
  3. package/dist/index.js.map +4 -4
  4. package/dist/oidc/index.js +5 -1
  5. package/dist/oidc/index.js.map +3 -3
  6. package/dist/server.js +15 -1
  7. package/dist/server.js.map +4 -4
  8. package/package.json +1 -1
  9. package/dist/abuse/captcha.d.ts +0 -11
  10. package/dist/abuse/config.d.ts +0 -29
  11. package/dist/actions.d.ts +0 -27
  12. package/dist/agents/config.d.ts +0 -44
  13. package/dist/agents/context.d.ts +0 -46
  14. package/dist/agents/principal.d.ts +0 -4
  15. package/dist/agents/registration.d.ts +0 -162
  16. package/dist/agents/routes.d.ts +0 -321
  17. package/dist/agents/types.d.ts +0 -129
  18. package/dist/apikeys/config.d.ts +0 -64
  19. package/dist/apikeys/routes.d.ts +0 -83
  20. package/dist/apikeys/types.d.ts +0 -53
  21. package/dist/audit/config.d.ts +0 -9
  22. package/dist/audit/export.d.ts +0 -2
  23. package/dist/audit/inMemoryAuditStore.d.ts +0 -2
  24. package/dist/audit/integrity.d.ts +0 -19
  25. package/dist/audit/postgresAuditStore.d.ts +0 -116
  26. package/dist/audit/siem.d.ts +0 -11
  27. package/dist/audit/types.d.ts +0 -19
  28. package/dist/audit/wrap.d.ts +0 -9
  29. package/dist/authContext.d.ts +0 -193
  30. package/dist/authorization/config.d.ts +0 -19
  31. package/dist/authorization/protectPermission.d.ts +0 -52
  32. package/dist/compliance/cipher.d.ts +0 -11
  33. package/dist/compliance/config.d.ts +0 -18
  34. package/dist/compliance/redaction.d.ts +0 -8
  35. package/dist/compliance/routes.d.ts +0 -89
  36. package/dist/constants.d.ts +0 -10
  37. package/dist/credentials/backgroundOps.d.ts +0 -45
  38. package/dist/credentials/config.d.ts +0 -68
  39. package/dist/credentials/emailValidation.d.ts +0 -9
  40. package/dist/credentials/emailVerification.d.ts +0 -83
  41. package/dist/credentials/import.d.ts +0 -34
  42. package/dist/credentials/inMemoryCredentialStore.d.ts +0 -2
  43. package/dist/credentials/legacyHashers.d.ts +0 -3
  44. package/dist/credentials/login.d.ts +0 -76
  45. package/dist/credentials/passwordPolicy.d.ts +0 -15
  46. package/dist/credentials/passwordReset.d.ts +0 -87
  47. package/dist/credentials/postgresCredentialStore.d.ts +0 -235
  48. package/dist/credentials/register.d.ts +0 -54
  49. package/dist/credentials/routes.d.ts +0 -201
  50. package/dist/credentials/types.d.ts +0 -26
  51. package/dist/crypto.d.ts +0 -32
  52. package/dist/errors.d.ts +0 -12
  53. package/dist/federation/tokenStore.d.ts +0 -29
  54. package/dist/htmx/configuredRoutes.d.ts +0 -542
  55. package/dist/htmx/renderers.d.ts +0 -8
  56. package/dist/htmx/routes.d.ts +0 -514
  57. package/dist/htmx/types.d.ts +0 -129
  58. package/dist/linkedProviders/inMemoryStores.d.ts +0 -9
  59. package/dist/linkedProviders/neonStores.d.ts +0 -484
  60. package/dist/linkedProviders/oauthAccountResolver.d.ts +0 -28
  61. package/dist/linkedProviders/oauthResolver.d.ts +0 -9
  62. package/dist/linkedProviders/resolver.d.ts +0 -22
  63. package/dist/lockout/config.d.ts +0 -17
  64. package/dist/lockout/inMemoryLockoutStore.d.ts +0 -2
  65. package/dist/lockout/postgresLockoutStore.d.ts +0 -71
  66. package/dist/lockout/redisLockoutStore.d.ts +0 -3
  67. package/dist/lockout/types.d.ts +0 -12
  68. package/dist/mfa/backupCodes.d.ts +0 -5
  69. package/dist/mfa/challenge.d.ts +0 -70
  70. package/dist/mfa/config.d.ts +0 -50
  71. package/dist/mfa/gate.d.ts +0 -2
  72. package/dist/mfa/inMemoryMfaStore.d.ts +0 -2
  73. package/dist/mfa/management.d.ts +0 -106
  74. package/dist/mfa/postgresMfaStore.d.ts +0 -206
  75. package/dist/mfa/rotation.d.ts +0 -17
  76. package/dist/mfa/routes.d.ts +0 -234
  77. package/dist/mfa/secret.d.ts +0 -2
  78. package/dist/mfa/sms.d.ts +0 -105
  79. package/dist/mfa/totp.d.ts +0 -91
  80. package/dist/mfa/types.d.ts +0 -23
  81. package/dist/oidc/clientAuth.d.ts +0 -19
  82. package/dist/oidc/config.d.ts +0 -296
  83. package/dist/oidc/dpop.d.ts +0 -23
  84. package/dist/oidc/inMemoryVciStores.d.ts +0 -3
  85. package/dist/oidc/jar.d.ts +0 -14
  86. package/dist/oidc/keys.d.ts +0 -35
  87. package/dist/oidc/logout.d.ts +0 -38
  88. package/dist/oidc/mtls.d.ts +0 -11
  89. package/dist/oidc/par.d.ts +0 -28
  90. package/dist/oidc/registration.d.ts +0 -138
  91. package/dist/oidc/routes.d.ts +0 -569
  92. package/dist/oidc/types.d.ts +0 -145
  93. package/dist/oidc/userinfo.d.ts +0 -21
  94. package/dist/oidc/vci.d.ts +0 -159
  95. package/dist/oidc/vciRoutes.d.ts +0 -92
  96. package/dist/organizations/config.d.ts +0 -46
  97. package/dist/organizations/operations.d.ts +0 -39
  98. package/dist/organizations/routes.d.ts +0 -299
  99. package/dist/organizations/types.d.ts +0 -45
  100. package/dist/passwordless/config.d.ts +0 -43
  101. package/dist/passwordless/routes.d.ts +0 -163
  102. package/dist/passwordless/types.d.ts +0 -9
  103. package/dist/pluginIdentity.d.ts +0 -2
  104. package/dist/portal/config.d.ts +0 -34
  105. package/dist/portal/operations.d.ts +0 -10
  106. package/dist/portal/routes.d.ts +0 -160
  107. package/dist/portal/types.d.ts +0 -16
  108. package/dist/providers/clients.d.ts +0 -1818
  109. package/dist/providersFromEnv.d.ts +0 -19
  110. package/dist/roles/config.d.ts +0 -27
  111. package/dist/roles/operations.d.ts +0 -8
  112. package/dist/roles/routes.d.ts +0 -106
  113. package/dist/roles/types.d.ts +0 -14
  114. package/dist/routes/authorize.d.ts +0 -102
  115. package/dist/routes/callback.d.ts +0 -64
  116. package/dist/routes/profile.d.ts +0 -70
  117. package/dist/routes/protectRoute.d.ts +0 -51
  118. package/dist/routes/refresh.d.ts +0 -74
  119. package/dist/routes/revoke.d.ts +0 -73
  120. package/dist/routes/sessions.d.ts +0 -103
  121. package/dist/routes/signout.d.ts +0 -70
  122. package/dist/routes/stepUp.d.ts +0 -48
  123. package/dist/routes/userStatus.d.ts +0 -72
  124. package/dist/scim/config.d.ts +0 -57
  125. package/dist/scim/extensions.d.ts +0 -30
  126. package/dist/scim/inMemoryScimTokenStore.d.ts +0 -2
  127. package/dist/scim/routes.d.ts +0 -366
  128. package/dist/scim/serialize.d.ts +0 -83
  129. package/dist/scim/types.d.ts +0 -54
  130. package/dist/session/access.d.ts +0 -33
  131. package/dist/session/anonymous.d.ts +0 -12
  132. package/dist/session/cleanup.d.ts +0 -45
  133. package/dist/session/impersonation.d.ts +0 -34
  134. package/dist/session/inMemoryStore.d.ts +0 -7
  135. package/dist/session/multiSession.d.ts +0 -27
  136. package/dist/session/neonStore.d.ts +0 -513
  137. package/dist/session/promote.d.ts +0 -23
  138. package/dist/session/redisStore.d.ts +0 -6
  139. package/dist/session/sessionsConfig.d.ts +0 -9
  140. package/dist/session/state.d.ts +0 -33
  141. package/dist/session/types.d.ts +0 -21
  142. package/dist/session/userSessions.d.ts +0 -19
  143. package/dist/sso/config.d.ts +0 -149
  144. package/dist/sso/discoveryRoute.d.ts +0 -63
  145. package/dist/sso/oidcRoutes.d.ts +0 -98
  146. package/dist/sso/samlRoutes.d.ts +0 -179
  147. package/dist/sso/types.d.ts +0 -53
  148. package/dist/stores/postgres.d.ts +0 -5
  149. package/dist/stores/redis.d.ts +0 -5
  150. package/dist/telemetry/tracing.d.ts +0 -15
  151. package/dist/tenancy.d.ts +0 -9
  152. package/dist/typeGuards.d.ts +0 -6
  153. package/dist/typebox.d.ts +0 -4
  154. package/dist/types.d.ts +0 -421
  155. package/dist/utils.d.ts +0 -45
  156. package/dist/vault/config.d.ts +0 -20
  157. package/dist/vault/inMemoryVaultStore.d.ts +0 -2
  158. package/dist/vault/postgresVaultStore.d.ts +0 -86
  159. package/dist/vault/types.d.ts +0 -14
  160. package/dist/vc/inMemoryVpStores.d.ts +0 -2
  161. package/dist/vc/openid4vp.d.ts +0 -82
  162. package/dist/vc/postgresVcStores.d.ts +0 -300
  163. package/dist/vc/sdJwt.d.ts +0 -37
  164. package/dist/vc/statusList.d.ts +0 -29
  165. package/dist/vc/statusListRoutes.d.ts +0 -63
  166. package/dist/vc/vpRoutes.d.ts +0 -120
  167. package/dist/webauthn/adapter.d.ts +0 -59
  168. package/dist/webauthn/config.d.ts +0 -36
  169. package/dist/webauthn/routes.d.ts +0 -155
  170. package/dist/webauthn/types.d.ts +0 -17
  171. package/dist/webhooks/config.d.ts +0 -34
  172. package/dist/webhooks/dispatcher.d.ts +0 -3
  173. package/dist/webhooks/sign.d.ts +0 -11
  174. package/dist/webhooks/types.d.ts +0 -25
@@ -19,7 +19,7 @@
19
19
  "// @bun\n// src/constants.ts\nvar BASE64_BLOCK_SIZE = 4;\nvar NUM_GENERATOR_BYTES = 32;\n\n// src/graphqlQueries.ts\nvar anilistProfileQuery = `query {\n Viewer {\n id\n name\n about\n avatar {\n large\n medium\n }\n bannerImage\n siteUrl\n createdAt\n updatedAt\n donatorTier\n donatorBadge\n unreadNotificationCount\n options {\n titleLanguage\n displayAdultContent\n airingNotifications\n profileColor\n activityMergeTime\n staffNameLanguage\n }\n mediaListOptions {\n scoreFormat\n rowOrder\n animeList {\n sectionOrder\n customLists\n advancedScoringEnabled\n }\n mangaList {\n sectionOrder\n customLists\n advancedScoringEnabled\n }\n }\n statistics {\n anime {\n count\n meanScore\n minutesWatched\n episodesWatched\n }\n manga {\n count\n meanScore\n chaptersRead\n volumesRead\n }\n }\n favourites {\n anime {\n nodes {\n id\n title {\n romaji\n english\n }\n siteUrl\n }\n }\n manga {\n nodes {\n id\n title {\n romaji\n english\n }\n siteUrl\n }\n }\n characters {\n nodes {\n id\n name {\n full\n }\n image {\n large\n }\n }\n }\n staff {\n nodes {\n id\n name {\n full\n }\n image {\n large\n }\n }\n }\n studios {\n nodes {\n id\n name\n siteUrl\n }\n }\n }\n isFollower\n isFollowing\n }\n}`;\n\n// src/providers.ts\nvar defineProviders = (providers) => providers;\nvar defineProvider = (config) => config;\nvar providers = defineProviders({\n \"42\": {\n authorizationUrl: \"https://api.intra.42.fr/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.intra.42.fr/v2/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.intra.42.fr/oauth/token\"\n }\n },\n amazoncognito: {\n authorizationUrl: \"https://${domain}/oauth2/authorize\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `https://${config.domain}/oauth2/userInfo`\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `https://${config.domain}/oauth2/revoke`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.domain}/oauth2/token`\n }\n },\n anilist: {\n authorizationUrl: \"https://anilist.co/api/v2/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n body: {\n query: anilistProfileQuery\n },\n encoding: \"application/json\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n method: \"POST\",\n url: \"https://graphql.anilist.co\"\n },\n scopeRequired: false,\n subject: [\"data\", \"Viewer\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://anilist.co/api/v2/oauth/token\"\n }\n },\n apple: {\n authorizationUrl: \"https://appleid.apple.com/auth/authorize\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://appleid.apple.com/auth/userinfo\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://appleid.apple.com/auth/token\"\n }\n },\n atlassian: {\n authorizationUrl: \"https://auth.atlassian.com/authorize\",\n createAuthorizationURLSearchParams: {\n audience: \"api.atlassian.com\"\n },\n email: [\"email\"],\n fullName: [\"name\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"picture\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.atlassian.com/me\"\n },\n scopeRequired: true,\n subject: [\"account_id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://auth.atlassian.com/oauth/token\"\n }\n },\n attio: {\n authorizationUrl: \"https://app.attio.com/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.attio.com/v2/self\"\n },\n scopeRequired: true,\n subject: [\"workspace_id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://app.attio.com/oauth/token\"\n }\n },\n auth0: {\n authorizationUrl: (config) => `https://${config.domain}/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `https://${config.domain}/userinfo`\n },\n revocationRequest: {\n authIn: \"body\",\n body: new URLSearchParams({\n token_type_hint: \"refresh_token\"\n }),\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `https://${config.domain}/oauth/revoke`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.domain}/oauth/token`\n }\n },\n authentik: {\n authorizationUrl: (config) => `https://${config.baseURL}/oauth/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `https://${config.baseURL}/api/v3/user/`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.baseURL}/oauth/token`\n }\n },\n autodesk: {\n authorizationUrl: \"https://developer.api.autodesk.com/authentication/v2/authorize\",\n email: [\"email\"],\n familyName: [\"family_name\"],\n fullName: [\"name\"],\n givenName: [\"given_name\"],\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.userprofile.autodesk.com/userinfo\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://developer.api.autodesk.com/authentication/v2/token\"\n }\n },\n azureadb2c: {\n authorizationUrl: (config) => `https://${config.tenantSubdomain}.b2clogin.com/${config.tenantSubdomain}.onmicrosoft.com/${config.policy}/oauth2/v2.0/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n scopeRequired: false,\n subject: [\"sub\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.tenantSubdomain}.b2clogin.com/${config.tenantSubdomain}.onmicrosoft.com/${config.policy}/oauth2/v2.0/token`\n }\n },\n battlenet: {\n authorizationUrl: \"https://oauth.battle.net/authorize\",\n isOIDC: true,\n isRefreshable: false,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://oauth.battle.net/userinfo\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth.battle.net/token\"\n }\n },\n bitbucket: {\n authorizationUrl: \"https://bitbucket.org/site/oauth2/authorize\",\n fullName: [\"display_name\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"links\", \"avatar\", \"href\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.bitbucket.org/2.0/user\"\n },\n scopeRequired: false,\n subject: [\"uuid\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://bitbucket.org/site/oauth2/access_token\"\n }\n },\n box: {\n authorizationUrl: \"https://account.box.com/api/oauth2/authorize\",\n email: [\"login\"],\n fullName: [\"name\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"avatar_url\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.box.com/2.0/users/me\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://api.box.com/oauth2/revoke\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.box.com/oauth2/token\"\n }\n },\n bungie: {\n authorizationUrl: \"https://www.bungie.net/en/OAuth/Authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n headers: {\n \"X-API-Key\": \"<YOUR_API_KEY>\"\n },\n method: \"GET\",\n url: \"https://www.bungie.net/Platform/User/GetCurrentBungieNetUser\"\n },\n scopeRequired: false,\n subject: [\"Response\", \"membershipId\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.bungie.net/Platform/App/OAuth/token\"\n }\n },\n calendly: {\n authorizationUrl: \"https://auth.calendly.com/oauth/authorize\",\n email: [\"resource\", \"email\"],\n fullName: [\"resource\", \"name\"],\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.calendly.com/users/me\"\n },\n scopeRequired: false,\n subject: [\"resource\", \"uri\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://auth.calendly.com/oauth/token\"\n }\n },\n close: {\n authorizationUrl: \"https://app.close.com/oauth2/authorize/\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.close.com/api/v1/me/\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.close.com/oauth2/token/\"\n }\n },\n coinbase: {\n authorizationUrl: \"https://www.coinbase.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.coinbase.com/v2/user\"\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.coinbase.com/oauth/token\"\n }\n },\n discord: {\n authorizationUrl: \"https://discord.com/api/oauth2/authorize\",\n email: [\"email\"],\n isOIDC: true,\n isRefreshable: true,\n picture: [\"avatar\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://discord.com/api/users/@me\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://discord.com/api/oauth2/token\"\n }\n },\n donationalerts: {\n authorizationUrl: \"https://www.donationalerts.com/oauth/authorize\",\n email: [\"data\", \"email\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"data\", \"avatar\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://www.donationalerts.com/api/v1/user/oauth\"\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.donationalerts.com/oauth/token\"\n }\n },\n dribbble: {\n authorizationUrl: \"https://dribbble.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: false,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.dribbble.com/v2/user\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://dribbble.com/oauth/token\"\n }\n },\n dropbox: {\n authorizationUrl: \"https://www.dropbox.com/oauth2/authorize\",\n email: [\"email\"],\n familyName: [\"name\", \"surname\"],\n fullName: [\"name\", \"display_name\"],\n givenName: [\"name\", \"given_name\"],\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"POST\",\n url: \"https://api.dropboxapi.com/2/users/get_current_account\"\n },\n revocationRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://api.dropboxapi.com/2/auth/token/revoke\"\n },\n scopeRequired: false,\n subject: [\"account_id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.dropboxapi.com/oauth2/token\"\n }\n },\n epicgames: {\n authorizationUrl: \"https://www.epicgames.com/id/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.epicgames.dev/epic/oauth/v2/userInfo\"\n },\n scopeRequired: false,\n subject: [\"account_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.epicgames.dev/epic/oauth/v1/token\"\n }\n },\n etsy: {\n authorizationUrl: \"https://www.etsy.com/oauth/connect\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://openapi.etsy.com/v3/application/users/me\"\n },\n scopeRequired: false,\n subject: [\"results\", \"0\", \"user_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.etsy.com/v3/public/oauth/token\"\n }\n },\n facebook: {\n authorizationUrl: \"https://www.facebook.com/v16.0/dialog/oauth\",\n email: [\"email\"],\n familyName: [\"family_name\"],\n fullName: [\"name\"],\n givenName: [\"given_name\"],\n isOIDC: true,\n isRefreshable: false,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n method: \"GET\",\n searchParams: [[\"fields\", \"id,name,email,picture\"]],\n url: \"https://graph.facebook.com/me\"\n },\n scopeRequired: false,\n subject: [\"sub\"],\n subjectBySource: {\n idToken: [\"sub\"],\n profile: [\"id\"]\n },\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://graph.facebook.com/v16.0/oauth/access_token\"\n }\n },\n figma: {\n authorizationUrl: \"https://www.figma.com/oauth\",\n email: [\"email\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"img_url\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.figma.com/v1/me\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.figma.com/v1/oauth/token\"\n }\n },\n gitea: {\n authorizationUrl: (config) => `${config.baseURL}/login/oauth/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `${config.baseURL}/api/v1/user`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `${config.baseURL}/login/oauth/access_token`\n }\n },\n github: {\n authorizationUrl: \"https://github.com/login/oauth/authorize\",\n email: [\"email\"],\n isOIDC: false,\n isRefreshable: false,\n picture: [\"avatar_url\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.github.com/user\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://github.com/login/oauth/access_token\"\n }\n },\n gitlab: {\n authorizationUrl: (config) => `${config.baseURL}/oauth/authorize`,\n email: [\"email\"],\n fullName: [\"name\"],\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://gitlab.com/api/v4/user\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `${config.baseURL}/oauth/revoke`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `${config.baseURL}/oauth/token`\n }\n },\n gohighlevel: {\n authorizationUrl: \"https://marketplace.gohighlevel.com/v2/oauth/chooselocation\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n headers: {\n Version: \"2021-07-28\"\n },\n method: \"GET\",\n url: \"https://services.leadconnectorhq.com/users/me\"\n },\n scopeRequired: true,\n subject: [\"locationId\"],\n subjectBySource: {\n tokenResponse: [\"locationId\"]\n },\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://services.leadconnectorhq.com/oauth/token\"\n }\n },\n google: {\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n email: [\"email\"],\n familyName: [\"family_name\"],\n fullName: [\"name\"],\n givenName: [\"given_name\"],\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://openidconnect.googleapis.com/v1/userinfo\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://oauth2.googleapis.com/revoke\"\n },\n scopeRequired: true,\n subject: [\"sub\"],\n subjectBySource: {\n idToken: [\"sub\"],\n profile: [\"sub\"]\n },\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth2.googleapis.com/token\"\n }\n },\n hubspot: {\n authorizationUrl: \"https://app.hubspot.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"path\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.hubapi.com/oauth/v1/access-tokens\"\n },\n scopeRequired: true,\n subject: [\"hub_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.hubapi.com/oauth/v1/token\"\n }\n },\n intuit: {\n authorizationUrl: \"https://appcenter.intuit.com/connect/oauth2\",\n isOIDC: true,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => config.environment === \"production\" ? \"https://accounts.platform.intuit.com/v1/openid_connect/userinfo\" : \"https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n headers: (config) => ({\n Authorization: `Basic ${encodeBase64(`${config.clientId}:${config.clientSecret}`)}`\n }),\n tokenParamName: \"token\",\n url: \"https://developer.api.intuit.com/v2/oauth2/tokens/revoke\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer\"\n }\n },\n kakao: {\n authorizationUrl: \"https://kauth.kakao.com/oauth/authorize\",\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://kapi.kakao.com/v2/user/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://kauth.kakao.com/oauth/token\"\n }\n },\n keycloak: {\n authorizationUrl: (config) => `${config.realmURL}/protocol/openid-connect/auth`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.kick.com/v1/user\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `${config.realmURL}/protocol/openid-connect/revoke`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `${config.realmURL}/protocol/openid-connect/token`\n }\n },\n kick: {\n authorizationUrl: \"https://id.kick.com/oauth/authorize\",\n email: [\"data\", \"email\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"data\", \"profile_picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.kick.com/public/v1/users\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://id.kick.com/oauth/revoke\"\n },\n scopeRequired: true,\n subject: [\"data\", \"user_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://id.kick.com/oauth/token\"\n }\n },\n lichess: {\n authorizationUrl: \"https://lichess.org/oauth/authorize\",\n isOIDC: false,\n isRefreshable: false,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://lichess.org/api/account\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://lichess.org/api/token\"\n }\n },\n line: {\n authorizationUrl: \"https://access.line.me/oauth2/v2.1/authorize\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.line.me/v2/profile\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.line.me/oauth2/v2.1/token\"\n }\n },\n linear: {\n authorizationUrl: \"https://linear.app/oauth/authorize\",\n isOIDC: false,\n isRefreshable: false,\n profileRequest: {\n authIn: \"header\",\n body: {\n query: `query { viewer { id name } }`\n },\n encoding: \"application/json\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n method: \"POST\",\n url: \"https://api.linear.app/graphql\"\n },\n revocationRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://api.linear.app/oauth/revoke\"\n },\n scopeRequired: false,\n subject: [\"data\", \"viewer\", \"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.linear.app/oauth/token\"\n }\n },\n linkedin: {\n authorizationUrl: \"https://www.linkedin.com/oauth/v2/authorization\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.linkedin.com/v2/me\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.linkedin.com/oauth/v2/accessToken\"\n }\n },\n mastodon: {\n authorizationUrl: (config) => `${config.baseURL}/oauth/authorize`,\n isOIDC: false,\n isRefreshable: false,\n picture: [\"avatar\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `${config.baseURL}/api/v1/accounts/verify_credentials`\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `${config.baseURL}/oauth/revoke`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `${config.baseURL}/oauth/token`\n }\n },\n mercadolibre: {\n authorizationUrl: \"https://auth.mercadolibre.com/authorization\",\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.mercadolibre.com/users/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.mercadolibre.com/oauth/token\"\n }\n },\n mercadopago: {\n authorizationUrl: \"https://auth.mercadopago.com/authorization\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.mercadopago.com/v1/users/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.mercadopago.com/oauth/token\"\n }\n },\n microsoftentraexternalid: {\n authorizationUrl: (config) => `https://${config.tenantSubdomain}.ciamlogin.com/${config.tenantId}/oauth2/v2.0/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n scopeRequired: false,\n subject: [\"sub\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.tenantSubdomain}.ciamlogin.com/${config.tenantId}/oauth2/v2.0/token`\n }\n },\n microsoftentraid: {\n authorizationUrl: (config) => `https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://graph.microsoft.com/oidc/userinfo\"\n },\n scopeRequired: false,\n subject: [\"sub\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://login.microsoftonline.com/${config.tenantId}/oauth2/v2.0/token`\n }\n },\n monday: {\n authorizationUrl: \"https://auth.monday.com/oauth2/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n body: {\n query: \"query { me { id name email } }\"\n },\n encoding: \"application/json\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n method: \"POST\",\n url: \"https://api.monday.com/v2\"\n },\n scopeRequired: true,\n subject: [\"data\", \"me\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://auth.monday.com/oauth2/token\"\n }\n },\n myanimelist: {\n authorizationUrl: \"https://myanimelist.net/v1/oauth2/authorize\",\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"plain\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.myanimelist.net/v2/users/@me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://myanimelist.net/v1/oauth2/token\"\n }\n },\n naver: {\n authorizationUrl: \"https://nid.naver.com/oauth2.0/authorize\",\n email: [\"response\", \"email\"],\n fullName: [\"response\", \"name\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"response\", \"profile_image\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://openapi.naver.com/v1/nid/me\"\n },\n scopeRequired: false,\n subject: [\"response\", \"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://nid.naver.com/oauth2.0/token\"\n }\n },\n notion: {\n authorizationUrl: \"https://api.notion.com/v1/oauth/authorize\",\n email: [\"bot\", \"owner\", \"user\", \"person\", \"email\"],\n isOIDC: false,\n isRefreshable: false,\n picture: [\"bot\", \"owner\", \"user\", \"avatar_url\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n headers: {\n \"Notion-Version\": \"2022-06-28\"\n },\n method: \"GET\",\n url: \"https://api.notion.com/v1/users/me\"\n },\n scopeRequired: false,\n subject: [\"bot\", \"owner\", \"user\", \"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://api.notion.com/v1/oauth/token\"\n }\n },\n okta: {\n authorizationUrl: (config) => `https://${config.domain}/oauth2/default/v1/authorize`,\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `https://${config.domain}/oauth2/default/v1/userinfo`\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: (config) => `https://${config.domain}/oauth2/default/v1/revoke`\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.domain}/oauth2/default/v1/token`\n }\n },\n onspark: {\n authorizationUrl: \"https://onspark.com/oauth2/authorize\",\n email: [\"email\"],\n familyName: [\"family_name\"],\n fullName: [\"name\"],\n givenName: [\"given_name\"],\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://onspark.com/oauth2/userinfo\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n tokenParamName: \"token\",\n url: \"https://onspark.com/oauth2/revoke\"\n },\n scopeRequired: true,\n subject: [\"sub\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://onspark.com/oauth2/token\"\n }\n },\n osu: {\n authorizationUrl: \"https://osu.ppy.sh/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n picture: [\"avatar_url\"],\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://osu.ppy.sh/api/v2/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://osu.ppy.sh/oauth/token\"\n }\n },\n patreon: {\n authorizationUrl: \"https://www.patreon.com/oauth2/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://www.patreon.com/api/oauth2/v2/identity\"\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.patreon.com/api/oauth2/token\"\n }\n },\n pipedrive: {\n authorizationUrl: \"https://oauth.pipedrive.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.pipedrive.com/v1/users/me\"\n },\n scopeRequired: true,\n subject: [\"data\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth.pipedrive.com/oauth/token\"\n }\n },\n polar: {\n authorizationUrl: \"https://polar.sh/oauth2/authorize\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.polar.sh/v1/oauth2/userinfo\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://api.polar.sh/v1/oauth2/revoke\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.polar.sh/v1/oauth2/token\"\n }\n },\n polaraccesslink: {\n authorizationUrl: \"https://flow.polar.com/oauth2/authorization\",\n isOIDC: false,\n isRefreshable: false,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://www.polaraccesslink.com/v3/users/me\"\n },\n scopeRequired: false,\n subject: [\"x_user_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"header\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://polarremote.com/v2/oauth2/token\"\n }\n },\n polarteampro: {\n authorizationUrl: \"https://auth.polar.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://www.polaraccesslink.com/v3/users/<USER_ID>\"\n },\n scopeRequired: false,\n subject: [\"x_user_id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"header\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://auth.polar.com/oauth/token\"\n }\n },\n reddit: {\n authorizationUrl: \"https://www.reddit.com/api/v1/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://oauth.reddit.com/api/v1/me\"\n },\n revocationRequest: {\n authIn: \"header\",\n body: new URLSearchParams({\n token_type_hint: \"refresh_token\"\n }),\n encoding: \"application/json\",\n url: \"https://www.reddit.com/api/v1/revoke_token\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"header\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.reddit.com/api/v1/access_token\"\n }\n },\n roblox: {\n authorizationUrl: \"https://apis.roblox.com/oauth/v1/authorize\",\n isOIDC: true,\n isRefreshable: true,\n picture: [\"picture\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://apis.roblox.com/oauth/v1/userinfo\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://apis.roblox.com/oauth/v1/token\"\n }\n },\n salesforce: {\n authorizationUrl: \"https://login.salesforce.com/services/oauth2/authorize\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://login.salesforce.com/services/oauth2/userinfo\"\n },\n revocationRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://login.salesforce.com/services/oauth2/revoke\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://login.salesforce.com/services/oauth2/token\"\n }\n },\n shikimori: {\n authorizationUrl: \"https://shikimori.org/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://shikimori.one/api/users/whoami\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://shikimori.org/oauth/token\"\n }\n },\n slack: {\n authorizationUrl: \"https://slack.com/openid/connect/authorize\",\n isOIDC: true,\n isRefreshable: true,\n profileRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://slack.com/api/users.identity\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://slack.com/api/auth.revoke\"\n },\n scopeRequired: true,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://slack.com/api/openid.connect.token\"\n }\n },\n slackuser: {\n accessTokenPath: [\"authed_user\", \"access_token\"],\n authorizationUrl: \"https://slack.com/oauth/v2/authorize\",\n isOIDC: false,\n isRefreshable: false,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"POST\",\n url: \"https://slack.com/api/auth.test\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://slack.com/api/auth.revoke\"\n },\n scopeParamName: \"user_scope\",\n scopeRequired: true,\n subject: [\"user_id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://slack.com/api/oauth.v2.access\"\n }\n },\n spotify: {\n authorizationUrl: \"https://accounts.spotify.com/authorize\",\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.spotify.com/v1/me\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://accounts.spotify.com/api/token\"\n }\n },\n startgg: {\n authorizationUrl: \"https://start.gg/oauth/authorize\",\n email: [\"data\", \"currentUser\", \"email\"],\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n body: {\n query: `query { currentUser { id slug email player { gamerTag } } }`\n },\n encoding: \"application/json\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\"\n },\n method: \"POST\",\n url: \"https://api.start.gg/gql/alpha\"\n },\n scopeRequired: false,\n subject: [\"data\", \"currentUser\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.start.gg/oauth/access_token\"\n }\n },\n strava: {\n authorizationUrl: \"https://www.strava.com/oauth/authorize\",\n familyName: [\"lastname\"],\n givenName: [\"firstname\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"profile\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://www.strava.com/api/v3/athlete\"\n },\n revocationRequest: {\n authIn: \"query\",\n body: new URLSearchParams({\n token_type_hint: \"access_token\"\n }),\n encoding: \"application/json\",\n tokenParamName: \"access_token\",\n url: \"https://www.strava.com/oauth/deauthorize\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://www.strava.com/oauth/token\"\n }\n },\n synology: {\n authorizationUrl: (config) => `${config.baseURL}/webman/sso/SSOOauth.cgi?client_id=${config.clientId}&response_type=code&redirect_uri=${config.redirectUri}`,\n isOIDC: false,\n isRefreshable: false,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `${config.baseURL}/webman/sso/SSOUserInfo.cgi?client_id=${config.clientId}&access_token=${config.accessToken}`\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `${config.baseURL}/webman/sso/SSOAccessToken.cgi?client_id=${config.clientId}&client_secret=${config.clientSecret}`\n }\n },\n tiktok: {\n authorizationUrl: \"https://www.tiktok.com/v2/auth/authorize\",\n createAuthorizationURLSearchParams: (config) => ({\n client_key: config.clientId\n }),\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://open.douyin.com/oauth/userinfo\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"token\",\n url: \"https://open.tiktokapis.com/v2/oauth/revoke/\"\n },\n scopeRequired: false,\n subject: [\"data\", \"open_id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://open.tiktokapis.com/v2/oauth/token/\"\n }\n },\n tiltify: {\n authorizationUrl: \"https://v5api.tiltify.com/oauth/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://v5api.tiltify.com/api/public/current-user\"\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://v5api.tiltify.com/oauth/token\"\n }\n },\n tumblr: {\n authorizationUrl: \"https://www.tumblr.com/oauth2/authorize\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.tumblr.com/v2/user/info\"\n },\n scopeRequired: false,\n subject: [\"response\", \"user\", \"name\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.tumblr.com/v2/oauth2/token\"\n }\n },\n twitch: {\n authorizationUrl: \"https://id.twitch.tv/oauth2/authorize\",\n isOIDC: true,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n headers: (config) => ({\n \"Client-Id\": config.clientId\n }),\n method: \"GET\",\n url: \"https://api.twitch.tv/helix/users\"\n },\n revocationRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n headers: (config) => ({\n \"Client-Id\": config.clientId\n }),\n tokenParamName: \"token\",\n url: \"https://id.twitch.tv/oauth2/revoke\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://id.twitch.tv/oauth2/token\"\n }\n },\n twitter: {\n authorizationUrl: \"https://twitter.com/i/oauth2/authorize\",\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.twitter.com/2/users/me\"\n },\n revocationRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://api.twitter.com/2/oauth2/revoke\"\n },\n scopeRequired: false,\n subject: [\"data\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.twitter.com/2/oauth2/token\"\n }\n },\n vk: {\n authorizationUrl: \"https://oauth.vk.com/authorize\",\n isOIDC: false,\n isRefreshable: false,\n profileRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.vk.com/method/users.get\"\n },\n scopeRequired: false,\n subject: [\"response\", \"0\", \"id\"],\n subjectType: \"number\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth.vk.com/access_token\"\n }\n },\n withings: {\n accessTokenPath: [\"body\", \"access_token\"],\n authorizationUrl: \"https://account.withings.com/oauth2_user/authorize2\",\n isOIDC: false,\n isRefreshable: true,\n profileRequest: {\n authIn: \"header\",\n body: async (config) => {\n const props = await getWithingsProps(config);\n if (props === undefined)\n throw new Error(\"Failed to get Withings search properties\");\n const { nonce, hashedSignature } = props;\n return [\n [\"action\", \"getuser\"],\n [\"nonce\", nonce],\n [\"client_id\", config.clientId],\n [\"signature\", hashedSignature]\n ];\n },\n encoding: \"application/x-www-form-urlencoded\",\n method: \"POST\",\n url: \"https://wbsapi.withings.net/v2/oauth2\"\n },\n refreshAccessTokenBody: {\n action: \"requesttoken\"\n },\n revocationRequest: {\n authIn: \"header\",\n body: async (config) => {\n const props = await getWithingsProps(config);\n if (!props)\n throw new Error(\"Failed to get Withings props\");\n const { nonce, hashedSignature } = props;\n return [\n [\"action\", \"revoke\"],\n [\"client_id\", config.clientId],\n [\"nonce\", nonce],\n [\"signature\", hashedSignature]\n ];\n },\n encoding: \"application/x-www-form-urlencoded\",\n method: \"POST\",\n url: \"https://wbsapi.withings.net/v2/oauth2\"\n },\n scopeDelimiter: \",\",\n scopeRequired: true,\n subject: [\"userid\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://wbsapi.withings.net/v2/oauth2\"\n },\n validateAuthorizationCodeBody: {\n action: \"requesttoken\"\n }\n },\n workos: {\n authorizationUrl: (config) => `https://${config.domain}/oauth2/authorize`,\n createAuthorizationURLSearchParams: () => {\n const nonce = crypto.randomUUID();\n return {\n nonce\n };\n },\n email: [\"email\"],\n familyName: [\"family_name\"],\n fullName: [\"name\"],\n givenName: [\"given_name\"],\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"POST\",\n url: (config) => `https://${config.domain}/oauth2/userinfo`\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://${config.domain}/oauth2/token`\n }\n },\n yahoo: {\n authorizationUrl: \"https://api.login.yahoo.com/oauth2/request_auth\",\n isOIDC: true,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.login.yahoo.com/openid/v1/userinfo\"\n },\n revocationRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n url: \"https://api.login.yahoo.com/oauth2/revoke\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://api.login.yahoo.com/oauth2/get_token\"\n }\n },\n yandex: {\n authorizationUrl: \"https://oauth.yandex.com/authorize\",\n createAuthorizationURLSearchParams: {\n device_id: crypto.randomUUID(),\n device_name: `${navigator.platform ?? \"Unknown\"} \\u2014 ${(navigator.userAgent.split(\")\")[0] || \"\").split(\"(\").pop() || \"Unknown\"}`\n },\n email: [\"default_email\"],\n familyName: [\"last_name\"],\n fullName: [\"real_name\"],\n givenName: [\"first_name\"],\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://login.yandex.ru/info\"\n },\n revocationRequest: {\n authIn: \"body\",\n encoding: \"application/json\",\n tokenParamName: \"access_token\",\n url: \"https://oauth.yandex.com/revoke_token\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://oauth.yandex.com/token\"\n }\n },\n zoho: {\n authorizationUrl: (config) => `https://accounts.zoho.${config.region ?? \"com\"}/oauth/v2/auth`,\n isOIDC: false,\n isRefreshable: true,\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: (config) => `https://accounts.zoho.${config.region ?? \"com\"}/oauth/user/info`\n },\n revocationRequest: {\n authIn: \"query\",\n encoding: \"application/x-www-form-urlencoded\",\n tokenParamName: \"token\",\n url: (config) => `https://accounts.zoho.${config.region ?? \"com\"}/oauth/v2/token/revoke`\n },\n scopeRequired: true,\n subject: [\"ZUID\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: (config) => `https://accounts.zoho.${config.region ?? \"com\"}/oauth/v2/token`\n }\n },\n zoom: {\n authorizationUrl: \"https://zoom.us/oauth/authorize\",\n email: [\"email\"],\n familyName: [\"last_name\"],\n givenName: [\"first_name\"],\n isOIDC: false,\n isRefreshable: true,\n picture: [\"pic_url\"],\n PKCEMethod: \"S256\",\n profileRequest: {\n authIn: \"header\",\n encoding: \"application/json\",\n method: \"GET\",\n url: \"https://api.zoom.us/v2/users/me\"\n },\n revocationRequest: {\n authIn: \"query\",\n encoding: \"application/json\",\n headers: (config) => ({\n Authorization: `Basic ${btoa(`${config.clientId}:${config.clientSecret}`)}`\n }),\n tokenParamName: \"token\",\n url: \"https://zoom.us/oauth/revoke\"\n },\n scopeRequired: false,\n subject: [\"id\"],\n subjectType: \"string\",\n tokenRequest: {\n authIn: \"body\",\n encoding: \"application/x-www-form-urlencoded\",\n url: \"https://zoom.us/oauth/token\"\n }\n }\n});\n\n// src/typeGuards.ts\nvar hasClientSecret = (credentials) => {\n if (typeof credentials !== \"object\" || credentials === null)\n return false;\n const secret = Reflect.get(credentials, \"clientSecret\");\n return typeof secret === \"string\";\n};\nvar isExpectedType = (value, kind) => {\n switch (kind) {\n case \"string\":\n return typeof value === \"string\";\n case \"number\":\n return typeof value === \"number\";\n case \"boolean\":\n return typeof value === \"boolean\";\n case \"object\":\n return typeof value === \"object\" && value !== null;\n default:\n return false;\n }\n};\nvar isObject = (value) => value !== null && typeof value === \"object\" && !Array.isArray(value) && Object.prototype.toString.call(value) === \"[object Object]\";\nvar isOIDCProviderOption = (option) => {\n if (!isValidProviderOption(option))\n return false;\n const provider = providers[option];\n return provider.isOIDC;\n};\nvar isPKCEProviderOption = (option) => {\n if (!isValidProviderOption(option))\n return false;\n const provider = providers[option];\n return provider.PKCEMethod !== undefined;\n};\nvar isRefreshableOAuth2Client = (providerName, _client) => isRefreshableProviderOption(providerName);\nvar isRefreshableProviderOption = (option) => {\n if (!isValidProviderOption(option))\n return false;\n const provider = providers[option];\n return provider.isRefreshable;\n};\nvar isRevocableOAuth2Client = (providerName, _client) => isRevocableProviderOption(providerName);\nvar isRevocableProviderOption = (option) => {\n if (!isValidProviderOption(option))\n return false;\n const provider = providers[option];\n return provider.revocationRequest !== undefined;\n};\nvar isScopeRequiredProviderOption = (option) => {\n if (!isValidProviderOption(option))\n return false;\n const provider = providers[option];\n return provider.scopeRequired;\n};\nvar isValidProviderOption = (option) => Object.hasOwn(providers, option);\n\n// src/utils.ts\nvar createOAuth2FetchError = async (response) => {\n const clone = response.clone();\n const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;\n const payload = await response.json().catch(() => null);\n if (payload && typeof payload === \"object\" && Object.keys(payload).length) {\n return new Error(`${prefix}\n${JSON.stringify(payload)}`);\n }\n const text = await clone.text().catch(() => \"\");\n if (text) {\n return new Error(`${prefix}\n${text}`);\n }\n return new Error(prefix);\n};\nvar createOAuth2Request = ({\n url,\n body,\n authIn,\n headers,\n encoding,\n clientId,\n clientSecret\n}) => {\n const oauthHeaders = new Headers(headers);\n oauthHeaders.set(\"Accept\", \"application/json\");\n oauthHeaders.set(\"User-Agent\", \"citra\");\n if (authIn === \"header\") {\n if (!clientSecret) {\n throw new Error(\"clientSecret required for header auth\");\n }\n oauthHeaders.set(\"Authorization\", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);\n }\n if (encoding === \"application/json\") {\n oauthHeaders.set(\"Content-Type\", \"application/json\");\n return new Request(url, {\n body: JSON.stringify(body),\n headers: oauthHeaders,\n method: \"POST\"\n });\n }\n oauthHeaders.set(\"Content-Type\", \"application/x-www-form-urlencoded\");\n const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === \"string\");\n const params = new URLSearchParams(entries);\n if (authIn === \"body\") {\n params.set(\"client_id\", clientId);\n clientSecret && params.set(\"client_secret\", clientSecret);\n }\n return new Request(url, {\n body: params.toString(),\n headers: oauthHeaders,\n method: \"POST\"\n });\n};\nvar decodeBase64 = (input, toUint8Array = false) => {\n const b64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\") + \"==\".slice(0, (BASE64_BLOCK_SIZE - input.length % BASE64_BLOCK_SIZE) % BASE64_BLOCK_SIZE);\n const raw = atob(b64);\n if (!toUint8Array) {\n return raw;\n }\n const bytes = new Uint8Array(raw.length);\n for (let i = 0;i < raw.length; i++) {\n bytes[i] = raw.charCodeAt(i);\n }\n return bytes;\n};\nvar decodeJWT = (tokenString) => {\n const [headerSegment, payloadSegment, signatureSegment] = tokenString.split(\".\");\n if (!headerSegment || !payloadSegment || !signatureSegment) {\n throw new Error(\"Invalid JWT format\");\n }\n const decodedPayload = decodeBase64(payloadSegment);\n if (typeof decodedPayload !== \"string\") {\n throw new Error(\"Expected JWT payload to be a UTF-8 string\");\n }\n const claims = JSON.parse(decodedPayload);\n return claims;\n};\nvar encodeBase64 = (input) => {\n let raw;\n if (typeof input === \"string\") {\n raw = input;\n } else {\n const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);\n raw = bytes.reduce((acc, byte) => acc + String.fromCharCode(byte), \"\");\n }\n return btoa(raw);\n};\nvar getWithingsProps = async (config) => {\n const timestamp = Math.floor(Date.now() / 1000);\n const signature = `getnonce,${config.clientId},${timestamp}`;\n const hashedSignature = await hmacSha256(signature, config.clientSecret);\n const nonceUrl = new URL(\"https://wbsapi.withings.net/v2/signature\");\n nonceUrl.searchParams.set(\"action\", \"getnonce\");\n nonceUrl.searchParams.set(\"client_id\", config.clientId);\n nonceUrl.searchParams.set(\"timestamp\", timestamp.toString());\n nonceUrl.searchParams.set(\"signature\", hashedSignature);\n const nonceTarget = nonceUrl.toString();\n const nonceResponse = await fetch(nonceTarget, { method: \"POST\" });\n const nonceData = await nonceResponse.json();\n if (nonceData.status === 0) {\n return {\n hashedSignature,\n nonce: nonceData.body.nonce\n };\n }\n return;\n};\nvar hmacSha256 = async (message, secret) => {\n const encoder = new TextEncoder;\n const key = await crypto.subtle.importKey(\"raw\", encoder.encode(secret), { hash: \"SHA-256\", name: \"HMAC\" }, false, [\"sign\"]);\n const sigBuffer = await crypto.subtle.sign(\"HMAC\", key, encoder.encode(message));\n return Array.from(new Uint8Array(sigBuffer)).map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n};\nvar extractPropFromIdentity = (identity, keys, propType) => {\n let value = identity;\n for (const key of keys) {\n if (Array.isArray(value))\n value = value[Number(key)];\n if (!isObject(value)) {\n throw new Error(`Invalid identity data shape: expected object, got ${typeof value}`);\n }\n value = value[key];\n }\n if (propType !== undefined && !isExpectedType(value, propType)) {\n throw new Error(`Invalid identity data shape: expected ${propType}, got ${typeof value}`);\n }\n return value;\n};\nvar getProviderSubjectKeys = (providerConfiguration, source) => providerConfiguration.subjectBySource?.[source] ?? providerConfiguration.subject;\nvar setPropInIdentity = (identity, keys, value) => {\n if (keys.length === 0) {\n return identity;\n }\n let cursor = identity;\n for (const key of keys.slice(0, -1)) {\n const next = cursor[key];\n cursor[key] = isObject(next) ? next : {};\n cursor = cursor[key];\n }\n cursor[keys[keys.length - 1]] = value;\n return identity;\n};\nvar normalizeProviderIdentity = ({\n identity,\n providerConfiguration,\n source\n}) => {\n const sourceKeys = getProviderSubjectKeys(providerConfiguration, source);\n const canonicalKeys = providerConfiguration.subject;\n if (sourceKeys.join(\".\") === canonicalKeys.join(\".\")) {\n return identity;\n }\n const subject = extractPropFromIdentity(identity, sourceKeys, providerConfiguration.subjectType);\n const normalizedIdentity = structuredClone(identity);\n return setPropInIdentity(normalizedIdentity, canonicalKeys, subject);\n};\n\n// src/arctic-utils.ts\nvar createS256CodeChallenge = async (codeVerifier) => {\n const data = new TextEncoder().encode(codeVerifier);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n return base64Url(hashBuffer);\n};\nvar createRandomBase64UrlGenerator = (length) => () => {\n const buffer = crypto.getRandomValues(new Uint8Array(length));\n return base64Url(buffer);\n};\nvar generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);\nvar generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);\nvar base64Url = (input) => encodeBase64(input).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n// src/oidc.ts\nvar ALG_ES256 = \"ES256\";\nvar ALG_RS256 = \"RS256\";\nvar CLOCK_SKEW_SECONDS = 60;\nvar DEFAULT_SCOPES = [\"openid\", \"email\", \"profile\"];\nvar JWKS_REFETCH_COOLDOWN_MS = 60000;\nvar JWT_SEGMENT_COUNT = 3;\nvar MILLISECONDS_PER_SECOND = 1000;\nvar trimTrailingSlash = (value) => value.endsWith(\"/\") ? value.slice(0, -1) : value;\nvar fetchJson = async (url) => {\n const response = await fetch(url, {\n headers: { accept: \"application/json\" }\n });\n if (!response.ok) {\n throw new Error(`Request to ${url} failed with status ${response.status}`);\n }\n return response.json();\n};\nvar decodeJsonSegment = (segment) => {\n const decoded = decodeBase64(segment);\n if (typeof decoded !== \"string\") {\n throw new Error(\"Expected a base64url-encoded JWT segment\");\n }\n const parsed = JSON.parse(decoded);\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new Error(\"Expected a JWT segment to decode to a JSON object\");\n }\n return parsed;\n};\nvar importVerificationKey = (jwk, alg) => {\n if (alg === ALG_RS256) {\n return crypto.subtle.importKey(\"jwk\", jwk, { hash: \"SHA-256\", name: \"RSASSA-PKCS1-v1_5\" }, false, [\"verify\"]);\n }\n if (alg === ALG_ES256) {\n return crypto.subtle.importKey(\"jwk\", jwk, { name: \"ECDSA\", namedCurve: \"P-256\" }, false, [\"verify\"]);\n }\n throw new Error(`Unsupported id_token signing algorithm \"${alg}\"`);\n};\nvar verifySignature = (key, alg, signingInput, signature) => {\n const algorithm = alg === ALG_ES256 ? { hash: \"SHA-256\", name: \"ECDSA\" } : { name: \"RSASSA-PKCS1-v1_5\" };\n return crypto.subtle.verify(algorithm, key, new Uint8Array(signature), new TextEncoder().encode(signingInput));\n};\nvar selectKey = (jwks, kid) => jwks.find((jwk) => kid === undefined || jwk.kid === kid);\nvar assertClaims = (payload, expected) => {\n const aud = Reflect.get(payload, \"aud\");\n const exp = Reflect.get(payload, \"exp\");\n const iss = Reflect.get(payload, \"iss\");\n const sub = Reflect.get(payload, \"sub\");\n const audiences = Array.isArray(aud) ? aud : [aud];\n const nowSeconds = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);\n if (iss !== expected.issuer) {\n throw new Error('id_token \"iss\" does not match the provider issuer');\n }\n if (typeof sub !== \"string\" || sub.length === 0) {\n throw new Error('id_token is missing \"sub\"');\n }\n if (!audiences.includes(expected.audience)) {\n throw new Error('id_token \"aud\" does not include the client id');\n }\n if (typeof exp !== \"number\" || exp + CLOCK_SKEW_SECONDS < nowSeconds) {\n throw new Error(\"id_token has expired\");\n }\n if (expected.nonce !== undefined && Reflect.get(payload, \"nonce\") !== expected.nonce) {\n throw new Error('id_token \"nonce\" does not match');\n }\n const claims = { ...payload, aud, exp, iss, sub };\n return claims;\n};\nvar verifyIdToken = async ({\n audience,\n idToken,\n issuer,\n jwks,\n nonce\n}) => {\n const segments = idToken.split(\".\");\n const [headerSegment, payloadSegment, signatureSegment] = segments;\n if (segments.length !== JWT_SEGMENT_COUNT || headerSegment === undefined || payloadSegment === undefined || signatureSegment === undefined) {\n throw new Error(\"Invalid id_token: expected three segments\");\n }\n const header = decodeJsonSegment(headerSegment);\n const alg = Reflect.get(header, \"alg\");\n const kid = Reflect.get(header, \"kid\");\n if (typeof alg !== \"string\") {\n throw new Error('id_token header is missing \"alg\"');\n }\n const jwk = selectKey(jwks, typeof kid === \"string\" ? kid : undefined);\n if (jwk === undefined) {\n throw new Error('No JWKS key matches the id_token \"kid\"');\n }\n const key = await importVerificationKey(jwk, alg);\n const signature = decodeBase64(signatureSegment, true);\n if (typeof signature === \"string\") {\n throw new Error(\"Failed to decode the id_token signature\");\n }\n const isValid = await verifySignature(key, alg, `${headerSegment}.${payloadSegment}`, signature);\n if (!isValid) {\n throw new Error(\"id_token signature verification failed\");\n }\n return assertClaims(decodeJsonSegment(payloadSegment), {\n audience,\n issuer,\n nonce\n });\n};\nvar toDiscoveryDocument = (value, expectedIssuer) => {\n if (typeof value !== \"object\" || value === null) {\n throw new Error(\"OIDC discovery document is not a JSON object\");\n }\n const issuer = Reflect.get(value, \"issuer\");\n const authorizationEndpoint = Reflect.get(value, \"authorization_endpoint\");\n const tokenEndpoint = Reflect.get(value, \"token_endpoint\");\n const jwksUri = Reflect.get(value, \"jwks_uri\");\n const userinfoEndpoint = Reflect.get(value, \"userinfo_endpoint\");\n if (typeof issuer !== \"string\" || typeof authorizationEndpoint !== \"string\" || typeof tokenEndpoint !== \"string\" || typeof jwksUri !== \"string\") {\n throw new Error(\"OIDC discovery document is missing required endpoints\");\n }\n if (trimTrailingSlash(issuer) !== trimTrailingSlash(expectedIssuer)) {\n throw new Error(`OIDC discovery issuer \"${issuer}\" does not match configured issuer \"${expectedIssuer}\"`);\n }\n return {\n authorization_endpoint: authorizationEndpoint,\n issuer,\n jwks_uri: jwksUri,\n token_endpoint: tokenEndpoint,\n userinfo_endpoint: typeof userinfoEndpoint === \"string\" ? userinfoEndpoint : undefined\n };\n};\nvar fetchDiscoveryDocument = async (issuer) => {\n const base = trimTrailingSlash(issuer);\n const document = await fetchJson(`${base}/.well-known/openid-configuration`);\n return toDiscoveryDocument(document, issuer);\n};\nvar fetchJwks = async (jwksUri) => {\n const body = await fetchJson(jwksUri);\n const value = Reflect.get(body ?? {}, \"keys\");\n if (!Array.isArray(value)) {\n throw new Error('JWKS response is missing a \"keys\" array');\n }\n const keys = value;\n return keys;\n};\nvar createOIDCClient = async (config) => {\n const discovery = await fetchDiscoveryDocument(config.issuer);\n const scopes = config.scopes !== undefined && config.scopes.length > 0 ? config.scopes : DEFAULT_SCOPES;\n let cachedJwks;\n let jwksFetchedAtMs = 0;\n const resolveJwks = async (forceRefresh) => {\n const isStale = Date.now() - jwksFetchedAtMs > JWKS_REFETCH_COOLDOWN_MS;\n if (cachedJwks === undefined || forceRefresh && isStale) {\n cachedJwks = await fetchJwks(discovery.jwks_uri);\n jwksFetchedAtMs = Date.now();\n }\n return cachedJwks;\n };\n const createAuthorizationUrl = async (options) => {\n const url = new URL(discovery.authorization_endpoint);\n const challenge = await createS256CodeChallenge(options.codeVerifier);\n const { searchParams } = url;\n searchParams.set(\"client_id\", config.clientId);\n searchParams.set(\"code_challenge\", challenge);\n searchParams.set(\"code_challenge_method\", \"S256\");\n searchParams.set(\"redirect_uri\", config.redirectUri);\n searchParams.set(\"response_type\", \"code\");\n searchParams.set(\"scope\", (options.scope ?? scopes).join(\" \"));\n searchParams.set(\"state\", options.state);\n if (options.nonce !== undefined) {\n searchParams.set(\"nonce\", options.nonce);\n }\n return url;\n };\n const validateAuthorizationCode = async (options) => {\n const body = new URLSearchParams;\n body.set(\"client_id\", config.clientId);\n body.set(\"client_secret\", config.clientSecret);\n body.set(\"code\", options.code);\n body.set(\"code_verifier\", options.codeVerifier);\n body.set(\"grant_type\", \"authorization_code\");\n body.set(\"redirect_uri\", config.redirectUri);\n const response = await fetch(discovery.token_endpoint, {\n body,\n headers: {\n accept: \"application/json\",\n \"content-type\": \"application/x-www-form-urlencoded\"\n },\n method: \"POST\"\n });\n if (!response.ok) {\n throw new Error(`OIDC token request failed with status ${response.status}`);\n }\n const tokens = await response.json();\n return tokens;\n };\n const verifyToken = async (idToken, options) => {\n const jwks = await resolveJwks(false);\n const params = {\n audience: config.clientId,\n idToken,\n issuer: discovery.issuer,\n nonce: options?.nonce\n };\n try {\n return await verifyIdToken({ ...params, jwks });\n } catch (error) {\n const refreshed = await resolveJwks(true);\n if (refreshed === jwks)\n throw error;\n return verifyIdToken({ ...params, jwks: refreshed });\n }\n };\n const fetchUserProfile = async (accessToken) => {\n if (discovery.userinfo_endpoint === undefined) {\n throw new Error(\"OIDC provider does not expose a userinfo endpoint\");\n }\n const response = await fetch(discovery.userinfo_endpoint, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${accessToken}`\n }\n });\n if (!response.ok) {\n throw new Error(`OIDC userinfo request failed with status ${response.status}`);\n }\n const profile = await response.json();\n return profile;\n };\n return {\n createAuthorizationUrl,\n discovery,\n fetchUserProfile,\n validateAuthorizationCode,\n verifyIdToken: verifyToken\n };\n};\n// src/providerOptions.ts\nvar oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);\nvar pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);\nvar providerOptions = Object.keys(providers).filter(isValidProviderOption);\nvar refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProviderOption);\nvar revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);\nvar scopeRequiredProviderOptions = Object.keys(providers).filter(isScopeRequiredProviderOption);\n\n// src/index.ts\nvar readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === \"object\" ? Reflect.get(cursor, key) : undefined, value);\nvar buildOAuth2Client = async (meta, config) => {\n const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === \"function\";\n const resolveConfigProp = async (cfgProp) => {\n const result = isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;\n return result;\n };\n const authorizationUrl = await resolveConfigProp(meta.authorizationUrl);\n const tokenUrl = await resolveConfigProp(meta.tokenRequest.url);\n return {\n async createAuthorizationUrl(opts) {\n const { state, scope = [], searchParams = [], codeVerifier } = opts;\n const url = new URL(authorizationUrl);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"client_id\", config.clientId);\n if (config.redirectUri)\n url.searchParams.set(\"redirect_uri\", config.redirectUri);\n if (state)\n url.searchParams.set(\"state\", state);\n if (scope.length !== 0) {\n url.searchParams.set(meta.scopeParamName ?? \"scope\", scope.join(meta.scopeDelimiter ?? \" \"));\n }\n if (meta.PKCEMethod !== undefined) {\n if (!codeVerifier) {\n throw new Error(\"`codeVerifier` is required when PKCE is enabled\");\n }\n const codeChallenge = meta.PKCEMethod === \"S256\" ? await createS256CodeChallenge(codeVerifier) : codeVerifier;\n url.searchParams.set(\"code_challenge_method\", meta.PKCEMethod);\n url.searchParams.set(\"code_challenge\", codeChallenge);\n }\n Object.entries(resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));\n searchParams.forEach(([key, value]) => url.searchParams.set(key, value));\n return url;\n },\n async fetchUserProfile(accessToken) {\n const { profileRequest } = meta;\n if (!profileRequest) {\n throw new Error(\"OIDC provider exposes identity through the id_token and does not define a UserInfo endpoint\");\n }\n const {\n url,\n method,\n authIn,\n searchParams,\n body: profileBody,\n headers,\n encoding\n } = profileRequest;\n const endpoint = new URL(await resolveConfigProp(url));\n const resolvedBody = await resolveConfigProp(profileBody);\n new URLSearchParams(await resolveConfigProp(searchParams)).forEach((value, key) => endpoint.searchParams.append(key, value));\n let headerEntries = [];\n const rawHeaders = headers ? await resolveConfigProp(headers) : undefined;\n if (rawHeaders instanceof Headers)\n headerEntries = Array.from(rawHeaders.entries());\n else if (Array.isArray(rawHeaders))\n headerEntries = rawHeaders;\n else if (rawHeaders && typeof rawHeaders === \"object\")\n headerEntries = Object.entries(rawHeaders);\n const profileHeaders = Object.fromEntries(headerEntries.filter(([, value]) => value !== \"\"));\n if (authIn === \"header\") {\n profileHeaders.Authorization = `Bearer ${accessToken}`;\n } else if (authIn === \"path\") {\n endpoint.pathname = `${endpoint.pathname.replace(/\\/+$/, \"\")}/${accessToken}`;\n } else {\n endpoint.searchParams.append(\"access_token\", accessToken);\n }\n const init = { headers: profileHeaders, method };\n if (method === \"POST\" && resolvedBody !== undefined) {\n profileHeaders[\"Content-Type\"] = encoding;\n init.body = encoding === \"application/json\" ? JSON.stringify(resolvedBody) : new URLSearchParams(resolvedBody).toString();\n }\n const profileTarget = endpoint.toString();\n const response = await fetch(profileTarget, init);\n if (!response.ok)\n throw await createOAuth2FetchError(response);\n return response.json();\n },\n async refreshAccessToken(refreshToken) {\n const { authIn, encoding } = meta.tokenRequest;\n const params = new URLSearchParams(meta.refreshAccessTokenBody);\n params.set(\"grant_type\", \"refresh_token\");\n params.set(\"refresh_token\", refreshToken);\n const { clientId } = config;\n let clientSecretValue;\n if (hasClientSecret(config)) {\n clientSecretValue = config.clientSecret;\n params.set(\"client_id\", clientId);\n params.set(\"client_secret\", clientSecretValue);\n }\n const request = createOAuth2Request({\n authIn,\n body: params,\n clientId,\n clientSecret: clientSecretValue,\n encoding,\n url: tokenUrl\n });\n const response = await fetch(request);\n if (!response.ok)\n throw await createOAuth2FetchError(response);\n return response.json();\n },\n async revokeToken(token) {\n const { revocationRequest } = meta;\n if (!revocationRequest) {\n throw new Error(\"Token revocation not defined for this provider\");\n }\n const { url, authIn, body, headers, tokenParamName } = revocationRequest;\n const endpoint = await resolveConfigProp(url);\n const resolvedBody = await resolveConfigProp(body);\n const revocationBody = new URLSearchParams(resolvedBody);\n const revocationHeaders = new Headers(headers && await resolveConfigProp(headers));\n const { clientId } = config;\n const clientSecret = hasClientSecret(config) ? config.clientSecret : undefined;\n let request;\n if (authIn === \"body\") {\n revocationBody.set(tokenParamName, token);\n revocationBody.set(\"client_id\", clientId);\n if (clientSecret)\n revocationBody.set(\"client_secret\", clientSecret);\n request = createOAuth2Request({\n authIn: \"body\",\n body: revocationBody,\n clientId,\n clientSecret,\n encoding: \"application/x-www-form-urlencoded\",\n headers: revocationHeaders,\n url: endpoint.toString()\n });\n } else if (authIn === \"header\") {\n revocationHeaders.set(\"Authorization\", `Bearer ${token}`);\n request = createOAuth2Request({\n authIn: \"header\",\n body: revocationBody,\n clientId,\n clientSecret,\n encoding: \"application/x-www-form-urlencoded\",\n headers: revocationHeaders,\n url: endpoint.toString()\n });\n } else {\n request = createOAuth2Request({\n authIn: \"query\",\n body: revocationBody,\n clientId,\n clientSecret,\n encoding: \"application/x-www-form-urlencoded\",\n headers: revocationHeaders,\n url: `${endpoint.toString()}?${tokenParamName}=${token}`\n });\n }\n const response = await fetch(request);\n if (!response.ok)\n throw await createOAuth2FetchError(response);\n },\n async validateAuthorizationCode(opts) {\n const { code, codeVerifier } = opts;\n const { authIn, encoding } = meta.tokenRequest;\n const bodyObj = {};\n for (const key in meta.validateAuthorizationCodeBody ?? {}) {\n const value = meta.validateAuthorizationCodeBody[key];\n if (typeof value === \"string\")\n bodyObj[key] = value;\n }\n bodyObj.grant_type = \"authorization_code\";\n bodyObj.code = code;\n if (config.redirectUri)\n bodyObj.redirect_uri = config.redirectUri;\n if (meta.PKCEMethod !== undefined) {\n if (!codeVerifier) {\n throw new Error(\"codeVerifier required when PKCE is enabled\");\n }\n bodyObj.code_verifier = codeVerifier;\n }\n const payload = encoding === \"application/json\" ? bodyObj : new URLSearchParams(bodyObj);\n const request = createOAuth2Request({\n authIn,\n body: payload,\n clientId: config.clientId,\n clientSecret: hasClientSecret(config) ? config.clientSecret : undefined,\n encoding,\n url: tokenUrl\n });\n const response = await fetch(request);\n if (!response.ok)\n throw await createOAuth2FetchError(response);\n const tokenResponse = await response.json();\n if (!tokenResponse || typeof tokenResponse !== \"object\") {\n throw new Error(\"OAuth token endpoint returned a non-object response\");\n }\n const oauthError = Reflect.get(tokenResponse, \"error\");\n if (typeof oauthError === \"string\" && oauthError.length > 0) {\n throw new Error(`OAuth token exchange failed: ${oauthError}`);\n }\n const nestedToken = meta.accessTokenPath ? readPath(tokenResponse, meta.accessTokenPath) : undefined;\n if (typeof nestedToken === \"string\" && nestedToken.length > 0 && tokenResponse && typeof tokenResponse === \"object\") {\n tokenResponse.access_token = nestedToken;\n }\n const accessToken = Reflect.get(tokenResponse, \"access_token\");\n if (typeof accessToken !== \"string\" || accessToken.length === 0) {\n throw new Error(\"OAuth token endpoint returned no access_token\");\n }\n return tokenResponse;\n }\n };\n};\nvar createCustomOAuth2Client = (providerConfig, credentials) => buildOAuth2Client(providerConfig, credentials);\nvar createOAuth2Client = (providerName, config) => buildOAuth2Client(providers[providerName], config);\nexport {\n verifyIdToken,\n scopeRequiredProviderOptions,\n revocableProviderOptions,\n refreshableProviderOptions,\n providers,\n providerOptions,\n pkceProviderOptions,\n oidcProviderOptions,\n normalizeProviderIdentity,\n isValidProviderOption,\n isScopeRequiredProviderOption,\n isRevocableProviderOption,\n isRevocableOAuth2Client,\n isRefreshableProviderOption,\n isRefreshableOAuth2Client,\n isPKCEProviderOption,\n isObject,\n isOIDCProviderOption,\n isExpectedType,\n hmacSha256,\n hasClientSecret,\n getWithingsProps,\n getProviderSubjectKeys,\n generateState,\n generateCodeVerifier,\n extractPropFromIdentity,\n encodeBase64,\n defineProviders,\n defineProvider,\n decodeJWT,\n decodeBase64,\n createS256CodeChallenge,\n createOIDCClient,\n createOAuth2Request,\n createOAuth2FetchError,\n createOAuth2Client,\n createCustomOAuth2Client\n};\n",
20
20
  "import { eq } from 'drizzle-orm';\nimport { bigint, integer, pgTable, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type { LockoutRecord, LockoutStore } from './types';\n\nconst KEY_LENGTH = 320;\n\nexport const lockoutsTable = pgTable('auth_lockouts', {\n\tfailed_attempts: integer('failed_attempts').notNull().default(0),\n\tkey: varchar('key', { length: KEY_LENGTH }).primaryKey(),\n\tlocked_until_ms: bigint('locked_until_ms', { mode: 'number' }),\n\twindow_started_at_ms: bigint('window_started_at_ms', {\n\t\tmode: 'number'\n\t}).notNull()\n});\n\ntype LockoutRow = typeof lockoutsTable.$inferSelect;\ntype LockoutInsert = typeof lockoutsTable.$inferInsert;\n\nconst toRecord = (row: LockoutRow): LockoutRecord => ({\n\tfailedAttempts: row.failed_attempts,\n\tkey: row.key,\n\tlockedUntil: row.locked_until_ms ?? undefined,\n\twindowStartedAt: row.window_started_at_ms\n});\n\nexport const createNeonLockoutStore = (databaseUrl: string) =>\n\tcreatePostgresLockoutStore(createNeonDatabase(databaseUrl));\nexport const createPostgresLockoutStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): LockoutStore => {\n\tconst get = async (key: string) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(lockoutsTable)\n\t\t\t.where(eq(lockoutsTable.key, key))\n\t\t\t.limit(1);\n\n\t\treturn row ? toRecord(row) : undefined;\n\t};\n\tconst save = async (record: LockoutRecord) => {\n\t\tconst values: LockoutInsert = {\n\t\t\tfailed_attempts: record.failedAttempts,\n\t\t\tkey: record.key,\n\t\t\tlocked_until_ms: record.lockedUntil ?? null,\n\t\t\twindow_started_at_ms: record.windowStartedAt\n\t\t};\n\t\tawait db\n\t\t\t.insert(lockoutsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({ set: values, target: lockoutsTable.key });\n\t};\n\n\treturn {\n\t\tget,\n\t\tincrement: async (key, windowMs) => {\n\t\t\tconst now = Date.now();\n\t\t\tconst existing = await get(key);\n\t\t\tconst next: LockoutRecord =\n\t\t\t\texisting !== undefined &&\n\t\t\t\tnow - existing.windowStartedAt <= windowMs\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...existing,\n\t\t\t\t\t\t\tfailedAttempts: existing.failedAttempts + 1\n\t\t\t\t\t\t}\n\t\t\t\t\t: { failedAttempts: 1, key, windowStartedAt: now };\n\t\t\tawait save(next);\n\n\t\t\treturn next;\n\t\t},\n\t\tlock: async (key, lockedUntil) => {\n\t\t\tconst existing = (await get(key)) ?? {\n\t\t\t\tfailedAttempts: 0,\n\t\t\t\tkey,\n\t\t\t\twindowStartedAt: Date.now()\n\t\t\t};\n\t\t\tawait save({ ...existing, lockedUntil });\n\t\t},\n\t\treset: async (key) => {\n\t\t\tawait db.delete(lockoutsTable).where(eq(lockoutsTable.key, key));\n\t\t}\n\t};\n};\n",
21
21
  "import { eq } from 'drizzle-orm';\nimport {\n\tbigint,\n\tboolean,\n\tjsonb,\n\tpgTable,\n\tsmallint,\n\ttext,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type { MfaEnrollment, MFAStore } from './types';\n\nconst ID_LENGTH = 255;\nconst PHONE_LENGTH = 20;\n\nexport const mfaEnrollmentsTable = pgTable('auth_mfa_enrollments', {\n\tbackup_code_hashes: jsonb('backup_code_hashes')\n\t\t.$type<string[]>()\n\t\t.notNull()\n\t\t.default([]),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tlast_used_at_ms: bigint('last_used_at_ms', { mode: 'number' }),\n\tsms_failed_attempts: smallint('sms_failed_attempts').notNull().default(0),\n\tsms_pending_code_expires_at_ms: bigint('sms_pending_code_expires_at_ms', {\n\t\tmode: 'number'\n\t}),\n\tsms_pending_code_hash: text('sms_pending_code_hash'),\n\tsms_phone: varchar('sms_phone', { length: PHONE_LENGTH }),\n\tsms_verified: boolean('sms_verified').notNull().default(false),\n\ttotp_failed_attempts: smallint('totp_failed_attempts').notNull().default(0),\n\ttotp_secret_ciphertext: text('totp_secret_ciphertext'),\n\ttotp_verified: boolean('totp_verified').notNull().default(false),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).primaryKey()\n});\n\ntype MfaRow = typeof mfaEnrollmentsTable.$inferSelect;\ntype MfaInsert = typeof mfaEnrollmentsTable.$inferInsert;\n\nconst toEnrollment = (row: MfaRow): MfaEnrollment => ({\n\tbackupCodeHashes: row.backup_code_hashes,\n\tcreatedAt: row.created_at_ms,\n\tlastUsedAt: row.last_used_at_ms ?? undefined,\n\tsmsFailedAttempts: row.sms_failed_attempts,\n\tsmsPendingCodeExpiresAt: row.sms_pending_code_expires_at_ms ?? undefined,\n\tsmsPendingCodeHash: row.sms_pending_code_hash ?? undefined,\n\tsmsPhone: row.sms_phone ?? undefined,\n\tsmsVerified: row.sms_verified,\n\ttotpFailedAttempts: row.totp_failed_attempts,\n\ttotpSecretCiphertext: row.totp_secret_ciphertext ?? undefined,\n\ttotpVerified: row.totp_verified,\n\tupdatedAt: row.updated_at_ms,\n\tuserId: row.user_id\n});\n\nexport const createNeonMfaStore = (databaseUrl: string) =>\n\tcreatePostgresMfaStore(createNeonDatabase(databaseUrl));\nexport const createPostgresMfaStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): MFAStore => ({\n\tgetEnrollment: async (userId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(mfaEnrollmentsTable)\n\t\t\t.where(eq(mfaEnrollmentsTable.user_id, userId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toEnrollment(row) : undefined;\n\t},\n\tlistEnrollments: async () => {\n\t\tconst rows = await db.select().from(mfaEnrollmentsTable);\n\n\t\treturn rows.map(toEnrollment);\n\t},\n\tremoveEnrollment: async (userId) => {\n\t\tawait db\n\t\t\t.delete(mfaEnrollmentsTable)\n\t\t\t.where(eq(mfaEnrollmentsTable.user_id, userId));\n\t},\n\tsaveEnrollment: async (enrollment) => {\n\t\tconst values: MfaInsert = {\n\t\t\tbackup_code_hashes: enrollment.backupCodeHashes,\n\t\t\tcreated_at_ms: enrollment.createdAt,\n\t\t\tlast_used_at_ms: enrollment.lastUsedAt ?? null,\n\t\t\tsms_failed_attempts: enrollment.smsFailedAttempts ?? 0,\n\t\t\tsms_pending_code_expires_at_ms:\n\t\t\t\tenrollment.smsPendingCodeExpiresAt ?? null,\n\t\t\tsms_pending_code_hash: enrollment.smsPendingCodeHash ?? null,\n\t\t\tsms_phone: enrollment.smsPhone ?? null,\n\t\t\tsms_verified: enrollment.smsVerified,\n\t\t\ttotp_failed_attempts: enrollment.totpFailedAttempts ?? 0,\n\t\t\ttotp_secret_ciphertext: enrollment.totpSecretCiphertext ?? null,\n\t\t\ttotp_verified: enrollment.totpVerified,\n\t\t\tupdated_at_ms: enrollment.updatedAt,\n\t\t\tuser_id: enrollment.userId\n\t\t};\n\t\tawait db.insert(mfaEnrollmentsTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: mfaEnrollmentsTable.user_id\n\t\t});\n\t}\n});\n",
22
- "import { and, desc, eq, gt, lt } from 'drizzle-orm';\nimport {\n\tbigint,\n\tboolean,\n\tjsonb,\n\tpgTable,\n\ttext,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tAuthorizationCode,\n\tAuthorizationCodeStore,\n\tBackchannelAuthRequest,\n\tBackchannelAuthStatus,\n\tBackchannelAuthStore,\n\tClientAssertionJtiStore,\n\tClientRegistrationToken,\n\tClientRegistrationTokenStore,\n\tDeviceAuthorization,\n\tDeviceAuthorizationStatus,\n\tDeviceAuthorizationStore,\n\tInitialAccessTokenStore,\n\tLogoutDelivery,\n\tLogoutDeliveryStore,\n\tOAuthClient,\n\tOAuthClientStore,\n\tOidcRefreshToken,\n\tOidcRefreshTokenStore,\n\tPushedAuthorizationRequest,\n\tPushedAuthorizationRequestStore\n} from './types';\n\nconst URL_LENGTH = 2048;\nconst DEFAULT_LIST_LIMIT = 100;\n\nconst ID_LENGTH = 255;\nconst BACKCHANNEL_AUTH_STATUSES = new Set<BackchannelAuthStatus>([\n\t'approved',\n\t'denied',\n\t'pending'\n]);\nconst DEVICE_AUTHORIZATION_STATUSES = new Set<DeviceAuthorizationStatus>([\n\t'approved',\n\t'denied',\n\t'pending'\n]);\n\nconst backchannelAuthStatus = (value: string) => {\n\tfor (const status of BACKCHANNEL_AUTH_STATUSES)\n\t\tif (status === value) return status;\n\n\tthrow new Error(\n\t\t`Invalid persisted backchannel authorization status: ${value}`\n\t);\n};\n\nconst deviceAuthorizationStatus = (value: string) => {\n\tfor (const status of DEVICE_AUTHORIZATION_STATUSES)\n\t\tif (status === value) return status;\n\n\tthrow new Error(`Invalid persisted device authorization status: ${value}`);\n};\n\nexport const oauthBackchannelAuthRequestsTable = pgTable(\n\t'auth_oauth_backchannel_auth_requests',\n\t{\n\t\tauth_req_id: varchar('auth_req_id', { length: ID_LENGTH }).primaryKey(),\n\t\tbinding_message: text('binding_message'),\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tinterval_seconds: bigint('interval_seconds', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tlast_polled_at_ms: bigint('last_polled_at_ms', { mode: 'number' }),\n\t\tscopes: text('scopes').array().notNull(),\n\t\tstatus: varchar('status', { length: 16 }).notNull(),\n\t\tuser_sub: varchar('user_sub', { length: ID_LENGTH })\n\t}\n);\nexport const oauthClientAssertionJtisTable = pgTable(\n\t'auth_oauth_client_assertion_jtis',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcomposite_key: varchar('composite_key', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tjti: varchar('jti', { length: ID_LENGTH }).notNull()\n\t}\n);\nexport const oauthClientRegistrationTokensTable = pgTable(\n\t'auth_oauth_client_registration_tokens',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey()\n\t}\n);\nexport const oauthClientsTable = pgTable('auth_oauth_clients', {\n\tbackchannel_logout_uri: varchar('backchannel_logout_uri', {\n\t\tlength: URL_LENGTH\n\t}),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).primaryKey(),\n\thashed_secret: varchar('hashed_secret', { length: ID_LENGTH }),\n\tjwks_json: jsonb('jwks_json').$type<JsonWebKey[]>(),\n\tjwks_uri: varchar('jwks_uri', { length: URL_LENGTH }),\n\tname: varchar('name', { length: ID_LENGTH }).notNull(),\n\tpost_logout_redirect_uris: text('post_logout_redirect_uris').array(),\n\tredirect_uris: text('redirect_uris').array().notNull(),\n\trequire_pushed_authorization_requests: boolean(\n\t\t'require_pushed_authorization_requests'\n\t),\n\trequire_signed_request_object: boolean('require_signed_request_object'),\n\tscopes: text('scopes').array().notNull()\n});\nexport const oauthCodesTable = pgTable('auth_oauth_codes', {\n\tacr: varchar('acr', { length: ID_LENGTH }),\n\taudience: varchar('audience', { length: URL_LENGTH }),\n\tclaims_json: jsonb('claims_json').$type<Record<string, unknown>>(),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\tcode_challenge: varchar('code_challenge', { length: ID_LENGTH }).notNull(),\n\tcode_hash: varchar('code_hash', { length: ID_LENGTH }).primaryKey(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdpop_jkt: varchar('dpop_jkt', { length: ID_LENGTH }),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\tnonce: varchar('nonce', { length: ID_LENGTH }),\n\tredirect_uri: varchar('redirect_uri', { length: ID_LENGTH }).notNull(),\n\tscopes: text('scopes').array().notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\nexport const oauthDeviceAuthorizationsTable = pgTable(\n\t'auth_oauth_device_authorizations',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\tdevice_code_hash: varchar('device_code_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tinterval_seconds: bigint('interval_seconds', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tscopes: text('scopes').array().notNull(),\n\t\tstatus: varchar('status', { length: 16 }).notNull(),\n\t\tuser_code: varchar('user_code', { length: 16 }).notNull().unique(),\n\t\tuser_sub: varchar('user_sub', { length: ID_LENGTH })\n\t}\n);\nexport const oauthInitialAccessTokensTable = pgTable(\n\t'auth_oauth_initial_access_tokens',\n\t{\n\t\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey()\n\t}\n);\nexport const oauthLogoutDeliveriesTable = pgTable(\n\t'auth_oauth_logout_deliveries',\n\t{\n\t\tattempts: bigint('attempts', { mode: 'number' }).notNull(),\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\tendpoint_url: varchar('endpoint_url', { length: URL_LENGTH }).notNull(),\n\t\tid: varchar('id', { length: ID_LENGTH }).primaryKey(),\n\t\tlast_error: text('last_error'),\n\t\tlast_status: bigint('last_status', { mode: 'number' }),\n\t\tlogout_token: text('logout_token').notNull(),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n\t}\n);\nexport const oauthPushedAuthorizationRequestsTable = pgTable(\n\t'auth_oauth_pushed_authorization_requests',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tparams_json: jsonb('params_json')\n\t\t\t.$type<Record<string, string>>()\n\t\t\t.notNull(),\n\t\trequest_uri_hash: varchar('request_uri_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey()\n\t}\n);\nexport const oauthRefreshTokensTable = pgTable('auth_oauth_refresh_tokens', {\n\tacr: varchar('acr', { length: ID_LENGTH }),\n\taudience: varchar('audience', { length: URL_LENGTH }),\n\tclaims_json: jsonb('claims_json').$type<Record<string, unknown>>(),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdpop_jkt: varchar('dpop_jkt', { length: ID_LENGTH }),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\tscopes: text('scopes').array().notNull(),\n\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\n\ntype ClientRow = typeof oauthClientsTable.$inferSelect;\ntype CodeRow = typeof oauthCodesTable.$inferSelect;\ntype DeviceAuthRow = typeof oauthDeviceAuthorizationsTable.$inferSelect;\ntype LogoutDeliveryRow = typeof oauthLogoutDeliveriesTable.$inferSelect;\ntype RefreshRow = typeof oauthRefreshTokensTable.$inferSelect;\n\nconst toClient = (row: ClientRow): OAuthClient => ({\n\tbackchannelLogoutUri: row.backchannel_logout_uri ?? undefined,\n\tclientId: row.client_id,\n\thashedSecret: row.hashed_secret ?? undefined,\n\tjwks: row.jwks_json ?? undefined,\n\tjwksUri: row.jwks_uri ?? undefined,\n\tname: row.name,\n\tpostLogoutRedirectUris: row.post_logout_redirect_uris ?? undefined,\n\tredirectUris: row.redirect_uris,\n\trequirePushedAuthorizationRequests:\n\t\trow.require_pushed_authorization_requests ?? undefined,\n\trequireSignedRequestObject: row.require_signed_request_object ?? undefined,\n\tscopes: row.scopes\n});\n\nconst toLogoutDelivery = (row: LogoutDeliveryRow): LogoutDelivery => ({\n\tattempts: row.attempts,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tendpointUrl: row.endpoint_url,\n\tid: row.id,\n\tlastError: row.last_error ?? undefined,\n\tlastStatus: row.last_status ?? undefined,\n\tlogoutToken: row.logout_token,\n\tuserId: row.user_id\n});\n\nconst toCode = (row: CodeRow): AuthorizationCode => ({\n\tacr: row.acr ?? undefined,\n\taudience: row.audience ?? undefined,\n\tclaims: row.claims_json ?? undefined,\n\tclientId: row.client_id,\n\tcodeChallenge: row.code_challenge,\n\tcodeHash: row.code_hash,\n\tcreatedAt: row.created_at_ms,\n\tdpopJkt: row.dpop_jkt ?? undefined,\n\texpiresAt: row.expires_at_ms,\n\tnonce: row.nonce ?? undefined,\n\tredirectUri: row.redirect_uri,\n\tscopes: row.scopes,\n\tuserId: row.user_id\n});\n\nconst toCodeValues = (\n\tcode: AuthorizationCode\n): typeof oauthCodesTable.$inferInsert => ({\n\tacr: code.acr ?? null,\n\taudience: code.audience ?? null,\n\tclaims_json: code.claims ?? null,\n\tclient_id: code.clientId,\n\tcode_challenge: code.codeChallenge,\n\tcode_hash: code.codeHash,\n\tcreated_at_ms: code.createdAt,\n\tdpop_jkt: code.dpopJkt ?? null,\n\texpires_at_ms: code.expiresAt,\n\tnonce: code.nonce ?? null,\n\tredirect_uri: code.redirectUri,\n\tscopes: code.scopes,\n\tuser_id: code.userId\n});\n\nconst toDeviceAuth = (row: DeviceAuthRow): DeviceAuthorization => ({\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tdeviceCodeHash: row.device_code_hash,\n\texpiresAt: row.expires_at_ms,\n\tintervalSeconds: row.interval_seconds,\n\tscopes: row.scopes,\n\tstatus: deviceAuthorizationStatus(row.status),\n\tuserCode: row.user_code,\n\tuserSub: row.user_sub ?? undefined\n});\n\nconst toRefresh = (row: RefreshRow): OidcRefreshToken => ({\n\tacr: row.acr ?? undefined,\n\taudience: row.audience ?? undefined,\n\tclaims: row.claims_json ?? undefined,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tdpopJkt: row.dpop_jkt ?? undefined,\n\texpiresAt: row.expires_at_ms,\n\tscopes: row.scopes,\n\ttokenHash: row.token_hash,\n\tuserId: row.user_id\n});\n\nconst toRefreshValues = (\n\ttoken: OidcRefreshToken\n): typeof oauthRefreshTokensTable.$inferInsert => ({\n\tacr: token.acr ?? null,\n\taudience: token.audience ?? null,\n\tclaims_json: token.claims ?? null,\n\tclient_id: token.clientId,\n\tcreated_at_ms: token.createdAt,\n\tdpop_jkt: token.dpopJkt ?? null,\n\texpires_at_ms: token.expiresAt,\n\tscopes: token.scopes,\n\ttoken_hash: token.tokenHash,\n\tuser_id: token.userId\n});\n\nexport const createNeonAuthorizationCodeStore = (databaseUrl: string) =>\n\tcreatePostgresAuthorizationCodeStore(createNeonDatabase(databaseUrl));\nexport const createNeonClientAssertionJtiStore = (databaseUrl: string) =>\n\tcreatePostgresClientAssertionJtiStore(createNeonDatabase(databaseUrl));\nexport const createNeonClientRegistrationTokenStore = (databaseUrl: string) =>\n\tcreatePostgresClientRegistrationTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonDeviceAuthorizationStore = (databaseUrl: string) =>\n\tcreatePostgresDeviceAuthorizationStore(createNeonDatabase(databaseUrl));\nexport const createNeonInitialAccessTokenStore = (databaseUrl: string) =>\n\tcreatePostgresInitialAccessTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonLogoutDeliveryStore = (databaseUrl: string) =>\n\tcreatePostgresLogoutDeliveryStore(createNeonDatabase(databaseUrl));\nexport const createNeonOAuthClientStore = (databaseUrl: string) =>\n\tcreatePostgresOAuthClientStore(createNeonDatabase(databaseUrl));\nexport const createNeonOidcRefreshTokenStore = (databaseUrl: string) =>\n\tcreatePostgresOidcRefreshTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonPushedAuthorizationRequestStore = (\n\tdatabaseUrl: string\n) =>\n\tcreatePostgresPushedAuthorizationRequestStore(\n\t\tcreateNeonDatabase(databaseUrl)\n\t);\nexport const createPostgresAuthorizationCodeStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AuthorizationCodeStore => ({\n\tconsumeCode: async (codeHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthCodesTable)\n\t\t\t.where(eq(oauthCodesTable.code_hash, codeHash))\n\t\t\t.returning();\n\n\t\treturn row ? toCode(row) : undefined;\n\t},\n\tsaveCode: async (code) => {\n\t\tawait db.insert(oauthCodesTable).values(toCodeValues(code));\n\t}\n});\nexport const createPostgresClientAssertionJtiStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): ClientAssertionJtiStore => ({\n\trecordIfFresh: async (clientId, jti, expiresAt) => {\n\t\t// Lazy GC of expired entries on each call. Bounded by the assertion `exp` window\n\t\t// (~5 min), so this stays cheap even at high call volume.\n\t\tawait db\n\t\t\t.delete(oauthClientAssertionJtisTable)\n\t\t\t.where(lt(oauthClientAssertionJtisTable.expires_at_ms, Date.now()));\n\t\tconst compositeKey = `${clientId}|${jti}`;\n\t\ttry {\n\t\t\tawait db.insert(oauthClientAssertionJtisTable).values({\n\t\t\t\tclient_id: clientId,\n\t\t\t\tcomposite_key: compositeKey,\n\t\t\t\texpires_at_ms: expiresAt,\n\t\t\t\tjti\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch {\n\t\t\t// PK collision = we've seen this jti for this client before = replay.\n\t\t\treturn false;\n\t\t}\n\t}\n});\nexport const createPostgresClientRegistrationTokenStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): ClientRegistrationTokenStore => ({\n\tdeleteByClientId: async (clientId) => {\n\t\tawait db\n\t\t\t.delete(oauthClientRegistrationTokensTable)\n\t\t\t.where(eq(oauthClientRegistrationTokensTable.client_id, clientId));\n\t},\n\tfindByTokenHash: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthClientRegistrationTokensTable)\n\t\t\t.where(eq(oauthClientRegistrationTokensTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\t\tif (!row) return undefined;\n\t\tconst token: ClientRegistrationToken = {\n\t\t\tclientId: row.client_id,\n\t\t\tcreatedAt: row.created_at_ms,\n\t\t\ttokenHash: row.token_hash\n\t\t};\n\n\t\treturn token;\n\t},\n\tsaveToken: async (token) => {\n\t\t// One reg token per client — drop any prior token before saving.\n\t\tawait db\n\t\t\t.delete(oauthClientRegistrationTokensTable)\n\t\t\t.where(\n\t\t\t\teq(oauthClientRegistrationTokensTable.client_id, token.clientId)\n\t\t\t);\n\t\tawait db.insert(oauthClientRegistrationTokensTable).values({\n\t\t\tclient_id: token.clientId,\n\t\t\tcreated_at_ms: token.createdAt,\n\t\t\ttoken_hash: token.tokenHash\n\t\t});\n\t}\n});\nexport const createPostgresDeviceAuthorizationStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): DeviceAuthorizationStore => ({\n\tdeleteByDeviceCodeHash: async (deviceCodeHash) => {\n\t\tawait db\n\t\t\t.delete(oauthDeviceAuthorizationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t);\n\t},\n\tfindByDeviceCodeHash: async (deviceCodeHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthDeviceAuthorizationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row ? toDeviceAuth(row) : undefined;\n\t},\n\tfindByUserCode: async (userCode) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthDeviceAuthorizationsTable)\n\t\t\t.where(eq(oauthDeviceAuthorizationsTable.user_code, userCode))\n\t\t\t.limit(1);\n\n\t\treturn row ? toDeviceAuth(row) : undefined;\n\t},\n\tsaveDeviceAuthorization: async (deviceAuthorization) => {\n\t\tawait db.insert(oauthDeviceAuthorizationsTable).values({\n\t\t\tclient_id: deviceAuthorization.clientId,\n\t\t\tcreated_at_ms: deviceAuthorization.createdAt,\n\t\t\tdevice_code_hash: deviceAuthorization.deviceCodeHash,\n\t\t\texpires_at_ms: deviceAuthorization.expiresAt,\n\t\t\tinterval_seconds: deviceAuthorization.intervalSeconds,\n\t\t\tscopes: deviceAuthorization.scopes,\n\t\t\tstatus: deviceAuthorization.status,\n\t\t\tuser_code: deviceAuthorization.userCode,\n\t\t\tuser_sub: deviceAuthorization.userSub ?? null\n\t\t});\n\t},\n\tupdateStatus: async (deviceCodeHash, status, userSub) => {\n\t\tawait db\n\t\t\t.update(oauthDeviceAuthorizationsTable)\n\t\t\t.set({ status, user_sub: userSub ?? null })\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t);\n\t}\n});\nexport const createPostgresInitialAccessTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): InitialAccessTokenStore => ({\n\tconsumeToken: async (tokenHash) => {\n\t\tconst deleted = await db\n\t\t\t.delete(oauthInitialAccessTokensTable)\n\t\t\t.where(eq(oauthInitialAccessTokensTable.token_hash, tokenHash))\n\t\t\t.returning({\n\t\t\t\ttoken_hash: oauthInitialAccessTokensTable.token_hash\n\t\t\t});\n\n\t\treturn deleted.length > 0;\n\t}\n});\nexport const createPostgresLogoutDeliveryStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): LogoutDeliveryStore => ({\n\tlistFailed: async (limit = DEFAULT_LIST_LIMIT) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(oauthLogoutDeliveriesTable)\n\t\t\t.orderBy(desc(oauthLogoutDeliveriesTable.created_at_ms))\n\t\t\t.limit(limit);\n\n\t\treturn rows.map(toLogoutDelivery);\n\t},\n\trecordFailure: async (delivery) => {\n\t\tawait db.insert(oauthLogoutDeliveriesTable).values({\n\t\t\tattempts: delivery.attempts,\n\t\t\tclient_id: delivery.clientId,\n\t\t\tcreated_at_ms: delivery.createdAt,\n\t\t\tendpoint_url: delivery.endpointUrl,\n\t\t\tid: delivery.id,\n\t\t\tlast_error: delivery.lastError ?? null,\n\t\t\tlast_status: delivery.lastStatus ?? null,\n\t\t\tlogout_token: delivery.logoutToken,\n\t\t\tuser_id: delivery.userId\n\t\t});\n\t},\n\tremoveFailure: async (deliveryId) => {\n\t\tawait db\n\t\t\t.delete(oauthLogoutDeliveriesTable)\n\t\t\t.where(eq(oauthLogoutDeliveriesTable.id, deliveryId));\n\t}\n});\n// Maps an OAuthClient domain object to the row shape (used by saveClient + updateClient).\nconst toClientValues = (\n\tclient: OAuthClient\n): typeof oauthClientsTable.$inferInsert => ({\n\tbackchannel_logout_uri: client.backchannelLogoutUri ?? null,\n\tclient_id: client.clientId,\n\thashed_secret: client.hashedSecret ?? null,\n\tjwks_json: client.jwks ?? null,\n\tjwks_uri: client.jwksUri ?? null,\n\tname: client.name,\n\tpost_logout_redirect_uris: client.postLogoutRedirectUris ?? null,\n\tredirect_uris: client.redirectUris,\n\trequire_pushed_authorization_requests:\n\t\tclient.requirePushedAuthorizationRequests ?? null,\n\trequire_signed_request_object: client.requireSignedRequestObject ?? null,\n\tscopes: client.scopes\n});\n\nexport const createPostgresOAuthClientStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): OAuthClientStore => ({\n\tdeleteClient: async (clientId) => {\n\t\tawait db\n\t\t\t.delete(oauthClientsTable)\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId));\n\t},\n\tfindClient: async (clientId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthClientsTable)\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toClient(row) : undefined;\n\t},\n\tsaveClient: async (client) => {\n\t\tawait db.insert(oauthClientsTable).values(toClientValues(client));\n\t},\n\tupdateClient: async (clientId, client) => {\n\t\tawait db\n\t\t\t.update(oauthClientsTable)\n\t\t\t.set(toClientValues(client))\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId));\n\t}\n});\nexport const createPostgresOidcRefreshTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): OidcRefreshTokenStore => ({\n\tconsumeToken: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.token_hash, tokenHash))\n\t\t\t.returning();\n\n\t\treturn row ? toRefresh(row) : undefined;\n\t},\n\tdeleteForUser: async (userId) => {\n\t\tawait db\n\t\t\t.delete(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.user_id, userId));\n\t},\n\tgetToken: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\n\t\treturn row ? toRefresh(row) : undefined;\n\t},\n\tlistClientIdsForUser: async (userId) => {\n\t\tconst rows = await db\n\t\t\t.selectDistinct({ client_id: oauthRefreshTokensTable.client_id })\n\t\t\t.from(oauthRefreshTokensTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(oauthRefreshTokensTable.user_id, userId),\n\t\t\t\t\tgt(oauthRefreshTokensTable.expires_at_ms, Date.now())\n\t\t\t\t)\n\t\t\t);\n\n\t\treturn rows.map((row) => row.client_id);\n\t},\n\tsaveToken: async (token) => {\n\t\tawait db.insert(oauthRefreshTokensTable).values(toRefreshValues(token));\n\t}\n});\nexport const createPostgresPushedAuthorizationRequestStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): PushedAuthorizationRequestStore => ({\n\tconsumeRequest: async (requestUriHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthPushedAuthorizationRequestsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthPushedAuthorizationRequestsTable.request_uri_hash,\n\t\t\t\t\trequestUriHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.returning();\n\t\tif (!row) return undefined;\n\t\t// Lazy GC of expired rows from prior writes — keeps the table thin without a\n\t\t// dedicated cron. Same call returns undefined if THIS request was already expired.\n\t\tawait db\n\t\t\t.delete(oauthPushedAuthorizationRequestsTable)\n\t\t\t.where(\n\t\t\t\tlt(\n\t\t\t\t\toauthPushedAuthorizationRequestsTable.expires_at_ms,\n\t\t\t\t\tDate.now()\n\t\t\t\t)\n\t\t\t);\n\t\tif (row.expires_at_ms < Date.now()) return undefined;\n\t\tconst request: PushedAuthorizationRequest = {\n\t\t\tclientId: row.client_id,\n\t\t\tcreatedAt: row.created_at_ms,\n\t\t\texpiresAt: row.expires_at_ms,\n\t\t\tparams: row.params_json,\n\t\t\trequestUriHash: row.request_uri_hash\n\t\t};\n\n\t\treturn request;\n\t},\n\tsaveRequest: async (request) => {\n\t\tawait db.insert(oauthPushedAuthorizationRequestsTable).values({\n\t\t\tclient_id: request.clientId,\n\t\t\tcreated_at_ms: request.createdAt,\n\t\t\texpires_at_ms: request.expiresAt,\n\t\t\tparams_json: request.params,\n\t\t\trequest_uri_hash: request.requestUriHash\n\t\t});\n\t}\n});\n\ntype BackchannelAuthRow = typeof oauthBackchannelAuthRequestsTable.$inferSelect;\n\nconst toBackchannelAuth = (\n\trow: BackchannelAuthRow\n): BackchannelAuthRequest => ({\n\tauthReqId: row.auth_req_id,\n\tbindingMessage: row.binding_message ?? undefined,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms,\n\tintervalSeconds: row.interval_seconds,\n\tlastPolledAt: row.last_polled_at_ms ?? undefined,\n\tscopes: row.scopes,\n\tstatus: backchannelAuthStatus(row.status),\n\tuserSub: row.user_sub ?? undefined\n});\n\nexport const createNeonBackchannelAuthStore = (databaseUrl: string) =>\n\tcreatePostgresBackchannelAuthStore(createNeonDatabase(databaseUrl));\n\nexport const createPostgresBackchannelAuthStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): BackchannelAuthStore => ({\n\tdeleteByAuthReqId: async (authReqId) => {\n\t\tawait db\n\t\t\t.delete(oauthBackchannelAuthRequestsTable)\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t},\n\tfindByAuthReqId: async (authReqId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthBackchannelAuthRequestsTable)\n\t\t\t.where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toBackchannelAuth(row) : undefined;\n\t},\n\trecordPoll: async (authReqId, polledAt) => {\n\t\tawait db\n\t\t\t.update(oauthBackchannelAuthRequestsTable)\n\t\t\t.set({ last_polled_at_ms: polledAt })\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t},\n\tsaveBackchannelAuth: async (request) => {\n\t\tawait db.insert(oauthBackchannelAuthRequestsTable).values({\n\t\t\tauth_req_id: request.authReqId,\n\t\t\tbinding_message: request.bindingMessage ?? null,\n\t\t\tclient_id: request.clientId,\n\t\t\tcreated_at_ms: request.createdAt,\n\t\t\texpires_at_ms: request.expiresAt,\n\t\t\tinterval_seconds: request.intervalSeconds,\n\t\t\tlast_polled_at_ms: request.lastPolledAt ?? null,\n\t\t\tscopes: request.scopes,\n\t\t\tstatus: request.status,\n\t\t\tuser_sub: request.userSub ?? null\n\t\t});\n\t},\n\tupdateStatus: async (authReqId, status, userSub) => {\n\t\tawait db\n\t\t\t.update(oauthBackchannelAuthRequestsTable)\n\t\t\t.set({ status, user_sub: userSub ?? null })\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t}\n});\n",
22
+ "import { and, desc, eq, gt, lt } from 'drizzle-orm';\nimport {\n\tbigint,\n\tboolean,\n\tjsonb,\n\tpgTable,\n\ttext,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tAuthorizationCode,\n\tAuthorizationCodeStore,\n\tBackchannelAuthRequest,\n\tBackchannelAuthStatus,\n\tBackchannelAuthStore,\n\tClientAssertionJtiStore,\n\tClientRegistrationToken,\n\tClientRegistrationTokenStore,\n\tDeviceAuthorization,\n\tDeviceAuthorizationStatus,\n\tDeviceAuthorizationStore,\n\tInitialAccessTokenStore,\n\tLogoutDelivery,\n\tLogoutDeliveryStore,\n\tOAuthClient,\n\tOAuthClientStore,\n\tOidcRefreshToken,\n\tOidcRefreshTokenStore,\n\tPushedAuthorizationRequest,\n\tPushedAuthorizationRequestStore\n} from './types';\n\nconst URL_LENGTH = 2048;\nconst DEFAULT_LIST_LIMIT = 100;\n\nconst ID_LENGTH = 255;\nconst BACKCHANNEL_AUTH_STATUSES = new Set<BackchannelAuthStatus>([\n\t'approved',\n\t'denied',\n\t'pending'\n]);\nconst DEVICE_AUTHORIZATION_STATUSES = new Set<DeviceAuthorizationStatus>([\n\t'approved',\n\t'denied',\n\t'pending'\n]);\n\nconst backchannelAuthStatus = (value: string) => {\n\tfor (const status of BACKCHANNEL_AUTH_STATUSES)\n\t\tif (status === value) return status;\n\n\tthrow new Error(\n\t\t`Invalid persisted backchannel authorization status: ${value}`\n\t);\n};\n\nconst deviceAuthorizationStatus = (value: string) => {\n\tfor (const status of DEVICE_AUTHORIZATION_STATUSES)\n\t\tif (status === value) return status;\n\n\tthrow new Error(`Invalid persisted device authorization status: ${value}`);\n};\n\nexport const oauthBackchannelAuthRequestsTable = pgTable(\n\t'auth_oauth_backchannel_auth_requests',\n\t{\n\t\tauth_req_id: varchar('auth_req_id', { length: ID_LENGTH }).primaryKey(),\n\t\tbinding_message: text('binding_message'),\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tinterval_seconds: bigint('interval_seconds', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tlast_polled_at_ms: bigint('last_polled_at_ms', { mode: 'number' }),\n\t\tscopes: text('scopes').array().notNull(),\n\t\tstatus: varchar('status', { length: 16 }).notNull(),\n\t\tuser_sub: varchar('user_sub', { length: ID_LENGTH })\n\t}\n);\nexport const oauthClientAssertionJtisTable = pgTable(\n\t'auth_oauth_client_assertion_jtis',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcomposite_key: varchar('composite_key', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tjti: varchar('jti', { length: ID_LENGTH }).notNull()\n\t}\n);\nexport const oauthClientRegistrationTokensTable = pgTable(\n\t'auth_oauth_client_registration_tokens',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey()\n\t}\n);\nexport const oauthClientsTable = pgTable('auth_oauth_clients', {\n\tbackchannel_logout_uri: varchar('backchannel_logout_uri', {\n\t\tlength: URL_LENGTH\n\t}),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).primaryKey(),\n\thashed_secret: varchar('hashed_secret', { length: ID_LENGTH }),\n\tjwks_json: jsonb('jwks_json').$type<JsonWebKey[]>(),\n\tjwks_uri: varchar('jwks_uri', { length: URL_LENGTH }),\n\tname: varchar('name', { length: ID_LENGTH }).notNull(),\n\tpost_logout_redirect_uris: text('post_logout_redirect_uris').array(),\n\tredirect_uris: text('redirect_uris').array().notNull(),\n\trequire_pushed_authorization_requests: boolean(\n\t\t'require_pushed_authorization_requests'\n\t),\n\trequire_signed_request_object: boolean('require_signed_request_object'),\n\tscopes: text('scopes').array().notNull()\n});\nexport const oauthCodesTable = pgTable('auth_oauth_codes', {\n\tacr: varchar('acr', { length: ID_LENGTH }),\n\taudience: varchar('audience', { length: URL_LENGTH }),\n\tclaims_json: jsonb('claims_json').$type<Record<string, unknown>>(),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\tcode_challenge: varchar('code_challenge', { length: ID_LENGTH }).notNull(),\n\tcode_hash: varchar('code_hash', { length: ID_LENGTH }).primaryKey(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdpop_jkt: varchar('dpop_jkt', { length: ID_LENGTH }),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\tnonce: varchar('nonce', { length: ID_LENGTH }),\n\tredirect_uri: varchar('redirect_uri', { length: ID_LENGTH }).notNull(),\n\tscopes: text('scopes').array().notNull(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\nexport const oauthDeviceAuthorizationsTable = pgTable(\n\t'auth_oauth_device_authorizations',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\tdevice_code_hash: varchar('device_code_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tinterval_seconds: bigint('interval_seconds', {\n\t\t\tmode: 'number'\n\t\t}).notNull(),\n\t\tscopes: text('scopes').array().notNull(),\n\t\tstatus: varchar('status', { length: 16 }).notNull(),\n\t\tuser_code: varchar('user_code', { length: 16 }).notNull().unique(),\n\t\tuser_sub: varchar('user_sub', { length: ID_LENGTH })\n\t}\n);\nexport const oauthInitialAccessTokensTable = pgTable(\n\t'auth_oauth_initial_access_tokens',\n\t{\n\t\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey()\n\t}\n);\nexport const oauthLogoutDeliveriesTable = pgTable(\n\t'auth_oauth_logout_deliveries',\n\t{\n\t\tattempts: bigint('attempts', { mode: 'number' }).notNull(),\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\tendpoint_url: varchar('endpoint_url', { length: URL_LENGTH }).notNull(),\n\t\tid: varchar('id', { length: ID_LENGTH }).primaryKey(),\n\t\tlast_error: text('last_error'),\n\t\tlast_status: bigint('last_status', { mode: 'number' }),\n\t\tlogout_token: text('logout_token').notNull(),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n\t}\n);\nexport const oauthPushedAuthorizationRequestsTable = pgTable(\n\t'auth_oauth_pushed_authorization_requests',\n\t{\n\t\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tparams_json: jsonb('params_json')\n\t\t\t.$type<Record<string, string>>()\n\t\t\t.notNull(),\n\t\trequest_uri_hash: varchar('request_uri_hash', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey()\n\t}\n);\nexport const oauthRefreshTokensTable = pgTable('auth_oauth_refresh_tokens', {\n\tacr: varchar('acr', { length: ID_LENGTH }),\n\taudience: varchar('audience', { length: URL_LENGTH }),\n\tclaims_json: jsonb('claims_json').$type<Record<string, unknown>>(),\n\tclient_id: varchar('client_id', { length: ID_LENGTH }).notNull(),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tdpop_jkt: varchar('dpop_jkt', { length: ID_LENGTH }),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\tscopes: text('scopes').array().notNull(),\n\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey(),\n\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n});\n\ntype ClientRow = typeof oauthClientsTable.$inferSelect;\ntype CodeRow = typeof oauthCodesTable.$inferSelect;\ntype DeviceAuthRow = typeof oauthDeviceAuthorizationsTable.$inferSelect;\ntype LogoutDeliveryRow = typeof oauthLogoutDeliveriesTable.$inferSelect;\ntype RefreshRow = typeof oauthRefreshTokensTable.$inferSelect;\n\nconst toClient = (row: ClientRow): OAuthClient => ({\n\tbackchannelLogoutUri: row.backchannel_logout_uri ?? undefined,\n\tclientId: row.client_id,\n\thashedSecret: row.hashed_secret ?? undefined,\n\tjwks: row.jwks_json ?? undefined,\n\tjwksUri: row.jwks_uri ?? undefined,\n\tname: row.name,\n\tpostLogoutRedirectUris: row.post_logout_redirect_uris ?? undefined,\n\tredirectUris: row.redirect_uris,\n\trequirePushedAuthorizationRequests:\n\t\trow.require_pushed_authorization_requests ?? undefined,\n\trequireSignedRequestObject: row.require_signed_request_object ?? undefined,\n\tscopes: row.scopes\n});\n\nconst toLogoutDelivery = (row: LogoutDeliveryRow): LogoutDelivery => ({\n\tattempts: row.attempts,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tendpointUrl: row.endpoint_url,\n\tid: row.id,\n\tlastError: row.last_error ?? undefined,\n\tlastStatus: row.last_status ?? undefined,\n\tlogoutToken: row.logout_token,\n\tuserId: row.user_id\n});\n\nconst toCode = (row: CodeRow): AuthorizationCode => ({\n\tacr: row.acr ?? undefined,\n\taudience: row.audience ?? undefined,\n\tclaims: row.claims_json ?? undefined,\n\tclientId: row.client_id,\n\tcodeChallenge: row.code_challenge,\n\tcodeHash: row.code_hash,\n\tcreatedAt: row.created_at_ms,\n\tdpopJkt: row.dpop_jkt ?? undefined,\n\texpiresAt: row.expires_at_ms,\n\tnonce: row.nonce ?? undefined,\n\tredirectUri: row.redirect_uri,\n\tscopes: row.scopes,\n\tuserId: row.user_id\n});\n\nconst toCodeValues = (\n\tcode: AuthorizationCode\n): typeof oauthCodesTable.$inferInsert => ({\n\tacr: code.acr ?? null,\n\taudience: code.audience ?? null,\n\tclaims_json: code.claims ?? null,\n\tclient_id: code.clientId,\n\tcode_challenge: code.codeChallenge,\n\tcode_hash: code.codeHash,\n\tcreated_at_ms: code.createdAt,\n\tdpop_jkt: code.dpopJkt ?? null,\n\texpires_at_ms: code.expiresAt,\n\tnonce: code.nonce ?? null,\n\tredirect_uri: code.redirectUri,\n\tscopes: code.scopes,\n\tuser_id: code.userId\n});\n\nconst toDeviceAuth = (row: DeviceAuthRow): DeviceAuthorization => ({\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tdeviceCodeHash: row.device_code_hash,\n\texpiresAt: row.expires_at_ms,\n\tintervalSeconds: row.interval_seconds,\n\tscopes: row.scopes,\n\tstatus: deviceAuthorizationStatus(row.status),\n\tuserCode: row.user_code,\n\tuserSub: row.user_sub ?? undefined\n});\n\nconst toRefresh = (row: RefreshRow): OidcRefreshToken => ({\n\tacr: row.acr ?? undefined,\n\taudience: row.audience ?? undefined,\n\tclaims: row.claims_json ?? undefined,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\tdpopJkt: row.dpop_jkt ?? undefined,\n\texpiresAt: row.expires_at_ms,\n\tscopes: row.scopes,\n\ttokenHash: row.token_hash,\n\tuserId: row.user_id\n});\n\nconst toRefreshValues = (\n\ttoken: OidcRefreshToken\n): typeof oauthRefreshTokensTable.$inferInsert => ({\n\tacr: token.acr ?? null,\n\taudience: token.audience ?? null,\n\tclaims_json: token.claims ?? null,\n\tclient_id: token.clientId,\n\tcreated_at_ms: token.createdAt,\n\tdpop_jkt: token.dpopJkt ?? null,\n\texpires_at_ms: token.expiresAt,\n\tscopes: token.scopes,\n\ttoken_hash: token.tokenHash,\n\tuser_id: token.userId\n});\n\nexport const createNeonAuthorizationCodeStore = (databaseUrl: string) =>\n\tcreatePostgresAuthorizationCodeStore(createNeonDatabase(databaseUrl));\nexport const createNeonClientAssertionJtiStore = (databaseUrl: string) =>\n\tcreatePostgresClientAssertionJtiStore(createNeonDatabase(databaseUrl));\nexport const createNeonClientRegistrationTokenStore = (databaseUrl: string) =>\n\tcreatePostgresClientRegistrationTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonDeviceAuthorizationStore = (databaseUrl: string) =>\n\tcreatePostgresDeviceAuthorizationStore(createNeonDatabase(databaseUrl));\nexport const createNeonInitialAccessTokenStore = (databaseUrl: string) =>\n\tcreatePostgresInitialAccessTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonLogoutDeliveryStore = (databaseUrl: string) =>\n\tcreatePostgresLogoutDeliveryStore(createNeonDatabase(databaseUrl));\nexport const createNeonOAuthClientStore = (databaseUrl: string) =>\n\tcreatePostgresOAuthClientStore(createNeonDatabase(databaseUrl));\nexport const createNeonOidcRefreshTokenStore = (databaseUrl: string) =>\n\tcreatePostgresOidcRefreshTokenStore(createNeonDatabase(databaseUrl));\nexport const createNeonPushedAuthorizationRequestStore = (\n\tdatabaseUrl: string\n) =>\n\tcreatePostgresPushedAuthorizationRequestStore(\n\t\tcreateNeonDatabase(databaseUrl)\n\t);\nexport const createPostgresAuthorizationCodeStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): AuthorizationCodeStore => ({\n\tconsumeCode: async (codeHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthCodesTable)\n\t\t\t.where(eq(oauthCodesTable.code_hash, codeHash))\n\t\t\t.returning();\n\n\t\treturn row ? toCode(row) : undefined;\n\t},\n\tsaveCode: async (code) => {\n\t\tawait db.insert(oauthCodesTable).values(toCodeValues(code));\n\t}\n});\nexport const createPostgresClientAssertionJtiStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): ClientAssertionJtiStore => ({\n\trecordIfFresh: async (clientId, jti, expiresAt) => {\n\t\t// Lazy GC of expired entries on each call. Bounded by the assertion `exp` window\n\t\t// (~5 min), so this stays cheap even at high call volume.\n\t\tawait db\n\t\t\t.delete(oauthClientAssertionJtisTable)\n\t\t\t.where(lt(oauthClientAssertionJtisTable.expires_at_ms, Date.now()));\n\t\tconst compositeKey = `${clientId}|${jti}`;\n\t\ttry {\n\t\t\tawait db.insert(oauthClientAssertionJtisTable).values({\n\t\t\t\tclient_id: clientId,\n\t\t\t\tcomposite_key: compositeKey,\n\t\t\t\texpires_at_ms: expiresAt,\n\t\t\t\tjti\n\t\t\t});\n\n\t\t\treturn true;\n\t\t} catch {\n\t\t\t// PK collision = we've seen this jti for this client before = replay.\n\t\t\treturn false;\n\t\t}\n\t}\n});\nexport const createPostgresClientRegistrationTokenStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): ClientRegistrationTokenStore => ({\n\tdeleteByClientId: async (clientId) => {\n\t\tawait db\n\t\t\t.delete(oauthClientRegistrationTokensTable)\n\t\t\t.where(eq(oauthClientRegistrationTokensTable.client_id, clientId));\n\t},\n\tfindByTokenHash: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthClientRegistrationTokensTable)\n\t\t\t.where(eq(oauthClientRegistrationTokensTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\t\tif (!row) return undefined;\n\t\tconst token: ClientRegistrationToken = {\n\t\t\tclientId: row.client_id,\n\t\t\tcreatedAt: row.created_at_ms,\n\t\t\ttokenHash: row.token_hash\n\t\t};\n\n\t\treturn token;\n\t},\n\tsaveToken: async (token) => {\n\t\t// One reg token per client — drop any prior token before saving.\n\t\tawait db\n\t\t\t.delete(oauthClientRegistrationTokensTable)\n\t\t\t.where(\n\t\t\t\teq(oauthClientRegistrationTokensTable.client_id, token.clientId)\n\t\t\t);\n\t\tawait db.insert(oauthClientRegistrationTokensTable).values({\n\t\t\tclient_id: token.clientId,\n\t\t\tcreated_at_ms: token.createdAt,\n\t\t\ttoken_hash: token.tokenHash\n\t\t});\n\t}\n});\nexport const createPostgresDeviceAuthorizationStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): DeviceAuthorizationStore => ({\n\tdeleteByDeviceCodeHash: async (deviceCodeHash) => {\n\t\tawait db\n\t\t\t.delete(oauthDeviceAuthorizationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t);\n\t},\n\tfindByDeviceCodeHash: async (deviceCodeHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthDeviceAuthorizationsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row ? toDeviceAuth(row) : undefined;\n\t},\n\tfindByUserCode: async (userCode) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthDeviceAuthorizationsTable)\n\t\t\t.where(eq(oauthDeviceAuthorizationsTable.user_code, userCode))\n\t\t\t.limit(1);\n\n\t\treturn row ? toDeviceAuth(row) : undefined;\n\t},\n\tsaveDeviceAuthorization: async (deviceAuthorization) => {\n\t\tawait db.insert(oauthDeviceAuthorizationsTable).values({\n\t\t\tclient_id: deviceAuthorization.clientId,\n\t\t\tcreated_at_ms: deviceAuthorization.createdAt,\n\t\t\tdevice_code_hash: deviceAuthorization.deviceCodeHash,\n\t\t\texpires_at_ms: deviceAuthorization.expiresAt,\n\t\t\tinterval_seconds: deviceAuthorization.intervalSeconds,\n\t\t\tscopes: deviceAuthorization.scopes,\n\t\t\tstatus: deviceAuthorization.status,\n\t\t\tuser_code: deviceAuthorization.userCode,\n\t\t\tuser_sub: deviceAuthorization.userSub ?? null\n\t\t});\n\t},\n\tupdateStatus: async (deviceCodeHash, status, userSub) => {\n\t\tawait db\n\t\t\t.update(oauthDeviceAuthorizationsTable)\n\t\t\t.set({ status, user_sub: userSub ?? null })\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthDeviceAuthorizationsTable.device_code_hash,\n\t\t\t\t\tdeviceCodeHash\n\t\t\t\t)\n\t\t\t);\n\t}\n});\nexport const createPostgresInitialAccessTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): InitialAccessTokenStore => ({\n\tconsumeToken: async (tokenHash) => {\n\t\tconst deleted = await db\n\t\t\t.delete(oauthInitialAccessTokensTable)\n\t\t\t.where(eq(oauthInitialAccessTokensTable.token_hash, tokenHash))\n\t\t\t.returning({\n\t\t\t\ttoken_hash: oauthInitialAccessTokensTable.token_hash\n\t\t\t});\n\n\t\treturn deleted.length > 0;\n\t}\n});\nexport const createPostgresLogoutDeliveryStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): LogoutDeliveryStore => ({\n\tlistFailed: async (limit = DEFAULT_LIST_LIMIT) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(oauthLogoutDeliveriesTable)\n\t\t\t.orderBy(desc(oauthLogoutDeliveriesTable.created_at_ms))\n\t\t\t.limit(limit);\n\n\t\treturn rows.map(toLogoutDelivery);\n\t},\n\trecordFailure: async (delivery) => {\n\t\tawait db.insert(oauthLogoutDeliveriesTable).values({\n\t\t\tattempts: delivery.attempts,\n\t\t\tclient_id: delivery.clientId,\n\t\t\tcreated_at_ms: delivery.createdAt,\n\t\t\tendpoint_url: delivery.endpointUrl,\n\t\t\tid: delivery.id,\n\t\t\tlast_error: delivery.lastError ?? null,\n\t\t\tlast_status: delivery.lastStatus ?? null,\n\t\t\tlogout_token: delivery.logoutToken,\n\t\t\tuser_id: delivery.userId\n\t\t});\n\t},\n\tremoveFailure: async (deliveryId) => {\n\t\tawait db\n\t\t\t.delete(oauthLogoutDeliveriesTable)\n\t\t\t.where(eq(oauthLogoutDeliveriesTable.id, deliveryId));\n\t}\n});\n// Maps an OAuthClient domain object to the row shape (used by saveClient + updateClient).\nconst toClientValues = (\n\tclient: OAuthClient\n): typeof oauthClientsTable.$inferInsert => ({\n\tbackchannel_logout_uri: client.backchannelLogoutUri ?? null,\n\tclient_id: client.clientId,\n\thashed_secret: client.hashedSecret ?? null,\n\tjwks_json: client.jwks ?? null,\n\tjwks_uri: client.jwksUri ?? null,\n\tname: client.name,\n\tpost_logout_redirect_uris: client.postLogoutRedirectUris ?? null,\n\tredirect_uris: client.redirectUris,\n\trequire_pushed_authorization_requests:\n\t\tclient.requirePushedAuthorizationRequests ?? null,\n\trequire_signed_request_object: client.requireSignedRequestObject ?? null,\n\tscopes: client.scopes\n});\n\nexport const createPostgresOAuthClientStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): OAuthClientStore => ({\n\tdeleteClient: async (clientId) => {\n\t\tawait db\n\t\t\t.delete(oauthClientsTable)\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId));\n\t},\n\tfindClient: async (clientId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthClientsTable)\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toClient(row) : undefined;\n\t},\n\tsaveClient: async (client) => {\n\t\tawait db.insert(oauthClientsTable).values(toClientValues(client));\n\t},\n\tupdateClient: async (clientId, client) => {\n\t\tawait db\n\t\t\t.update(oauthClientsTable)\n\t\t\t.set(toClientValues(client))\n\t\t\t.where(eq(oauthClientsTable.client_id, clientId));\n\t}\n});\nexport const createPostgresOidcRefreshTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): OidcRefreshTokenStore => ({\n\tconsumeToken: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.token_hash, tokenHash))\n\t\t\t.returning();\n\n\t\treturn row ? toRefresh(row) : undefined;\n\t},\n\tdeleteForUser: async (userId) => {\n\t\tawait db\n\t\t\t.delete(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.user_id, userId));\n\t},\n\tdeleteForUserClient: async (userId, clientId) => {\n\t\tconst deleted = await db\n\t\t\t.delete(oauthRefreshTokensTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(oauthRefreshTokensTable.user_id, userId),\n\t\t\t\t\teq(oauthRefreshTokensTable.client_id, clientId)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.returning({ tokenHash: oauthRefreshTokensTable.token_hash });\n\n\t\treturn deleted.length;\n\t},\n\tgetToken: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthRefreshTokensTable)\n\t\t\t.where(eq(oauthRefreshTokensTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\n\t\treturn row ? toRefresh(row) : undefined;\n\t},\n\tlistClientIdsForUser: async (userId) => {\n\t\tconst rows = await db\n\t\t\t.selectDistinct({ client_id: oauthRefreshTokensTable.client_id })\n\t\t\t.from(oauthRefreshTokensTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(oauthRefreshTokensTable.user_id, userId),\n\t\t\t\t\tgt(oauthRefreshTokensTable.expires_at_ms, Date.now())\n\t\t\t\t)\n\t\t\t);\n\n\t\treturn rows.map((row) => row.client_id);\n\t},\n\tsaveToken: async (token) => {\n\t\tawait db.insert(oauthRefreshTokensTable).values(toRefreshValues(token));\n\t}\n});\nexport const createPostgresPushedAuthorizationRequestStore = <\n\tDB extends AnyPgDatabase\n>(\n\tdb: DB\n): PushedAuthorizationRequestStore => ({\n\tconsumeRequest: async (requestUriHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(oauthPushedAuthorizationRequestsTable)\n\t\t\t.where(\n\t\t\t\teq(\n\t\t\t\t\toauthPushedAuthorizationRequestsTable.request_uri_hash,\n\t\t\t\t\trequestUriHash\n\t\t\t\t)\n\t\t\t)\n\t\t\t.returning();\n\t\tif (!row) return undefined;\n\t\t// Lazy GC of expired rows from prior writes — keeps the table thin without a\n\t\t// dedicated cron. Same call returns undefined if THIS request was already expired.\n\t\tawait db\n\t\t\t.delete(oauthPushedAuthorizationRequestsTable)\n\t\t\t.where(\n\t\t\t\tlt(\n\t\t\t\t\toauthPushedAuthorizationRequestsTable.expires_at_ms,\n\t\t\t\t\tDate.now()\n\t\t\t\t)\n\t\t\t);\n\t\tif (row.expires_at_ms < Date.now()) return undefined;\n\t\tconst request: PushedAuthorizationRequest = {\n\t\t\tclientId: row.client_id,\n\t\t\tcreatedAt: row.created_at_ms,\n\t\t\texpiresAt: row.expires_at_ms,\n\t\t\tparams: row.params_json,\n\t\t\trequestUriHash: row.request_uri_hash\n\t\t};\n\n\t\treturn request;\n\t},\n\tsaveRequest: async (request) => {\n\t\tawait db.insert(oauthPushedAuthorizationRequestsTable).values({\n\t\t\tclient_id: request.clientId,\n\t\t\tcreated_at_ms: request.createdAt,\n\t\t\texpires_at_ms: request.expiresAt,\n\t\t\tparams_json: request.params,\n\t\t\trequest_uri_hash: request.requestUriHash\n\t\t});\n\t}\n});\n\ntype BackchannelAuthRow = typeof oauthBackchannelAuthRequestsTable.$inferSelect;\n\nconst toBackchannelAuth = (\n\trow: BackchannelAuthRow\n): BackchannelAuthRequest => ({\n\tauthReqId: row.auth_req_id,\n\tbindingMessage: row.binding_message ?? undefined,\n\tclientId: row.client_id,\n\tcreatedAt: row.created_at_ms,\n\texpiresAt: row.expires_at_ms,\n\tintervalSeconds: row.interval_seconds,\n\tlastPolledAt: row.last_polled_at_ms ?? undefined,\n\tscopes: row.scopes,\n\tstatus: backchannelAuthStatus(row.status),\n\tuserSub: row.user_sub ?? undefined\n});\n\nexport const createNeonBackchannelAuthStore = (databaseUrl: string) =>\n\tcreatePostgresBackchannelAuthStore(createNeonDatabase(databaseUrl));\n\nexport const createPostgresBackchannelAuthStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): BackchannelAuthStore => ({\n\tdeleteByAuthReqId: async (authReqId) => {\n\t\tawait db\n\t\t\t.delete(oauthBackchannelAuthRequestsTable)\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t},\n\tfindByAuthReqId: async (authReqId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(oauthBackchannelAuthRequestsTable)\n\t\t\t.where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toBackchannelAuth(row) : undefined;\n\t},\n\trecordPoll: async (authReqId, polledAt) => {\n\t\tawait db\n\t\t\t.update(oauthBackchannelAuthRequestsTable)\n\t\t\t.set({ last_polled_at_ms: polledAt })\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t},\n\tsaveBackchannelAuth: async (request) => {\n\t\tawait db.insert(oauthBackchannelAuthRequestsTable).values({\n\t\t\tauth_req_id: request.authReqId,\n\t\t\tbinding_message: request.bindingMessage ?? null,\n\t\t\tclient_id: request.clientId,\n\t\t\tcreated_at_ms: request.createdAt,\n\t\t\texpires_at_ms: request.expiresAt,\n\t\t\tinterval_seconds: request.intervalSeconds,\n\t\t\tlast_polled_at_ms: request.lastPolledAt ?? null,\n\t\t\tscopes: request.scopes,\n\t\t\tstatus: request.status,\n\t\t\tuser_sub: request.userSub ?? null\n\t\t});\n\t},\n\tupdateStatus: async (authReqId, status, userSub) => {\n\t\tawait db\n\t\t\t.update(oauthBackchannelAuthRequestsTable)\n\t\t\t.set({ status, user_sub: userSub ?? null })\n\t\t\t.where(\n\t\t\t\teq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)\n\t\t\t);\n\t}\n});\n",
23
23
  "import { and, desc, eq } from 'drizzle-orm';\nimport {\n\tbigint,\n\tjsonb,\n\tpgTable,\n\tprimaryKey,\n\tvarchar\n} from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type {\n\tInvitationState,\n\tMembershipStatus,\n\tOrganization,\n\tOrganizationInvitation,\n\tOrganizationMembership,\n\tOrganizationStore\n} from './types';\n\nconst ID_LENGTH = 255;\nconst NAME_LENGTH = 255;\nconst STATE_LENGTH = 16;\n\nexport const organizationInvitationsTable = pgTable(\n\t'auth_organization_invitations',\n\t{\n\t\taccepted_at_ms: bigint('accepted_at_ms', { mode: 'number' }),\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\temail: varchar('email', { length: ID_LENGTH }).notNull(),\n\t\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\t\tinvitation_id: varchar('invitation_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}).primaryKey(),\n\t\tinviter_user_id: varchar('inviter_user_id', { length: ID_LENGTH }),\n\t\torganization_id: varchar('organization_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}).notNull(),\n\t\troles: jsonb('roles').$type<string[]>().notNull().default([]),\n\t\tstate: varchar('state', { length: STATE_LENGTH })\n\t\t\t.$type<InvitationState>()\n\t\t\t.notNull()\n\t\t\t.default('pending'),\n\t\ttoken_hash: varchar('token_hash', { length: ID_LENGTH })\n\t\t\t.notNull()\n\t\t\t.unique()\n\t}\n);\nexport const organizationMembershipsTable = pgTable(\n\t'auth_organization_memberships',\n\t{\n\t\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\t\torganization_id: varchar('organization_id', {\n\t\t\tlength: ID_LENGTH\n\t\t}).notNull(),\n\t\troles: jsonb('roles').$type<string[]>().notNull().default([]),\n\t\tstatus: varchar('status', { length: STATE_LENGTH })\n\t\t\t.$type<MembershipStatus>()\n\t\t\t.notNull()\n\t\t\t.default('active'),\n\t\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull(),\n\t\tuser_id: varchar('user_id', { length: ID_LENGTH }).notNull()\n\t},\n\t(table) => [primaryKey({ columns: [table.organization_id, table.user_id] })]\n);\nexport const organizationsTable = pgTable('auth_organizations', {\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tmetadata: jsonb('metadata').$type<Record<string, unknown>>(),\n\tname: varchar('name', { length: NAME_LENGTH }).notNull(),\n\torganization_id: varchar('organization_id', {\n\t\tlength: ID_LENGTH\n\t}).primaryKey(),\n\tupdated_at_ms: bigint('updated_at_ms', { mode: 'number' }).notNull()\n});\n\ntype OrganizationRow = typeof organizationsTable.$inferSelect;\ntype MembershipRow = typeof organizationMembershipsTable.$inferSelect;\ntype InvitationRow = typeof organizationInvitationsTable.$inferSelect;\ntype OrganizationInsert = typeof organizationsTable.$inferInsert;\ntype MembershipInsert = typeof organizationMembershipsTable.$inferInsert;\ntype InvitationInsert = typeof organizationInvitationsTable.$inferInsert;\n\nconst toOrganization = (row: OrganizationRow): Organization => ({\n\tcreatedAt: row.created_at_ms,\n\tmetadata: row.metadata ?? undefined,\n\tname: row.name,\n\torganizationId: row.organization_id,\n\tupdatedAt: row.updated_at_ms\n});\n\nconst toMembership = (row: MembershipRow): OrganizationMembership => ({\n\tcreatedAt: row.created_at_ms,\n\torganizationId: row.organization_id,\n\troles: row.roles,\n\tstatus: row.status,\n\tupdatedAt: row.updated_at_ms,\n\tuserId: row.user_id\n});\n\nconst toInvitation = (row: InvitationRow): OrganizationInvitation => ({\n\tacceptedAt: row.accepted_at_ms ?? undefined,\n\tcreatedAt: row.created_at_ms,\n\temail: row.email,\n\texpiresAt: row.expires_at_ms,\n\tinvitationId: row.invitation_id,\n\tinviterUserId: row.inviter_user_id ?? undefined,\n\torganizationId: row.organization_id,\n\troles: row.roles,\n\tstate: row.state,\n\ttokenHash: row.token_hash\n});\n\nexport const createNeonOrganizationStore = (databaseUrl: string) =>\n\tcreatePostgresOrganizationStore(createNeonDatabase(databaseUrl));\nexport const createPostgresOrganizationStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): OrganizationStore => ({\n\tdeleteOrganization: async (organizationId) => {\n\t\tawait db\n\t\t\t.delete(organizationsTable)\n\t\t\t.where(eq(organizationsTable.organization_id, organizationId));\n\t},\n\tgetInvitation: async (invitationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(organizationInvitationsTable)\n\t\t\t.where(eq(organizationInvitationsTable.invitation_id, invitationId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toInvitation(row) : undefined;\n\t},\n\tgetInvitationByTokenHash: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(organizationInvitationsTable)\n\t\t\t.where(eq(organizationInvitationsTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\n\t\treturn row ? toInvitation(row) : undefined;\n\t},\n\tgetMembership: async (organizationId, userId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(organizationMembershipsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\torganizationMembershipsTable.organization_id,\n\t\t\t\t\t\torganizationId\n\t\t\t\t\t),\n\t\t\t\t\teq(organizationMembershipsTable.user_id, userId)\n\t\t\t\t)\n\t\t\t)\n\t\t\t.limit(1);\n\n\t\treturn row ? toMembership(row) : undefined;\n\t},\n\tgetOrganization: async (organizationId) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(organizationsTable)\n\t\t\t.where(eq(organizationsTable.organization_id, organizationId))\n\t\t\t.limit(1);\n\n\t\treturn row ? toOrganization(row) : undefined;\n\t},\n\tlistInvitationsByOrganization: async (organizationId) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(organizationInvitationsTable)\n\t\t\t.where(\n\t\t\t\teq(organizationInvitationsTable.organization_id, organizationId)\n\t\t\t);\n\n\t\treturn rows.map(toInvitation);\n\t},\n\tlistMembershipsByOrganization: async (organizationId) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(organizationMembershipsTable)\n\t\t\t.where(\n\t\t\t\teq(organizationMembershipsTable.organization_id, organizationId)\n\t\t\t);\n\n\t\treturn rows.map(toMembership);\n\t},\n\tlistMembershipsByUser: async (userId) => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(organizationMembershipsTable)\n\t\t\t.where(eq(organizationMembershipsTable.user_id, userId));\n\n\t\treturn rows.map(toMembership);\n\t},\n\tlistOrganizations: async () => {\n\t\tconst rows = await db\n\t\t\t.select()\n\t\t\t.from(organizationsTable)\n\t\t\t.orderBy(desc(organizationsTable.created_at_ms));\n\n\t\treturn rows.map(toOrganization);\n\t},\n\tremoveMembership: async (organizationId, userId) => {\n\t\tawait db\n\t\t\t.delete(organizationMembershipsTable)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(\n\t\t\t\t\t\torganizationMembershipsTable.organization_id,\n\t\t\t\t\t\torganizationId\n\t\t\t\t\t),\n\t\t\t\t\teq(organizationMembershipsTable.user_id, userId)\n\t\t\t\t)\n\t\t\t);\n\t},\n\tsaveInvitation: async (invitation) => {\n\t\tconst values: InvitationInsert = {\n\t\t\taccepted_at_ms: invitation.acceptedAt ?? null,\n\t\t\tcreated_at_ms: invitation.createdAt,\n\t\t\temail: invitation.email,\n\t\t\texpires_at_ms: invitation.expiresAt,\n\t\t\tinvitation_id: invitation.invitationId,\n\t\t\tinviter_user_id: invitation.inviterUserId ?? null,\n\t\t\torganization_id: invitation.organizationId,\n\t\t\troles: invitation.roles,\n\t\t\tstate: invitation.state,\n\t\t\ttoken_hash: invitation.tokenHash\n\t\t};\n\t\tawait db\n\t\t\t.insert(organizationInvitationsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: organizationInvitationsTable.invitation_id\n\t\t\t});\n\t},\n\tsaveMembership: async (membership) => {\n\t\tconst values: MembershipInsert = {\n\t\t\tcreated_at_ms: membership.createdAt,\n\t\t\torganization_id: membership.organizationId,\n\t\t\troles: membership.roles,\n\t\t\tstatus: membership.status,\n\t\t\tupdated_at_ms: membership.updatedAt,\n\t\t\tuser_id: membership.userId\n\t\t};\n\t\tawait db\n\t\t\t.insert(organizationMembershipsTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: [\n\t\t\t\t\torganizationMembershipsTable.organization_id,\n\t\t\t\t\torganizationMembershipsTable.user_id\n\t\t\t\t]\n\t\t\t});\n\t},\n\tsaveOrganization: async (organization) => {\n\t\tconst values: OrganizationInsert = {\n\t\t\tcreated_at_ms: organization.createdAt,\n\t\t\tmetadata: organization.metadata ?? null,\n\t\t\tname: organization.name,\n\t\t\torganization_id: organization.organizationId,\n\t\t\tupdated_at_ms: organization.updatedAt\n\t\t};\n\t\tawait db.insert(organizationsTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: organizationsTable.organization_id\n\t\t});\n\t}\n});\n",
24
24
  "import { eq } from 'drizzle-orm';\nimport { bigint, pgTable, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type { PasswordlessToken, PasswordlessTokenStore } from './types';\n\nconst ID_LENGTH = 255;\n\nexport const passwordlessTokensTable = pgTable('auth_passwordless_tokens', {\n\temail: varchar('email', { length: ID_LENGTH }).notNull(),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).primaryKey()\n});\n\ntype PasswordlessRow = typeof passwordlessTokensTable.$inferSelect;\ntype PasswordlessInsert = typeof passwordlessTokensTable.$inferInsert;\n\nconst toToken = (row: PasswordlessRow): PasswordlessToken => ({\n\temail: row.email,\n\texpiresAt: row.expires_at_ms,\n\ttokenHash: row.token_hash\n});\n\nexport const createNeonPasswordlessTokenStore = (databaseUrl: string) =>\n\tcreatePostgresPasswordlessTokenStore(createNeonDatabase(databaseUrl));\nexport const createPostgresPasswordlessTokenStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): PasswordlessTokenStore => ({\n\tconsumeToken: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.delete(passwordlessTokensTable)\n\t\t\t.where(eq(passwordlessTokensTable.token_hash, tokenHash))\n\t\t\t.returning();\n\n\t\treturn row ? toToken(row) : undefined;\n\t},\n\tsaveToken: async (token) => {\n\t\tconst values: PasswordlessInsert = {\n\t\t\temail: token.email,\n\t\t\texpires_at_ms: token.expiresAt,\n\t\t\ttoken_hash: token.tokenHash\n\t\t};\n\t\tawait db\n\t\t\t.insert(passwordlessTokensTable)\n\t\t\t.values(values)\n\t\t\t.onConflictDoUpdate({\n\t\t\t\tset: values,\n\t\t\t\ttarget: passwordlessTokensTable.token_hash\n\t\t\t});\n\t}\n});\n",
25
25
  "import { eq } from 'drizzle-orm';\nimport { bigint, jsonb, pgTable, varchar } from 'drizzle-orm/pg-core';\nimport { type AnyPgDatabase, createNeonDatabase } from '../stores/postgres';\nimport type { SetupCapability, SetupSession, SetupSessionStore } from './types';\n\nconst ID_LENGTH = 255;\n\nexport const setupSessionsTable = pgTable('auth_setup_sessions', {\n\tcapabilities: jsonb('capabilities')\n\t\t.$type<SetupCapability[]>()\n\t\t.notNull()\n\t\t.default([]),\n\tcreated_at_ms: bigint('created_at_ms', { mode: 'number' }).notNull(),\n\tcreated_by: varchar('created_by', { length: ID_LENGTH }),\n\texpires_at_ms: bigint('expires_at_ms', { mode: 'number' }).notNull(),\n\torganization_id: varchar('organization_id', {\n\t\tlength: ID_LENGTH\n\t}).notNull(),\n\tsetup_session_id: varchar('setup_session_id', {\n\t\tlength: ID_LENGTH\n\t}).primaryKey(),\n\ttoken_hash: varchar('token_hash', { length: ID_LENGTH }).notNull().unique()\n});\n\ntype SetupRow = typeof setupSessionsTable.$inferSelect;\ntype SetupInsert = typeof setupSessionsTable.$inferInsert;\n\nconst toSession = (row: SetupRow): SetupSession => ({\n\tcapabilities: row.capabilities,\n\tcreatedAt: row.created_at_ms,\n\tcreatedBy: row.created_by ?? undefined,\n\texpiresAt: row.expires_at_ms,\n\torganizationId: row.organization_id,\n\tsetupSessionId: row.setup_session_id,\n\ttokenHash: row.token_hash\n});\n\nexport const createNeonSetupSessionStore = (databaseUrl: string) =>\n\tcreatePostgresSetupSessionStore(createNeonDatabase(databaseUrl));\nexport const createPostgresSetupSessionStore = <DB extends AnyPgDatabase>(\n\tdb: DB\n): SetupSessionStore => ({\n\tdeleteSetupSession: async (setupSessionId) => {\n\t\tawait db\n\t\t\t.delete(setupSessionsTable)\n\t\t\t.where(eq(setupSessionsTable.setup_session_id, setupSessionId));\n\t},\n\tgetSetupSessionByTokenHash: async (tokenHash) => {\n\t\tconst [row] = await db\n\t\t\t.select()\n\t\t\t.from(setupSessionsTable)\n\t\t\t.where(eq(setupSessionsTable.token_hash, tokenHash))\n\t\t\t.limit(1);\n\n\t\treturn row ? toSession(row) : undefined;\n\t},\n\tsaveSetupSession: async (session) => {\n\t\tconst values: SetupInsert = {\n\t\t\tcapabilities: session.capabilities,\n\t\t\tcreated_at_ms: session.createdAt,\n\t\t\tcreated_by: session.createdBy ?? null,\n\t\t\texpires_at_ms: session.expiresAt,\n\t\t\torganization_id: session.organizationId,\n\t\t\tsetup_session_id: session.setupSessionId,\n\t\t\ttoken_hash: session.tokenHash\n\t\t};\n\t\tawait db.insert(setupSessionsTable).values(values).onConflictDoUpdate({\n\t\t\tset: values,\n\t\t\ttarget: setupSessionsTable.setup_session_id\n\t\t});\n\t}\n});\n",
package/dist/index.js CHANGED
@@ -27128,6 +27128,16 @@ var createInMemoryOidcRefreshTokenStore = () => {
27128
27128
  tokens.delete(hash);
27129
27129
  }
27130
27130
  },
27131
+ deleteForUserClient: async (userId, clientId) => {
27132
+ let deleted = 0;
27133
+ for (const [hash, token] of tokens) {
27134
+ if (token.userId !== userId || token.clientId !== clientId)
27135
+ continue;
27136
+ tokens.delete(hash);
27137
+ deleted += 1;
27138
+ }
27139
+ return deleted;
27140
+ },
27131
27141
  getToken: async (tokenHash) => tokens.get(tokenHash),
27132
27142
  listClientIdsForUser: async (userId) => {
27133
27143
  const now = Date.now();
@@ -27532,6 +27542,10 @@ var createPostgresOidcRefreshTokenStore = (db) => ({
27532
27542
  deleteForUser: async (userId) => {
27533
27543
  await db.delete(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.user_id, userId));
27534
27544
  },
27545
+ deleteForUserClient: async (userId, clientId) => {
27546
+ const deleted = await db.delete(oauthRefreshTokensTable).where(and(eq(oauthRefreshTokensTable.user_id, userId), eq(oauthRefreshTokensTable.client_id, clientId))).returning({ tokenHash: oauthRefreshTokensTable.token_hash });
27547
+ return deleted.length;
27548
+ },
27535
27549
  getToken: async (tokenHash) => {
27536
27550
  const [row] = await db.select().from(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.token_hash, tokenHash)).limit(1);
27537
27551
  return row ? toRefresh(row) : undefined;
@@ -30197,5 +30211,5 @@ export {
30197
30211
  AGENT_CLAIM_GRANT_TYPE
30198
30212
  };
30199
30213
 
30200
- //# debugId=08F44D063C89F47864756E2164756E21
30214
+ //# debugId=296A2A7B4F342AE364756E2164756E21
30201
30215
  //# sourceMappingURL=index.js.map