@warlock.js/auth 5.28.0 → 5.30.0

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 (274) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/esm/commands/auth-cleanup-command.d.mts +1 -1
  3. package/esm/commands/auth-cleanup-command.d.mts.map +1 -1
  4. package/esm/commands/auth-cleanup-command.mjs +1 -1
  5. package/esm/commands/auth-cleanup-command.mjs.map +1 -1
  6. package/esm/commands/auth-purge-never-expiring-command.d.mts +1 -1
  7. package/esm/commands/auth-purge-never-expiring-command.d.mts.map +1 -1
  8. package/esm/commands/auth-purge-never-expiring-command.mjs +1 -1
  9. package/esm/commands/auth-purge-never-expiring-command.mjs.map +1 -1
  10. package/esm/commands/jwt-secret-generator-command.d.mts +1 -1
  11. package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -1
  12. package/esm/commands/jwt-secret-generator-command.mjs +1 -1
  13. package/esm/commands/jwt-secret-generator-command.mjs.map +1 -1
  14. package/esm/contracts/auth-contract.d.mts +1 -1
  15. package/esm/contracts/auth-contract.d.mts.map +1 -1
  16. package/esm/contracts/providers.d.mts +1 -1
  17. package/esm/contracts/providers.d.mts.map +1 -1
  18. package/esm/contracts/request-locals.d.mts +1 -1
  19. package/esm/contracts/request-locals.d.mts.map +1 -1
  20. package/esm/contracts/types.d.mts +1 -1
  21. package/esm/contracts/types.d.mts.map +1 -1
  22. package/esm/contracts/types.mjs +1 -1
  23. package/esm/contracts/types.mjs.map +1 -1
  24. package/esm/errors/auth-provider-sdk-missing.error.d.mts +1 -1
  25. package/esm/errors/auth-provider-sdk-missing.error.d.mts.map +1 -1
  26. package/esm/errors/auth-provider-sdk-missing.error.mjs +1 -1
  27. package/esm/errors/auth-provider-sdk-missing.error.mjs.map +1 -1
  28. package/esm/errors/email-not-verified.error.d.mts +1 -1
  29. package/esm/errors/email-not-verified.error.d.mts.map +1 -1
  30. package/esm/errors/email-not-verified.error.mjs +1 -1
  31. package/esm/errors/email-not-verified.error.mjs.map +1 -1
  32. package/esm/errors/invalid-one-time-token.error.d.mts +1 -1
  33. package/esm/errors/invalid-one-time-token.error.d.mts.map +1 -1
  34. package/esm/errors/invalid-one-time-token.error.mjs +1 -1
  35. package/esm/errors/invalid-one-time-token.error.mjs.map +1 -1
  36. package/esm/errors/invalid-passkey.error.d.mts +1 -1
  37. package/esm/errors/invalid-passkey.error.d.mts.map +1 -1
  38. package/esm/errors/invalid-passkey.error.mjs +1 -1
  39. package/esm/errors/invalid-passkey.error.mjs.map +1 -1
  40. package/esm/errors/invalid-provider-callback.error.d.mts +1 -1
  41. package/esm/errors/invalid-provider-callback.error.d.mts.map +1 -1
  42. package/esm/errors/invalid-provider-callback.error.mjs +1 -1
  43. package/esm/errors/invalid-provider-callback.error.mjs.map +1 -1
  44. package/esm/errors/notifications-unavailable.error.d.mts +1 -1
  45. package/esm/errors/notifications-unavailable.error.d.mts.map +1 -1
  46. package/esm/errors/notifications-unavailable.error.mjs +1 -1
  47. package/esm/errors/notifications-unavailable.error.mjs.map +1 -1
  48. package/esm/errors/provider-email-not-verified.error.d.mts +1 -1
  49. package/esm/errors/provider-email-not-verified.error.d.mts.map +1 -1
  50. package/esm/errors/provider-email-not-verified.error.mjs +1 -1
  51. package/esm/errors/provider-email-not-verified.error.mjs.map +1 -1
  52. package/esm/index.d.mts +2 -2
  53. package/esm/index.mjs +2 -2
  54. package/esm/localization/ar.translations.mjs +1 -1
  55. package/esm/localization/ar.translations.mjs.map +1 -1
  56. package/esm/localization/auth-default-translations.d.mts +1 -1
  57. package/esm/localization/auth-default-translations.d.mts.map +1 -1
  58. package/esm/localization/auth-default-translations.mjs +1 -1
  59. package/esm/localization/auth-default-translations.mjs.map +1 -1
  60. package/esm/localization/auth-translations.type.d.mts +1 -1
  61. package/esm/localization/auth-translations.type.d.mts.map +1 -1
  62. package/esm/localization/en.translations.mjs +1 -1
  63. package/esm/localization/en.translations.mjs.map +1 -1
  64. package/esm/localization/init.mjs +1 -1
  65. package/esm/localization/init.mjs.map +1 -1
  66. package/esm/localization/register-auth-translations.d.mts +1 -1
  67. package/esm/localization/register-auth-translations.d.mts.map +1 -1
  68. package/esm/localization/register-auth-translations.mjs +1 -1
  69. package/esm/localization/register-auth-translations.mjs.map +1 -1
  70. package/esm/middleware/auth.middleware.d.mts +1 -1
  71. package/esm/middleware/auth.middleware.d.mts.map +1 -1
  72. package/esm/middleware/auth.middleware.mjs +1 -1
  73. package/esm/middleware/auth.middleware.mjs.map +1 -1
  74. package/esm/middleware/csrf-origin-check.d.mts +1 -1
  75. package/esm/middleware/csrf-origin-check.d.mts.map +1 -1
  76. package/esm/middleware/csrf-origin-check.mjs +1 -1
  77. package/esm/middleware/csrf-origin-check.mjs.map +1 -1
  78. package/esm/middleware/index.d.mts +1 -1
  79. package/esm/middleware/index.mjs +1 -1
  80. package/esm/middleware/localized-login-path.mjs +1 -1
  81. package/esm/middleware/localized-login-path.mjs.map +1 -1
  82. package/esm/middleware/login-throttle.middleware.d.mts +17 -2
  83. package/esm/middleware/login-throttle.middleware.d.mts.map +1 -1
  84. package/esm/middleware/login-throttle.middleware.mjs +84 -6
  85. package/esm/middleware/login-throttle.middleware.mjs.map +1 -1
  86. package/esm/middleware/one-time-token-throttle.middleware.d.mts +1 -1
  87. package/esm/middleware/one-time-token-throttle.middleware.d.mts.map +1 -1
  88. package/esm/middleware/one-time-token-throttle.middleware.mjs +1 -1
  89. package/esm/middleware/one-time-token-throttle.middleware.mjs.map +1 -1
  90. package/esm/middleware/otp-throttle.middleware.d.mts +1 -1
  91. package/esm/middleware/otp-throttle.middleware.d.mts.map +1 -1
  92. package/esm/middleware/otp-throttle.middleware.mjs +1 -1
  93. package/esm/middleware/otp-throttle.middleware.mjs.map +1 -1
  94. package/esm/middleware/require-verified-email.middleware.d.mts +1 -1
  95. package/esm/middleware/require-verified-email.middleware.d.mts.map +1 -1
  96. package/esm/middleware/require-verified-email.middleware.mjs +1 -1
  97. package/esm/middleware/require-verified-email.middleware.mjs.map +1 -1
  98. package/esm/models/access-token/access-token.model.d.mts +27 -35
  99. package/esm/models/access-token/access-token.model.d.mts.map +1 -1
  100. package/esm/models/access-token/access-token.model.mjs +1 -1
  101. package/esm/models/access-token/access-token.model.mjs.map +1 -1
  102. package/esm/models/access-token/family-association.migration.mjs +1 -1
  103. package/esm/models/access-token/family-association.migration.mjs.map +1 -1
  104. package/esm/models/access-token/migration.mjs +1 -1
  105. package/esm/models/access-token/migration.mjs.map +1 -1
  106. package/esm/models/auth-token-family/auth-token-family.model.d.mts +27 -43
  107. package/esm/models/auth-token-family/auth-token-family.model.d.mts.map +1 -1
  108. package/esm/models/auth-token-family/auth-token-family.model.mjs +1 -1
  109. package/esm/models/auth-token-family/auth-token-family.model.mjs.map +1 -1
  110. package/esm/models/auth-token-family/migration.mjs +1 -1
  111. package/esm/models/auth-token-family/migration.mjs.map +1 -1
  112. package/esm/models/auth.model.d.mts +1 -1
  113. package/esm/models/auth.model.d.mts.map +1 -1
  114. package/esm/models/auth.model.mjs +1 -1
  115. package/esm/models/auth.model.mjs.map +1 -1
  116. package/esm/models/index.d.mts +1 -1
  117. package/esm/models/index.d.mts.map +1 -1
  118. package/esm/models/index.mjs +1 -1
  119. package/esm/models/index.mjs.map +1 -1
  120. package/esm/models/one-time-token/migration.mjs +1 -1
  121. package/esm/models/one-time-token/migration.mjs.map +1 -1
  122. package/esm/models/one-time-token/one-time-token.model.d.mts +35 -67
  123. package/esm/models/one-time-token/one-time-token.model.d.mts.map +1 -1
  124. package/esm/models/one-time-token/one-time-token.model.mjs +1 -1
  125. package/esm/models/one-time-token/one-time-token.model.mjs.map +1 -1
  126. package/esm/models/passkey-credential/migration.mjs +1 -1
  127. package/esm/models/passkey-credential/migration.mjs.map +1 -1
  128. package/esm/models/passkey-credential/passkey-credential.model.d.mts +31 -47
  129. package/esm/models/passkey-credential/passkey-credential.model.d.mts.map +1 -1
  130. package/esm/models/passkey-credential/passkey-credential.model.mjs +1 -1
  131. package/esm/models/passkey-credential/passkey-credential.model.mjs.map +1 -1
  132. package/esm/models/provider-account/migration.mjs +1 -1
  133. package/esm/models/provider-account/migration.mjs.map +1 -1
  134. package/esm/models/provider-account/provider-account.model.d.mts +27 -35
  135. package/esm/models/provider-account/provider-account.model.d.mts.map +1 -1
  136. package/esm/models/provider-account/provider-account.model.mjs +1 -1
  137. package/esm/models/provider-account/provider-account.model.mjs.map +1 -1
  138. package/esm/models/refresh-token/migration.mjs +1 -1
  139. package/esm/models/refresh-token/migration.mjs.map +1 -1
  140. package/esm/models/refresh-token/refresh-token.model.d.mts +47 -95
  141. package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -1
  142. package/esm/models/refresh-token/refresh-token.model.mjs +1 -1
  143. package/esm/models/refresh-token/refresh-token.model.mjs.map +1 -1
  144. package/esm/models/refresh-token/successor-association.migration.mjs +1 -1
  145. package/esm/models/refresh-token/successor-association.migration.mjs.map +1 -1
  146. package/esm/otp/otp.d.mts +1 -1
  147. package/esm/otp/otp.d.mts.map +1 -1
  148. package/esm/otp/otp.mjs +1 -1
  149. package/esm/otp/otp.mjs.map +1 -1
  150. package/esm/passkeys/passkey-authentication.d.mts +1 -1
  151. package/esm/passkeys/passkey-authentication.d.mts.map +1 -1
  152. package/esm/passkeys/passkey-authentication.mjs +1 -1
  153. package/esm/passkeys/passkey-authentication.mjs.map +1 -1
  154. package/esm/passkeys/passkey-challenges.mjs +1 -1
  155. package/esm/passkeys/passkey-challenges.mjs.map +1 -1
  156. package/esm/passkeys/passkey-registration.d.mts +1 -1
  157. package/esm/passkeys/passkey-registration.d.mts.map +1 -1
  158. package/esm/passkeys/passkey-registration.mjs +1 -1
  159. package/esm/passkeys/passkey-registration.mjs.map +1 -1
  160. package/esm/passkeys/simplewebauthn.d.mts +1 -1
  161. package/esm/passkeys/simplewebauthn.d.mts.map +1 -1
  162. package/esm/passkeys/simplewebauthn.mjs +1 -1
  163. package/esm/passkeys/simplewebauthn.mjs.map +1 -1
  164. package/esm/providers/apple-provider.d.mts +1 -1
  165. package/esm/providers/apple-provider.d.mts.map +1 -1
  166. package/esm/providers/apple-provider.mjs +1 -1
  167. package/esm/providers/apple-provider.mjs.map +1 -1
  168. package/esm/providers/discord-provider.d.mts +1 -1
  169. package/esm/providers/discord-provider.d.mts.map +1 -1
  170. package/esm/providers/discord-provider.mjs +1 -1
  171. package/esm/providers/discord-provider.mjs.map +1 -1
  172. package/esm/providers/facebook-provider.d.mts +1 -1
  173. package/esm/providers/facebook-provider.d.mts.map +1 -1
  174. package/esm/providers/facebook-provider.mjs +1 -1
  175. package/esm/providers/facebook-provider.mjs.map +1 -1
  176. package/esm/providers/github-provider.d.mts +1 -1
  177. package/esm/providers/github-provider.d.mts.map +1 -1
  178. package/esm/providers/github-provider.mjs +1 -1
  179. package/esm/providers/github-provider.mjs.map +1 -1
  180. package/esm/providers/google-provider.d.mts +1 -1
  181. package/esm/providers/google-provider.d.mts.map +1 -1
  182. package/esm/providers/google-provider.mjs +1 -1
  183. package/esm/providers/google-provider.mjs.map +1 -1
  184. package/esm/providers/linkedin-provider.d.mts +1 -1
  185. package/esm/providers/linkedin-provider.d.mts.map +1 -1
  186. package/esm/providers/linkedin-provider.mjs +1 -1
  187. package/esm/providers/linkedin-provider.mjs.map +1 -1
  188. package/esm/providers/provider-login.d.mts +1 -1
  189. package/esm/providers/provider-login.d.mts.map +1 -1
  190. package/esm/providers/provider-login.mjs +1 -1
  191. package/esm/providers/provider-login.mjs.map +1 -1
  192. package/esm/providers/provider-state-cookie.mjs +1 -1
  193. package/esm/providers/provider-state-cookie.mjs.map +1 -1
  194. package/esm/providers/resolve-provider-user.mjs +1 -1
  195. package/esm/providers/resolve-provider-user.mjs.map +1 -1
  196. package/esm/providers/x-provider.d.mts +1 -1
  197. package/esm/providers/x-provider.d.mts.map +1 -1
  198. package/esm/providers/x-provider.mjs +1 -1
  199. package/esm/providers/x-provider.mjs.map +1 -1
  200. package/esm/services/auth-config.mjs +1 -1
  201. package/esm/services/auth-config.mjs.map +1 -1
  202. package/esm/services/auth-events.d.mts +1 -1
  203. package/esm/services/auth-events.d.mts.map +1 -1
  204. package/esm/services/auth-events.mjs +1 -1
  205. package/esm/services/auth-events.mjs.map +1 -1
  206. package/esm/services/auth.service.d.mts +1 -1
  207. package/esm/services/auth.service.d.mts.map +1 -1
  208. package/esm/services/auth.service.mjs +1 -1
  209. package/esm/services/auth.service.mjs.map +1 -1
  210. package/esm/services/current-user.d.mts +1 -1
  211. package/esm/services/current-user.d.mts.map +1 -1
  212. package/esm/services/current-user.mjs +1 -1
  213. package/esm/services/current-user.mjs.map +1 -1
  214. package/esm/services/email-verification.d.mts +1 -1
  215. package/esm/services/email-verification.d.mts.map +1 -1
  216. package/esm/services/email-verification.mjs +1 -1
  217. package/esm/services/email-verification.mjs.map +1 -1
  218. package/esm/services/generate-jwt-secret.d.mts +1 -1
  219. package/esm/services/generate-jwt-secret.d.mts.map +1 -1
  220. package/esm/services/generate-jwt-secret.mjs +1 -1
  221. package/esm/services/generate-jwt-secret.mjs.map +1 -1
  222. package/esm/services/jwt.d.mts +1 -1
  223. package/esm/services/jwt.d.mts.map +1 -1
  224. package/esm/services/jwt.mjs +1 -1
  225. package/esm/services/jwt.mjs.map +1 -1
  226. package/esm/services/one-time-tokens.d.mts +1 -1
  227. package/esm/services/one-time-tokens.d.mts.map +1 -1
  228. package/esm/services/one-time-tokens.mjs +1 -1
  229. package/esm/services/one-time-tokens.mjs.map +1 -1
  230. package/esm/services/optional-peer.mjs +1 -1
  231. package/esm/services/optional-peer.mjs.map +1 -1
  232. package/esm/services/password-reset.d.mts +1 -1
  233. package/esm/services/password-reset.d.mts.map +1 -1
  234. package/esm/services/password-reset.mjs +1 -1
  235. package/esm/services/password-reset.mjs.map +1 -1
  236. package/esm/services/resolve-request-user.d.mts +1 -1
  237. package/esm/services/resolve-request-user.d.mts.map +1 -1
  238. package/esm/services/resolve-request-user.mjs +1 -1
  239. package/esm/services/resolve-request-user.mjs.map +1 -1
  240. package/esm/services/token-family-operation.mjs +1 -1
  241. package/esm/services/token-family-operation.mjs.map +1 -1
  242. package/esm/services/token-notifications.mjs +1 -1
  243. package/esm/services/token-notifications.mjs.map +1 -1
  244. package/esm/session/page-session.d.mts +1 -1
  245. package/esm/session/page-session.d.mts.map +1 -1
  246. package/esm/session/page-session.mjs +1 -1
  247. package/esm/session/page-session.mjs.map +1 -1
  248. package/esm/utils/auth-error-codes.d.mts +1 -1
  249. package/esm/utils/auth-error-codes.d.mts.map +1 -1
  250. package/esm/utils/auth-error-codes.mjs +1 -1
  251. package/esm/utils/auth-error-codes.mjs.map +1 -1
  252. package/esm/utils/safe-equal.d.mts +1 -1
  253. package/esm/utils/safe-equal.d.mts.map +1 -1
  254. package/esm/utils/safe-equal.mjs +1 -1
  255. package/esm/utils/safe-equal.mjs.map +1 -1
  256. package/esm/utils/token-expiry.d.mts +1 -1
  257. package/esm/utils/token-expiry.d.mts.map +1 -1
  258. package/esm/utils/token-expiry.mjs +1 -1
  259. package/esm/utils/token-expiry.mjs.map +1 -1
  260. package/llms-full.txt +76 -74
  261. package/llms.txt +10 -10
  262. package/package.json +10 -9
  263. package/skills/auth-basics/SKILL.md +10 -10
  264. package/skills/customize-token-storage/SKILL.md +5 -5
  265. package/skills/customize-user-type/SKILL.md +5 -5
  266. package/skills/handle-login-and-logout/SKILL.md +5 -5
  267. package/skills/login-with-providers/SKILL.md +4 -4
  268. package/skills/manage-tokens/SKILL.md +6 -6
  269. package/skills/overview/SKILL.md +12 -12
  270. package/skills/protect-routes/SKILL.md +7 -7
  271. package/skills/register-user/SKILL.md +7 -7
  272. package/skills/run-auth-commands/SKILL.md +4 -4
  273. package/skills/throttle-login-attempts/SKILL.md +7 -5
  274. package/skills/verify-email-and-reset-password/SKILL.md +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"apple-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/apple-provider.ts"],"sourcesContent":["import type {\n AppleProviderConfig,\n AuthProvider,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport type { JoseModule } from \"./google-provider\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://appleid.apple.com/auth/authorize\";\nconst TOKEN_ENDPOINT = \"https://appleid.apple.com/auth/token\";\nconst JWKS_URI = \"https://appleid.apple.com/auth/keys\";\nconst ISSUER = \"https://appleid.apple.com\";\nconst DEFAULT_SCOPES = [\"name\", \"email\"];\n/** Apple's own recommendation for a per-request client secret: minutes, not the 6-month maximum it allows. */\nconst CLIENT_SECRET_TTL_SECONDS = 5 * 60;\n\n/** The `warlock add` feature that installs `jose`. */\nexport const APPLE_AUTH_FEATURE = \"auth-apple\";\n\ntype AppleSignJwtBuilder = {\n setIssuer: (issuer: string) => AppleSignJwtBuilder;\n setIssuedAt: () => AppleSignJwtBuilder;\n setExpirationTime: (time: number) => AppleSignJwtBuilder;\n setAudience: (audience: string) => AppleSignJwtBuilder;\n setSubject: (subject: string) => AppleSignJwtBuilder;\n sign: (key: unknown) => Promise<string>;\n};\n\n/**\n * The slice of `jose` the Apple provider uses on top of {@link JoseModule} —\n * `importPKCS8` and `SignJWT` sign the ES256 client-secret JWT Apple requires\n * instead of a static client secret.\n */\nexport type AppleJoseModule = JoseModule & {\n importPKCS8: (pkcs8: string, alg: string) => Promise<unknown>;\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader: (header: Record<string, unknown>) => AppleSignJwtBuilder;\n };\n};\n\n/** Apple's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet appleKeySet: unknown;\n\n/**\n * Apple (\"Sign in with Apple\") is OpenID Connect over the authorization code\n * flow with PKCE (S256), `state` and `nonce`, the same shape as\n * {@link GoogleProvider} — with two differences that come straight from\n * Apple's own docs (this package has no Apple developer account, so nothing\n * here is proven against a live exchange):\n *\n * 1. Apple has no static client secret. The \"secret\" the token endpoint wants\n * is a fresh ES256 JWT (`iss` = team id, `sub` = client id, `aud` =\n * `https://appleid.apple.com`, signed with the app's private key and the\n * key id as `kid`) — signed once per callback here rather than cached, to\n * avoid ever presenting a stale one.\n * 2. Requesting `name`/`email` (the default scopes) forces\n * `response_mode=form_post`: Apple POSTs `code`, `state` and, on the\n * user's FIRST authorization only, a `user` field — a JSON string with\n * `{ name: { firstName, lastName }, email } ` that never comes back again\n * on a later login. The callback route must therefore accept a POST body,\n * not just a query string, and `request.input` must read that body the\n * same way it already reads a GET query for the other providers.\n *\n * The id_token is verified with `jose` against Apple's JWKS (signature,\n * `iss`, `aud` = the client id, `exp`), then its `nonce` is compared. Apple is\n * also documented to send `email_verified` (and `is_private_email`) as the\n * STRING `\"true\"`/`\"false\"` rather than a JSON boolean on some responses, so\n * both forms are accepted here — treating only the unambiguous `\"false\"` (or\n * `false`) as unverified. A private-relay address (`@privaterelay.appleid.com`)\n * is still a real, working forwarding address and is accepted as any other.\n */\nexport class AppleProvider implements AuthProvider {\n public readonly name = \"apple\";\n /** Apple's default scopes (`name`, `email`) force `response_mode=form_post`. */\n public readonly callbackMode = \"form_post\";\n\n public constructor(private readonly settings: AppleProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const scopes = this.settings.scopes ?? DEFAULT_SCOPES;\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", scopes.join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n // Apple requires form_post whenever name/email scopes are requested.\n if (scopes.includes(\"name\") || scopes.includes(\"email\")) {\n url.searchParams.set(\"response_mode\", \"form_post\");\n }\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<AppleJoseModule>(\"jose\", APPLE_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(jose, code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n emailVerified: isVerifiedFlag(claims.email_verified),\n name: nameFromUserField(query.user),\n raw: claims,\n };\n }\n\n /** Sign a fresh ES256 client-secret JWT — Apple has no static one. */\n private async clientSecret(jose: AppleJoseModule): Promise<string> {\n const key = await jose.importPKCS8(this.settings.privateKey, \"ES256\");\n const now = Math.floor(Date.now() / 1000);\n\n try {\n return await new jose.SignJWT({})\n .setProtectedHeader({ alg: \"ES256\", kid: this.settings.keyId })\n .setIssuer(this.settings.teamId)\n .setIssuedAt()\n .setExpirationTime(now + CLIENT_SECRET_TTL_SECONDS)\n .setAudience(ISSUER)\n .setSubject(this.settings.clientId)\n .sign(key);\n } catch (error) {\n throw new InvalidProviderCallbackError(\"client-secret-signing-failed\", error);\n }\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(\n jose: AppleJoseModule,\n code: string,\n codeVerifier: string,\n ): Promise<string> {\n const clientSecret = await this.clientSecret(jose);\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Apple token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(\n jose: AppleJoseModule,\n idToken: string,\n ): Promise<Record<string, unknown>> {\n appleKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, appleKeySet, {\n issuer: [ISSUER],\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n\n/** Apple's `email_verified` arrives as a JSON boolean on some responses, a `\"true\"`/`\"false\"` string on others. */\nfunction isVerifiedFlag(value: unknown): boolean {\n return value === true || value === \"true\";\n}\n\n/**\n * Apple sends the account name only once, on the FIRST authorization, as a\n * `user` form field holding a JSON string: `{\"name\":{\"firstName\",\"lastName\"}}`.\n * Every later login omits it entirely — callers that need the name must save\n * it themselves the first time.\n */\nfunction nameFromUserField(user: unknown): string | undefined {\n if (typeof user !== \"string\" || user.length === 0) return undefined;\n\n try {\n const parsed = JSON.parse(user) as { name?: { firstName?: string; lastName?: string } };\n const first = parsed.name?.firstName;\n const last = parsed.name?.lastName;\n const name = [first, last].filter((part): part is string => Boolean(part)).join(\" \");\n\n return name.length > 0 ? name : undefined;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"apple-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/apple-provider.ts"],"sourcesContent":["import type {\n AppleProviderConfig,\n AuthProvider,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport type { JoseModule } from \"./google-provider\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://appleid.apple.com/auth/authorize\";\nconst TOKEN_ENDPOINT = \"https://appleid.apple.com/auth/token\";\nconst JWKS_URI = \"https://appleid.apple.com/auth/keys\";\nconst ISSUER = \"https://appleid.apple.com\";\nconst DEFAULT_SCOPES = [\"name\", \"email\"];\n/** Apple's own recommendation for a per-request client secret: minutes, not the 6-month maximum it allows. */\nconst CLIENT_SECRET_TTL_SECONDS = 5 * 60;\n\n/** The `warlock add` feature that installs `jose`. */\nexport const APPLE_AUTH_FEATURE = \"auth-apple\";\n\ntype AppleSignJwtBuilder = {\n setIssuer: (issuer: string) => AppleSignJwtBuilder;\n setIssuedAt: () => AppleSignJwtBuilder;\n setExpirationTime: (time: number) => AppleSignJwtBuilder;\n setAudience: (audience: string) => AppleSignJwtBuilder;\n setSubject: (subject: string) => AppleSignJwtBuilder;\n sign: (key: unknown) => Promise<string>;\n};\n\n/**\n * The slice of `jose` the Apple provider uses on top of {@link JoseModule} —\n * `importPKCS8` and `SignJWT` sign the ES256 client-secret JWT Apple requires\n * instead of a static client secret.\n */\nexport type AppleJoseModule = JoseModule & {\n importPKCS8: (pkcs8: string, alg: string) => Promise<unknown>;\n SignJWT: new (payload: Record<string, unknown>) => {\n setProtectedHeader: (header: Record<string, unknown>) => AppleSignJwtBuilder;\n };\n};\n\n/** Apple's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet appleKeySet: unknown;\n\n/**\n * Apple (\"Sign in with Apple\") is OpenID Connect over the authorization code\n * flow with PKCE (S256), `state` and `nonce`, the same shape as\n * {@link GoogleProvider} — with two differences that come straight from\n * Apple's own docs (this package has no Apple developer account, so nothing\n * here is proven against a live exchange):\n *\n * 1. Apple has no static client secret. The \"secret\" the token endpoint wants\n * is a fresh ES256 JWT (`iss` = team id, `sub` = client id, `aud` =\n * `https://appleid.apple.com`, signed with the app's private key and the\n * key id as `kid`) — signed once per callback here rather than cached, to\n * avoid ever presenting a stale one.\n * 2. Requesting `name`/`email` (the default scopes) forces\n * `response_mode=form_post`: Apple POSTs `code`, `state` and, on the\n * user's FIRST authorization only, a `user` field — a JSON string with\n * `{ name: { firstName, lastName }, email } ` that never comes back again\n * on a later login. The callback route must therefore accept a POST body,\n * not just a query string, and `request.input` must read that body the\n * same way it already reads a GET query for the other providers.\n *\n * The id_token is verified with `jose` against Apple's JWKS (signature,\n * `iss`, `aud` = the client id, `exp`), then its `nonce` is compared. Apple is\n * also documented to send `email_verified` (and `is_private_email`) as the\n * STRING `\"true\"`/`\"false\"` rather than a JSON boolean on some responses, so\n * both forms are accepted here — treating only the unambiguous `\"false\"` (or\n * `false`) as unverified. A private-relay address (`@privaterelay.appleid.com`)\n * is still a real, working forwarding address and is accepted as any other.\n */\nexport class AppleProvider implements AuthProvider {\n public readonly name = \"apple\";\n /** Apple's default scopes (`name`, `email`) force `response_mode=form_post`. */\n public readonly callbackMode = \"form_post\";\n\n public constructor(private readonly settings: AppleProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const scopes = this.settings.scopes ?? DEFAULT_SCOPES;\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", scopes.join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n // Apple requires form_post whenever name/email scopes are requested.\n if (scopes.includes(\"name\") || scopes.includes(\"email\")) {\n url.searchParams.set(\"response_mode\", \"form_post\");\n }\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<AppleJoseModule>(\"jose\", APPLE_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(jose, code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n emailVerified: isVerifiedFlag(claims.email_verified),\n name: nameFromUserField(query.user),\n raw: claims,\n };\n }\n\n /** Sign a fresh ES256 client-secret JWT — Apple has no static one. */\n private async clientSecret(jose: AppleJoseModule): Promise<string> {\n const key = await jose.importPKCS8(this.settings.privateKey, \"ES256\");\n const now = Math.floor(Date.now() / 1000);\n\n try {\n return await new jose.SignJWT({})\n .setProtectedHeader({ alg: \"ES256\", kid: this.settings.keyId })\n .setIssuer(this.settings.teamId)\n .setIssuedAt()\n .setExpirationTime(now + CLIENT_SECRET_TTL_SECONDS)\n .setAudience(ISSUER)\n .setSubject(this.settings.clientId)\n .sign(key);\n } catch (error) {\n throw new InvalidProviderCallbackError(\"client-secret-signing-failed\", error);\n }\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(\n jose: AppleJoseModule,\n code: string,\n codeVerifier: string,\n ): Promise<string> {\n const clientSecret = await this.clientSecret(jose);\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Apple token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(\n jose: AppleJoseModule,\n idToken: string,\n ): Promise<Record<string, unknown>> {\n appleKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, appleKeySet, {\n issuer: [ISSUER],\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n\n/** Apple's `email_verified` arrives as a JSON boolean on some responses, a `\"true\"`/`\"false\"` string on others. */\nfunction isVerifiedFlag(value: unknown): boolean {\n return value === true || value === \"true\";\n}\n\n/**\n * Apple sends the account name only once, on the FIRST authorization, as a\n * `user` form field holding a JSON string: `{\"name\":{\"firstName\",\"lastName\"}}`.\n * Every later login omits it entirely — callers that need the name must save\n * it themselves the first time.\n */\nfunction nameFromUserField(user: unknown): string | undefined {\n if (typeof user !== \"string\" || user.length === 0) return undefined;\n\n try {\n const parsed = JSON.parse(user) as { name?: { firstName?: string; lastName?: string } };\n const first = parsed.name?.firstName;\n const last = parsed.name?.lastName;\n const name = [first, last].filter((part): part is string => Boolean(part)).join(\" \");\n\n return name.length > 0 ? name : undefined;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, DiscordProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/discord-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/discord-provider.d.ts
4
4
  /**
5
5
  * Discord sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
6
6
  * Like GitHub, Discord is not OpenID Connect — there is no `id_token`; the
@@ -1 +1 @@
1
- {"version":3,"file":"discord-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/discord-provider.ts"],"mappings":";;;;;AA8BA;;;;cAAa,eAAA,YAA2B,YAAA;EAAA,iBAGF,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,qBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAtBE;EAAA,QAkDxB,YAAA;EAAA,QAiCA,SAAA;AAAA"}
1
+ {"version":3,"file":"discord-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/discord-provider.ts"],"mappings":";;;;;AA8BA;;;;cAAa,eAAA,YAA2B,YAAA;EAAA,iBAGF,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,qBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAtBE;EAAA,QAkDxB,YAAA;EAAA,QAiCA,SAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { InvalidProviderCallbackError } from "../errors/invalid-provider-callback.error.mjs";
2
2
  import { pkceChallenge } from "./google-provider.mjs";
3
3
 
4
- //#region ../auth/src/providers/discord-provider.ts
4
+ //#region ../../packages/auth/src/providers/discord-provider.ts
5
5
  const AUTHORIZATION_ENDPOINT = "https://discord.com/oauth2/authorize";
6
6
  const TOKEN_ENDPOINT = "https://discord.com/api/oauth2/token";
7
7
  const USER_ENDPOINT = "https://discord.com/api/users/@me";
@@ -1 +1 @@
1
- {"version":3,"file":"discord-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/discord-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n DiscordProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://discord.com/oauth2/authorize\";\nconst TOKEN_ENDPOINT = \"https://discord.com/api/oauth2/token\";\nconst USER_ENDPOINT = \"https://discord.com/api/users/@me\";\nconst DEFAULT_SCOPES = [\"identify\", \"email\"];\n\ntype DiscordUser = {\n id: string;\n username: string;\n global_name?: string | null;\n avatar?: string | null;\n email?: string | null;\n verified?: boolean;\n};\n\n/**\n * Discord sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * Like GitHub, Discord is not OpenID Connect — there is no `id_token`; the\n * access token is redeemed once for `/users/@me` over a direct HTTPS call.\n * No vendor SDK is involved.\n */\nexport class DiscordProvider implements AuthProvider {\n public readonly name = \"discord\";\n\n public constructor(private readonly settings: DiscordProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n\n if (typeof user.id !== \"string\" || user.id.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: user.id,\n email: user.email ?? undefined,\n emailVerified: user.verified === true,\n name: user.global_name ?? user.username,\n avatar: user.avatar\n ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`\n : undefined,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Discord token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<DiscordUser> {\n try {\n const response = await fetch(USER_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n throw new Error(`Discord user endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as DiscordUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n}\n"],"mappings":";;;;AAUA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,CAAC,YAAY,OAAO;;;;;;;AAiB3C,IAAa,kBAAb,MAAqD;CAGnD,AAAO,YAAY,AAAiB,UAAiC;EAAjC;cAFb;CAE+C;CAEtE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EACnD,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,cAAc,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACvE,MAAM,OAAO,MAAM,KAAK,UAAU,WAAW;EAE7C,IAAI,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,WAAW,GACpD,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,KAAK;GACrB,OAAO,KAAK,SAAS;GACrB,eAAe,KAAK,aAAa;GACjC,MAAM,KAAK,eAAe,KAAK;GAC/B,QAAQ,KAAK,SACT,sCAAsC,KAAK,GAAG,GAAG,KAAK,OAAO,QAC7D;GACJ,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,gBAAgB;IAC3C,QAAQ;IACR,SAAS,EAAE,gBAAgB,oCAAoC;IAC/D,MAAM,IAAI,gBAAgB;KACxB;KACA,WAAW,KAAK,SAAS;KACzB,eAAe,KAAK,SAAS;KAC7B,cAAc,KAAK,SAAS;KAC5B,YAAY;KACZ,eAAe;IACjB,CAAC;GACH,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,mCAAmC,SAAS,QAAQ;GAGtE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,WAAW,GACxE,MAAM,IAAI,6BAA6B,sBAAsB;EAG/D,OAAO,KAAK;CACd;CAEA,MAAc,UAAU,aAA2C;EACjE,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,eAAe,EAC1C,SAAS,EAAE,eAAe,UAAU,cAAc,EACpD,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,QAAQ;GAGrE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;CACF;AACF"}
1
+ {"version":3,"file":"discord-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/discord-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n DiscordProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://discord.com/oauth2/authorize\";\nconst TOKEN_ENDPOINT = \"https://discord.com/api/oauth2/token\";\nconst USER_ENDPOINT = \"https://discord.com/api/users/@me\";\nconst DEFAULT_SCOPES = [\"identify\", \"email\"];\n\ntype DiscordUser = {\n id: string;\n username: string;\n global_name?: string | null;\n avatar?: string | null;\n email?: string | null;\n verified?: boolean;\n};\n\n/**\n * Discord sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * Like GitHub, Discord is not OpenID Connect — there is no `id_token`; the\n * access token is redeemed once for `/users/@me` over a direct HTTPS call.\n * No vendor SDK is involved.\n */\nexport class DiscordProvider implements AuthProvider {\n public readonly name = \"discord\";\n\n public constructor(private readonly settings: DiscordProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n\n if (typeof user.id !== \"string\" || user.id.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: user.id,\n email: user.email ?? undefined,\n emailVerified: user.verified === true,\n name: user.global_name ?? user.username,\n avatar: user.avatar\n ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`\n : undefined,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Discord token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<DiscordUser> {\n try {\n const response = await fetch(USER_ENDPOINT, {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n throw new Error(`Discord user endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as DiscordUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n}\n"],"mappings":";;;;AAUA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,CAAC,YAAY,OAAO;;;;;;;AAiB3C,IAAa,kBAAb,MAAqD;CAGnD,AAAO,YAAY,AAAiB,UAAiC;EAAjC;cAFb;CAE+C;CAEtE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EACnD,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,cAAc,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACvE,MAAM,OAAO,MAAM,KAAK,UAAU,WAAW;EAE7C,IAAI,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,WAAW,GACpD,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,KAAK;GACrB,OAAO,KAAK,SAAS;GACrB,eAAe,KAAK,aAAa;GACjC,MAAM,KAAK,eAAe,KAAK;GAC/B,QAAQ,KAAK,SACT,sCAAsC,KAAK,GAAG,GAAG,KAAK,OAAO,QAC7D;GACJ,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,gBAAgB;IAC3C,QAAQ;IACR,SAAS,EAAE,gBAAgB,oCAAoC;IAC/D,MAAM,IAAI,gBAAgB;KACxB;KACA,WAAW,KAAK,SAAS;KACzB,eAAe,KAAK,SAAS;KAC7B,cAAc,KAAK,SAAS;KAC5B,YAAY;KACZ,eAAe;IACjB,CAAC;GACH,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,mCAAmC,SAAS,QAAQ;GAGtE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,WAAW,GACxE,MAAM,IAAI,6BAA6B,sBAAsB;EAG/D,OAAO,KAAK;CACd;CAEA,MAAc,UAAU,aAA2C;EACjE,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,eAAe,EAC1C,SAAS,EAAE,eAAe,UAAU,cAAc,EACpD,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,QAAQ;GAGrE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;CACF;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, FacebookProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/facebook-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/facebook-provider.d.ts
4
4
  /**
5
5
  * Facebook sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
6
6
  * Facebook is not OpenID Connect — there is no `id_token`. Unlike every other
@@ -1 +1 @@
1
- {"version":3,"file":"facebook-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/facebook-provider.ts"],"mappings":";;;;;AAuCA;;;;;;;;;;;;;;;cAAa,gBAAA,YAA4B,YAAA;EAAA,iBAGH,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,sBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAjBZ;EAAA,QA4CV,YAAA;EAAA,QAkCA,SAAA;AAAA"}
1
+ {"version":3,"file":"facebook-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/facebook-provider.ts"],"mappings":";;;;;AAuCA;;;;;;;;;;;;;;;cAAa,gBAAA,YAA4B,YAAA;EAAA,iBAGH,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,sBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAjBZ;EAAA,QA4CV,YAAA;EAAA,QAkCA,SAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { InvalidProviderCallbackError } from "../errors/invalid-provider-callback.error.mjs";
2
2
  import { pkceChallenge } from "./google-provider.mjs";
3
3
 
4
- //#region ../auth/src/providers/facebook-provider.ts
4
+ //#region ../../packages/auth/src/providers/facebook-provider.ts
5
5
  const AUTHORIZATION_ENDPOINT = "https://www.facebook.com/v19.0/dialog/oauth";
6
6
  const TOKEN_ENDPOINT = "https://graph.facebook.com/v19.0/oauth/access_token";
7
7
  const USER_ENDPOINT = "https://graph.facebook.com/v19.0/me";
@@ -1 +1 @@
1
- {"version":3,"file":"facebook-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/facebook-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n FacebookProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://www.facebook.com/v19.0/dialog/oauth\";\nconst TOKEN_ENDPOINT = \"https://graph.facebook.com/v19.0/oauth/access_token\";\nconst USER_ENDPOINT = \"https://graph.facebook.com/v19.0/me\";\nconst DEFAULT_SCOPES = [\"email\", \"public_profile\"];\n\ntype FacebookUser = {\n id: string;\n name?: string;\n email?: string;\n picture?: { data?: { url?: string } };\n};\n\n/**\n * Facebook sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * Facebook is not OpenID Connect — there is no `id_token`. Unlike every other\n * built-in provider, Facebook's token endpoint is redeemed with a `GET`\n * request (query-string parameters, not a POST body) — that shape comes\n * straight from Facebook's Graph API docs, not from a live exchange (this\n * package has no Facebook test app). The profile is then read once from\n * `Graph /me?fields=id,name,email,picture`, over a direct HTTPS call; no\n * Facebook SDK is involved.\n *\n * Facebook only ever returns a confirmed address for `email` — there is no\n * separate verified flag — so an email present on the profile is treated as\n * verified. An app without `email` permission granted, or one that simply has\n * no email on file, gets `email: undefined`, which `resolveProviderUser`\n * already rejects as an unverified/missing email, the same path GitHub's\n * unverified addresses take.\n */\nexport class FacebookProvider implements AuthProvider {\n public readonly name = \"facebook\";\n\n public constructor(private readonly settings: FacebookProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n\n if (typeof user.id !== \"string\" || user.id.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: user.id,\n email: user.email,\n // Facebook does not report a verification flag; a returned address is confirmed.\n emailVerified: typeof user.email === \"string\" && user.email.length > 0,\n name: user.name,\n avatar: user.picture?.data?.url,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown; error?: unknown };\n\n try {\n const url = new URL(TOKEN_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"client_secret\", this.settings.clientSecret);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"code\", code);\n url.searchParams.set(\"code_verifier\", codeVerifier);\n\n const response = await fetch(url.toString(), { headers: { accept: \"application/json\" } });\n\n if (!response.ok) {\n throw new Error(`Facebook token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown; error?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (body.error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", body.error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<FacebookUser> {\n try {\n const url = new URL(USER_ENDPOINT);\n\n url.searchParams.set(\"fields\", \"id,name,email,picture\");\n url.searchParams.set(\"access_token\", accessToken);\n\n const response = await fetch(url.toString(), { headers: { accept: \"application/json\" } });\n\n if (!response.ok) {\n throw new Error(`Facebook /me endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as FacebookUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n}\n"],"mappings":";;;;AAUA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,CAAC,SAAS,gBAAgB;;;;;;;;;;;;;;;;;;AA0BjD,IAAa,mBAAb,MAAsD;CAGpD,AAAO,YAAY,AAAiB,UAAkC;EAAlC;cAFb;CAEgD;CAEvE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EACnD,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,cAAc,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACvE,MAAM,OAAO,MAAM,KAAK,UAAU,WAAW;EAE7C,IAAI,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,WAAW,GACpD,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,KAAK;GACrB,OAAO,KAAK;GAEZ,eAAe,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,SAAS;GACrE,MAAM,KAAK;GACX,QAAQ,KAAK,SAAS,MAAM;GAC5B,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,cAAc;GAElC,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;GACxD,IAAI,aAAa,IAAI,iBAAiB,KAAK,SAAS,YAAY;GAChE,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;GAC9D,IAAI,aAAa,IAAI,QAAQ,IAAI;GACjC,IAAI,aAAa,IAAI,iBAAiB,YAAY;GAElD,MAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;GAExF,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,oCAAoC,SAAS,QAAQ;GAGvE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,KAAK,OACP,MAAM,IAAI,6BAA6B,wBAAwB,KAAK,KAAK;EAG3E,IAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,WAAW,GACxE,MAAM,IAAI,6BAA6B,sBAAsB;EAG/D,OAAO,KAAK;CACd;CAEA,MAAc,UAAU,aAA4C;EAClE,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,aAAa;GAEjC,IAAI,aAAa,IAAI,UAAU,uBAAuB;GACtD,IAAI,aAAa,IAAI,gBAAgB,WAAW;GAEhD,MAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;GAExF,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,QAAQ;GAGrE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;CACF;AACF"}
1
+ {"version":3,"file":"facebook-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/facebook-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n FacebookProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://www.facebook.com/v19.0/dialog/oauth\";\nconst TOKEN_ENDPOINT = \"https://graph.facebook.com/v19.0/oauth/access_token\";\nconst USER_ENDPOINT = \"https://graph.facebook.com/v19.0/me\";\nconst DEFAULT_SCOPES = [\"email\", \"public_profile\"];\n\ntype FacebookUser = {\n id: string;\n name?: string;\n email?: string;\n picture?: { data?: { url?: string } };\n};\n\n/**\n * Facebook sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * Facebook is not OpenID Connect — there is no `id_token`. Unlike every other\n * built-in provider, Facebook's token endpoint is redeemed with a `GET`\n * request (query-string parameters, not a POST body) — that shape comes\n * straight from Facebook's Graph API docs, not from a live exchange (this\n * package has no Facebook test app). The profile is then read once from\n * `Graph /me?fields=id,name,email,picture`, over a direct HTTPS call; no\n * Facebook SDK is involved.\n *\n * Facebook only ever returns a confirmed address for `email` — there is no\n * separate verified flag — so an email present on the profile is treated as\n * verified. An app without `email` permission granted, or one that simply has\n * no email on file, gets `email: undefined`, which `resolveProviderUser`\n * already rejects as an unverified/missing email, the same path GitHub's\n * unverified addresses take.\n */\nexport class FacebookProvider implements AuthProvider {\n public readonly name = \"facebook\";\n\n public constructor(private readonly settings: FacebookProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n\n if (typeof user.id !== \"string\" || user.id.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: user.id,\n email: user.email,\n // Facebook does not report a verification flag; a returned address is confirmed.\n emailVerified: typeof user.email === \"string\" && user.email.length > 0,\n name: user.name,\n avatar: user.picture?.data?.url,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown; error?: unknown };\n\n try {\n const url = new URL(TOKEN_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"client_secret\", this.settings.clientSecret);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"code\", code);\n url.searchParams.set(\"code_verifier\", codeVerifier);\n\n const response = await fetch(url.toString(), { headers: { accept: \"application/json\" } });\n\n if (!response.ok) {\n throw new Error(`Facebook token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown; error?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (body.error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", body.error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<FacebookUser> {\n try {\n const url = new URL(USER_ENDPOINT);\n\n url.searchParams.set(\"fields\", \"id,name,email,picture\");\n url.searchParams.set(\"access_token\", accessToken);\n\n const response = await fetch(url.toString(), { headers: { accept: \"application/json\" } });\n\n if (!response.ok) {\n throw new Error(`Facebook /me endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as FacebookUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n}\n"],"mappings":";;;;AAUA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,CAAC,SAAS,gBAAgB;;;;;;;;;;;;;;;;;;AA0BjD,IAAa,mBAAb,MAAsD;CAGpD,AAAO,YAAY,AAAiB,UAAkC;EAAlC;cAFb;CAEgD;CAEvE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EACnD,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,cAAc,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACvE,MAAM,OAAO,MAAM,KAAK,UAAU,WAAW;EAE7C,IAAI,OAAO,KAAK,OAAO,YAAY,KAAK,GAAG,WAAW,GACpD,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,KAAK;GACrB,OAAO,KAAK;GAEZ,eAAe,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,SAAS;GACrE,MAAM,KAAK;GACX,QAAQ,KAAK,SAAS,MAAM;GAC5B,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,cAAc;GAElC,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;GACxD,IAAI,aAAa,IAAI,iBAAiB,KAAK,SAAS,YAAY;GAChE,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;GAC9D,IAAI,aAAa,IAAI,QAAQ,IAAI;GACjC,IAAI,aAAa,IAAI,iBAAiB,YAAY;GAElD,MAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;GAExF,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,oCAAoC,SAAS,QAAQ;GAGvE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,KAAK,OACP,MAAM,IAAI,6BAA6B,wBAAwB,KAAK,KAAK;EAG3E,IAAI,OAAO,KAAK,iBAAiB,YAAY,KAAK,aAAa,WAAW,GACxE,MAAM,IAAI,6BAA6B,sBAAsB;EAG/D,OAAO,KAAK;CACd;CAEA,MAAc,UAAU,aAA4C;EAClE,IAAI;GACF,MAAM,MAAM,IAAI,IAAI,aAAa;GAEjC,IAAI,aAAa,IAAI,UAAU,uBAAuB;GACtD,IAAI,aAAa,IAAI,gBAAgB,WAAW;GAEhD,MAAM,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;GAExF,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,kCAAkC,SAAS,QAAQ;GAGrE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;CACF;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, GitHubProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/github-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/github-provider.d.ts
4
4
  /**
5
5
  * GitHub sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
6
6
  * GitHub is not OpenID Connect — there is no `id_token` and nothing to verify
@@ -1 +1 @@
1
- {"version":3,"file":"github-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/github-provider.ts"],"mappings":";;;;;AA0CA;;;;;;;;cAAa,cAAA,YAA0B,YAAA;EAAA,iBAGD,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,oBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAnBA;EAAA,QA8CtB,YAAA;EAAA,QAwCA,SAAA;EAtFgC;EAAA,QA2GhC,iBAAA;AAAA"}
1
+ {"version":3,"file":"github-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/github-provider.ts"],"mappings":";;;;;AA0CA;;;;;;;;cAAa,cAAA,YAA0B,YAAA;EAAA,iBAGD,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,oBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAnBA;EAAA,QA8CtB,YAAA;EAAA,QAwCA,SAAA;EAtFgC;EAAA,QA2GhC,iBAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { InvalidProviderCallbackError } from "../errors/invalid-provider-callback.error.mjs";
2
2
  import { pkceChallenge } from "./google-provider.mjs";
3
3
 
4
- //#region ../auth/src/providers/github-provider.ts
4
+ //#region ../../packages/auth/src/providers/github-provider.ts
5
5
  const AUTHORIZATION_ENDPOINT = "https://github.com/login/oauth/authorize";
6
6
  const TOKEN_ENDPOINT = "https://github.com/login/oauth/access_token";
7
7
  const USER_ENDPOINT = "https://api.github.com/user";
@@ -1 +1 @@
1
- {"version":3,"file":"github-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/github-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n GitHubProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://github.com/login/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://github.com/login/oauth/access_token\";\nconst USER_ENDPOINT = \"https://api.github.com/user\";\nconst EMAILS_ENDPOINT = \"https://api.github.com/user/emails\";\nconst DEFAULT_SCOPES = [\"read:user\", \"user:email\"];\n\n/** GitHub requires a `User-Agent`; identify the framework, not the app. */\nconst USER_AGENT = \"warlock.js-auth\";\n\ntype GitHubUser = {\n id: number;\n login: string;\n name?: string | null;\n avatar_url?: string;\n};\n\ntype GitHubEmail = {\n email: string;\n primary: boolean;\n verified: boolean;\n};\n\n/**\n * GitHub sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * GitHub is not OpenID Connect — there is no `id_token` and nothing to verify\n * against a JWKS. The access token is redeemed once for the profile, over a\n * direct HTTPS call to GitHub's API; no vendor SDK is involved.\n *\n * The account email is fetched separately from `/user/emails`, because\n * `/user.email` is `null` unless the user made it public — the emails\n * endpoint is also where GitHub reports whether an address is verified.\n */\nexport class GitHubProvider implements AuthProvider {\n public readonly name = \"github\";\n\n public constructor(private readonly settings: GitHubProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n const email = await this.fetchPrimaryEmail(accessToken);\n\n if (typeof user.id !== \"number\") {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: String(user.id),\n email: email?.email,\n emailVerified: email?.verified === true,\n name: user.name ?? user.login,\n avatar: user.avatar_url,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown; error?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`GitHub token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown; error?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n // GitHub answers 200 even on a rejected code, with `error` set instead.\n if (body.error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", body.error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<GitHubUser> {\n try {\n const response = await fetch(USER_ENDPOINT, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n accept: \"application/vnd.github+json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (!response.ok) {\n throw new Error(`GitHub user endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as GitHubUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n\n /** The primary, verified email — `undefined` when none is both primary and verified. */\n private async fetchPrimaryEmail(accessToken: string): Promise<GitHubEmail | undefined> {\n let emails: GitHubEmail[];\n\n try {\n const response = await fetch(EMAILS_ENDPOINT, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n accept: \"application/vnd.github+json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (!response.ok) {\n throw new Error(`GitHub emails endpoint answered ${response.status}`);\n }\n\n emails = (await response.json()) as GitHubEmail[];\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n\n return emails.find((email) => email.primary && email.verified);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"github-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/github-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n GitHubProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://github.com/login/oauth/authorize\";\nconst TOKEN_ENDPOINT = \"https://github.com/login/oauth/access_token\";\nconst USER_ENDPOINT = \"https://api.github.com/user\";\nconst EMAILS_ENDPOINT = \"https://api.github.com/user/emails\";\nconst DEFAULT_SCOPES = [\"read:user\", \"user:email\"];\n\n/** GitHub requires a `User-Agent`; identify the framework, not the app. */\nconst USER_AGENT = \"warlock.js-auth\";\n\ntype GitHubUser = {\n id: number;\n login: string;\n name?: string | null;\n avatar_url?: string;\n};\n\ntype GitHubEmail = {\n email: string;\n primary: boolean;\n verified: boolean;\n};\n\n/**\n * GitHub sign-in: plain OAuth 2 authorization code flow with PKCE (S256).\n * GitHub is not OpenID Connect — there is no `id_token` and nothing to verify\n * against a JWKS. The access token is redeemed once for the profile, over a\n * direct HTTPS call to GitHub's API; no vendor SDK is involved.\n *\n * The account email is fetched separately from `/user/emails`, because\n * `/user.email` is `null` unless the user made it public — the emails\n * endpoint is also where GitHub reports whether an address is verified.\n */\nexport class GitHubProvider implements AuthProvider {\n public readonly name = \"github\";\n\n public constructor(private readonly settings: GitHubProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const accessToken = await this.exchangeCode(code, expected.codeVerifier);\n const user = await this.fetchUser(accessToken);\n const email = await this.fetchPrimaryEmail(accessToken);\n\n if (typeof user.id !== \"number\") {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: String(user.id),\n email: email?.email,\n emailVerified: email?.verified === true,\n name: user.name ?? user.login,\n avatar: user.avatar_url,\n raw: user,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an access token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { access_token?: unknown; error?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`GitHub token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { access_token?: unknown; error?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n // GitHub answers 200 even on a rejected code, with `error` set instead.\n if (body.error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", body.error);\n }\n\n if (typeof body.access_token !== \"string\" || body.access_token.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-access-token\");\n }\n\n return body.access_token;\n }\n\n private async fetchUser(accessToken: string): Promise<GitHubUser> {\n try {\n const response = await fetch(USER_ENDPOINT, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n accept: \"application/vnd.github+json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (!response.ok) {\n throw new Error(`GitHub user endpoint answered ${response.status}`);\n }\n\n return (await response.json()) as GitHubUser;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n }\n\n /** The primary, verified email — `undefined` when none is both primary and verified. */\n private async fetchPrimaryEmail(accessToken: string): Promise<GitHubEmail | undefined> {\n let emails: GitHubEmail[];\n\n try {\n const response = await fetch(EMAILS_ENDPOINT, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n accept: \"application/vnd.github+json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (!response.ok) {\n throw new Error(`GitHub emails endpoint answered ${response.status}`);\n }\n\n emails = (await response.json()) as GitHubEmail[];\n } catch (error) {\n throw new InvalidProviderCallbackError(\"profile-fetch-failed\", error);\n }\n\n return emails.find((email) => email.primary && email.verified);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, GoogleProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/google-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/google-provider.d.ts
4
4
  /** The `warlock add` feature that installs `jose`. */
5
5
  declare const GOOGLE_AUTH_FEATURE = "auth-google";
6
6
  /** The slice of `jose` the Google provider uses — declared locally because `jose` is an optional peer. */
@@ -1 +1 @@
1
- {"version":3,"file":"google-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/google-provider.ts"],"mappings":";;;;cAkBa,mBAAA;AAAb;AAAA,KAGY,UAAA;EACV,kBAAA,GAAqB,GAAA,EAAK,GAAA;EAC1B,SAAA,GACE,KAAA,UACA,GAAA,WACA,OAAA;IAAW,MAAA;IAAkB,QAAA;EAAA,MAC1B,OAAA;IAAU,OAAA,EAAS,MAAA;EAAA;AAAA;;iBAOV,aAAA,CAAc,YAAoB;;;;;;;cAUrC,cAAA,YAA0B,YAAA;EAAA,iBAGD,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,oBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAelB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAxCZ;EAAA,QA0EV,YAAA;EA1EgB;EAAA,QA4GhB,aAAA;AAAA"}
1
+ {"version":3,"file":"google-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/google-provider.ts"],"mappings":";;;;cAkBa,mBAAA;AAAb;AAAA,KAGY,UAAA;EACV,kBAAA,GAAqB,GAAA,EAAK,GAAA;EAC1B,SAAA,GACE,KAAA,UACA,GAAA,WACA,OAAA;IAAW,MAAA;IAAkB,QAAA;EAAA,MAC1B,OAAA;IAAU,OAAA,EAAS,MAAA;EAAA;AAAA;;iBAOV,aAAA,CAAc,YAAoB;;;;;;;cAUrC,cAAA,YAA0B,YAAA;EAAA,iBAGD,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,oBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAelB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAxCZ;EAAA,QA0EV,YAAA;EA1EgB;EAAA,QA4GhB,aAAA;AAAA"}
@@ -2,7 +2,7 @@ import { InvalidProviderCallbackError } from "../errors/invalid-provider-callbac
2
2
  import { loadOptionalPeer } from "../services/optional-peer.mjs";
3
3
  import { createHash } from "node:crypto";
4
4
 
5
- //#region ../auth/src/providers/google-provider.ts
5
+ //#region ../../packages/auth/src/providers/google-provider.ts
6
6
  const AUTHORIZATION_ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth";
7
7
  const TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
8
8
  const JWKS_URI = "https://www.googleapis.com/oauth2/v3/certs";
@@ -1 +1 @@
1
- {"version":3,"file":"google-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/google-provider.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type {\n AuthProvider,\n GoogleProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://accounts.google.com/o/oauth2/v2/auth\";\nconst TOKEN_ENDPOINT = \"https://oauth2.googleapis.com/token\";\nconst JWKS_URI = \"https://www.googleapis.com/oauth2/v3/certs\";\nconst ISSUERS = [\"https://accounts.google.com\", \"accounts.google.com\"];\nconst DEFAULT_SCOPES = [\"openid\", \"email\", \"profile\"];\n\n/** The `warlock add` feature that installs `jose`. */\nexport const GOOGLE_AUTH_FEATURE = \"auth-google\";\n\n/** The slice of `jose` the Google provider uses — declared locally because `jose` is an optional peer. */\nexport type JoseModule = {\n createRemoteJWKSet: (url: URL) => unknown;\n jwtVerify: (\n token: string,\n key: unknown,\n options: { issuer: string[]; audience: string },\n ) => Promise<{ payload: Record<string, unknown> }>;\n};\n\n/** Google's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet googleKeySet: unknown;\n\n/** PKCE S256 code challenge for a verifier. */\nexport function pkceChallenge(codeVerifier: string): string {\n return createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n\n/**\n * Google sign-in: OpenID Connect authorization code flow with PKCE (S256),\n * `state` and `nonce`. The code is exchanged with plain `fetch`; the id_token\n * is verified with `jose` against Google's JWKS (signature, `iss`, `aud` = the\n * client id, `exp`), then its `nonce` is compared. No Google SDK.\n */\nexport class GoogleProvider implements AuthProvider {\n public readonly name = \"google\";\n\n public constructor(private readonly settings: GoogleProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<JoseModule>(\"jose\", GOOGLE_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n // Google sends a boolean; anything else counts as NOT verified.\n emailVerified: claims.email_verified === true,\n name: typeof claims.name === \"string\" ? claims.name : undefined,\n avatar: typeof claims.picture === \"string\" ? claims.picture : undefined,\n raw: claims,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Google token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(jose: JoseModule, idToken: string): Promise<Record<string, unknown>> {\n googleKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, googleKeySet, {\n issuer: ISSUERS,\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"google-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/google-provider.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type {\n AuthProvider,\n GoogleProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://accounts.google.com/o/oauth2/v2/auth\";\nconst TOKEN_ENDPOINT = \"https://oauth2.googleapis.com/token\";\nconst JWKS_URI = \"https://www.googleapis.com/oauth2/v3/certs\";\nconst ISSUERS = [\"https://accounts.google.com\", \"accounts.google.com\"];\nconst DEFAULT_SCOPES = [\"openid\", \"email\", \"profile\"];\n\n/** The `warlock add` feature that installs `jose`. */\nexport const GOOGLE_AUTH_FEATURE = \"auth-google\";\n\n/** The slice of `jose` the Google provider uses — declared locally because `jose` is an optional peer. */\nexport type JoseModule = {\n createRemoteJWKSet: (url: URL) => unknown;\n jwtVerify: (\n token: string,\n key: unknown,\n options: { issuer: string[]; audience: string },\n ) => Promise<{ payload: Record<string, unknown> }>;\n};\n\n/** Google's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet googleKeySet: unknown;\n\n/** PKCE S256 code challenge for a verifier. */\nexport function pkceChallenge(codeVerifier: string): string {\n return createHash(\"sha256\").update(codeVerifier).digest(\"base64url\");\n}\n\n/**\n * Google sign-in: OpenID Connect authorization code flow with PKCE (S256),\n * `state` and `nonce`. The code is exchanged with plain `fetch`; the id_token\n * is verified with `jose` against Google's JWKS (signature, `iss`, `aud` = the\n * client id, `exp`), then its `nonce` is compared. No Google SDK.\n */\nexport class GoogleProvider implements AuthProvider {\n public readonly name = \"google\";\n\n public constructor(private readonly settings: GoogleProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<JoseModule>(\"jose\", GOOGLE_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n // Google sends a boolean; anything else counts as NOT verified.\n emailVerified: claims.email_verified === true,\n name: typeof claims.name === \"string\" ? claims.name : undefined,\n avatar: typeof claims.picture === \"string\" ? claims.picture : undefined,\n raw: claims,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Google token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(jose: JoseModule, idToken: string): Promise<Record<string, unknown>> {\n googleKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, googleKeySet, {\n issuer: ISSUERS,\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, LinkedInProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/linkedin-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/linkedin-provider.d.ts
4
4
  /** The `warlock add` feature that installs `jose`. */
5
5
  declare const LINKEDIN_AUTH_FEATURE = "auth-linkedin";
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"linkedin-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/linkedin-provider.ts"],"mappings":";;;;cAmBa,qBAAA;AAAb;;;;AAAkC;AAkBlC;;;;;;;;AAlBA,cAkBa,gBAAA,YAA4B,YAAA;EAAA,iBAGH,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,sBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAelB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EApBA;EAAA,QAsDtB,YAAA;;UAkCA,aAAA;AAAA"}
1
+ {"version":3,"file":"linkedin-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/linkedin-provider.ts"],"mappings":";;;;cAmBa,qBAAA;AAAb;;;;AAAkC;AAkBlC;;;;;;;;AAlBA,cAkBa,gBAAA,YAA4B,YAAA;EAAA,iBAGH,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,sBAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAelB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EApBA;EAAA,QAsDtB,YAAA;;UAkCA,aAAA;AAAA"}
@@ -2,7 +2,7 @@ import { InvalidProviderCallbackError } from "../errors/invalid-provider-callbac
2
2
  import { loadOptionalPeer } from "../services/optional-peer.mjs";
3
3
  import { pkceChallenge } from "./google-provider.mjs";
4
4
 
5
- //#region ../auth/src/providers/linkedin-provider.ts
5
+ //#region ../../packages/auth/src/providers/linkedin-provider.ts
6
6
  const AUTHORIZATION_ENDPOINT = "https://www.linkedin.com/oauth/v2/authorization";
7
7
  const TOKEN_ENDPOINT = "https://www.linkedin.com/oauth/v2/accessToken";
8
8
  const JWKS_URI = "https://www.linkedin.com/oauth/openid/jwks";
@@ -1 +1 @@
1
- {"version":3,"file":"linkedin-provider.mjs","names":[],"sources":["../../../../../../../auth/src/providers/linkedin-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n LinkedInProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport type { JoseModule } from \"./google-provider\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://www.linkedin.com/oauth/v2/authorization\";\nconst TOKEN_ENDPOINT = \"https://www.linkedin.com/oauth/v2/accessToken\";\nconst JWKS_URI = \"https://www.linkedin.com/oauth/openid/jwks\";\nconst ISSUERS = [\"https://www.linkedin.com\", \"https://www.linkedin.com/oauth\"];\nconst DEFAULT_SCOPES = [\"openid\", \"profile\", \"email\"];\n\n/** The `warlock add` feature that installs `jose`. */\nexport const LINKEDIN_AUTH_FEATURE = \"auth-linkedin\";\n\n/** LinkedIn's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet linkedinKeySet: unknown;\n\n/**\n * LinkedIn sign-in: OpenID Connect authorization code flow with PKCE (S256)\n * and `state` and `nonce` — LinkedIn's \"Sign In with LinkedIn using OpenID\n * Connect\" product, the same shape as {@link GoogleProvider}. The code is\n * exchanged with plain `fetch`; the id_token is verified with `jose` against\n * LinkedIn's JWKS (signature, `iss`, `aud` = the client id, `exp`), then its\n * `nonce` is compared. No LinkedIn SDK.\n *\n * The exact issuer string LinkedIn signs (`ISSUERS` above) and whether its\n * token endpoint honours `code_verifier` are asserted from LinkedIn's public\n * OIDC docs, not from a live exchange — this package has no LinkedIn test\n * credentials. See the worker report for what is and is not proven.\n */\nexport class LinkedInProvider implements AuthProvider {\n public readonly name = \"linkedin\";\n\n public constructor(private readonly settings: LinkedInProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<JoseModule>(\"jose\", LINKEDIN_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n // LinkedIn sends a boolean; anything else counts as NOT verified.\n emailVerified: claims.email_verified === true,\n name: typeof claims.name === \"string\" ? claims.name : undefined,\n avatar: typeof claims.picture === \"string\" ? claims.picture : undefined,\n raw: claims,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`LinkedIn token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(jose: JoseModule, idToken: string): Promise<Record<string, unknown>> {\n linkedinKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, linkedinKeySet, {\n issuer: ISSUERS,\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,WAAW;AACjB,MAAM,UAAU,CAAC,4BAA4B,gCAAgC;AAC7E,MAAM,iBAAiB;CAAC;CAAU;CAAW;AAAO;;AAGpD,MAAa,wBAAwB;;AAGrC,IAAI;;;;;;;;;;;;;;AAeJ,IAAa,mBAAb,MAAsD;CAGpD,AAAO,YAAY,AAAiB,UAAkC;EAAlC;cAFb;CAEgD;CAEvE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EAEnD,MAAM,OAAO,MAAM,iBAA6B,QAAQ,qBAAqB;EAE7E,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,UAAU,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACnE,MAAM,SAAS,MAAM,KAAK,cAAc,MAAM,OAAO;EAErD,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,SAAS,OAChE,MAAM,IAAI,6BAA6B,gBAAgB;EAGzD,IAAI,OAAO,OAAO,QAAQ,YAAY,OAAO,IAAI,WAAW,GAC1D,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,OAAO;GACvB,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;GAEzD,eAAe,OAAO,mBAAmB;GACzC,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GACtD,QAAQ,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;GAC9D,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,gBAAgB;IAC3C,QAAQ;IACR,SAAS,EAAE,gBAAgB,oCAAoC;IAC/D,MAAM,IAAI,gBAAgB;KACxB;KACA,WAAW,KAAK,SAAS;KACzB,eAAe,KAAK,SAAS;KAC7B,cAAc,KAAK,SAAS;KAC5B,YAAY;KACZ,eAAe;IACjB,CAAC;GACH,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,oCAAoC,SAAS,QAAQ;GAGvE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,OAAO,KAAK,aAAa,UAC3B,MAAM,IAAI,6BAA6B,kBAAkB;EAG3D,OAAO,KAAK;CACd;;CAGA,MAAc,cAAc,MAAkB,SAAmD;EAC/F,mBAAmB,KAAK,mBAAmB,IAAI,IAAI,QAAQ,CAAC;EAE5D,IAAI;GACF,MAAM,EAAE,YAAY,MAAM,KAAK,UAAU,SAAS,gBAAgB;IAChE,QAAQ;IACR,UAAU,KAAK,SAAS;GAC1B,CAAC;GAED,OAAO;EACT,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,oBAAoB,KAAK;EAClE;CACF;AACF"}
1
+ {"version":3,"file":"linkedin-provider.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/linkedin-provider.ts"],"sourcesContent":["import type {\n AuthProvider,\n LinkedInProviderConfig,\n ProviderAuthorizationState,\n ProviderCallbackParams,\n ProviderProfile,\n} from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport type { JoseModule } from \"./google-provider\";\nimport { pkceChallenge } from \"./google-provider\";\n\nconst AUTHORIZATION_ENDPOINT = \"https://www.linkedin.com/oauth/v2/authorization\";\nconst TOKEN_ENDPOINT = \"https://www.linkedin.com/oauth/v2/accessToken\";\nconst JWKS_URI = \"https://www.linkedin.com/oauth/openid/jwks\";\nconst ISSUERS = [\"https://www.linkedin.com\", \"https://www.linkedin.com/oauth\"];\nconst DEFAULT_SCOPES = [\"openid\", \"profile\", \"email\"];\n\n/** The `warlock add` feature that installs `jose`. */\nexport const LINKEDIN_AUTH_FEATURE = \"auth-linkedin\";\n\n/** LinkedIn's key set, created once per process; `jose` caches the fetched JWKS inside it. */\nlet linkedinKeySet: unknown;\n\n/**\n * LinkedIn sign-in: OpenID Connect authorization code flow with PKCE (S256)\n * and `state` and `nonce` — LinkedIn's \"Sign In with LinkedIn using OpenID\n * Connect\" product, the same shape as {@link GoogleProvider}. The code is\n * exchanged with plain `fetch`; the id_token is verified with `jose` against\n * LinkedIn's JWKS (signature, `iss`, `aud` = the client id, `exp`), then its\n * `nonce` is compared. No LinkedIn SDK.\n *\n * The exact issuer string LinkedIn signs (`ISSUERS` above) and whether its\n * token endpoint honours `code_verifier` are asserted from LinkedIn's public\n * OIDC docs, not from a live exchange — this package has no LinkedIn test\n * credentials. See the worker report for what is and is not proven.\n */\nexport class LinkedInProvider implements AuthProvider {\n public readonly name = \"linkedin\";\n\n public constructor(private readonly settings: LinkedInProviderConfig) {}\n\n public authorizationUrl(state: ProviderAuthorizationState): string {\n const url = new URL(AUTHORIZATION_ENDPOINT);\n\n url.searchParams.set(\"client_id\", this.settings.clientId);\n url.searchParams.set(\"redirect_uri\", this.settings.redirectUri);\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", (this.settings.scopes ?? DEFAULT_SCOPES).join(\" \"));\n url.searchParams.set(\"state\", state.state);\n url.searchParams.set(\"nonce\", state.nonce);\n url.searchParams.set(\"code_challenge\", pkceChallenge(state.codeVerifier));\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n\n return url.toString();\n }\n\n public async handleCallback({\n query,\n expected,\n }: ProviderCallbackParams): Promise<ProviderProfile> {\n // Load the peer first: a missing SDK fails loudly before any network call.\n const jose = await loadOptionalPeer<JoseModule>(\"jose\", LINKEDIN_AUTH_FEATURE);\n\n const code = query.code;\n\n if (typeof code !== \"string\" || code.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-code\");\n }\n\n const idToken = await this.exchangeCode(code, expected.codeVerifier);\n const claims = await this.verifyIdToken(jose, idToken);\n\n if (typeof claims.nonce !== \"string\" || claims.nonce !== expected.nonce) {\n throw new InvalidProviderCallbackError(\"nonce-mismatch\");\n }\n\n if (typeof claims.sub !== \"string\" || claims.sub.length === 0) {\n throw new InvalidProviderCallbackError(\"missing-subject\");\n }\n\n return {\n provider: this.name,\n providerUserId: claims.sub,\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n // LinkedIn sends a boolean; anything else counts as NOT verified.\n emailVerified: claims.email_verified === true,\n name: typeof claims.name === \"string\" ? claims.name : undefined,\n avatar: typeof claims.picture === \"string\" ? claims.picture : undefined,\n raw: claims,\n };\n }\n\n /** Redeem the authorization code (with the PKCE verifier) for an id_token. */\n private async exchangeCode(code: string, codeVerifier: string): Promise<string> {\n let body: { id_token?: unknown };\n\n try {\n const response = await fetch(TOKEN_ENDPOINT, {\n method: \"POST\",\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n code,\n client_id: this.settings.clientId,\n client_secret: this.settings.clientSecret,\n redirect_uri: this.settings.redirectUri,\n grant_type: \"authorization_code\",\n code_verifier: codeVerifier,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`LinkedIn token endpoint answered ${response.status}`);\n }\n\n body = (await response.json()) as { id_token?: unknown };\n } catch (error) {\n throw new InvalidProviderCallbackError(\"code-exchange-failed\", error);\n }\n\n if (typeof body.id_token !== \"string\") {\n throw new InvalidProviderCallbackError(\"missing-id-token\");\n }\n\n return body.id_token;\n }\n\n /** Verify signature, issuer, audience and expiry; resolve the claims. */\n private async verifyIdToken(jose: JoseModule, idToken: string): Promise<Record<string, unknown>> {\n linkedinKeySet ??= jose.createRemoteJWKSet(new URL(JWKS_URI));\n\n try {\n const { payload } = await jose.jwtVerify(idToken, linkedinKeySet, {\n issuer: ISSUERS,\n audience: this.settings.clientId,\n });\n\n return payload;\n } catch (error) {\n throw new InvalidProviderCallbackError(\"invalid-id-token\", error);\n }\n }\n}\n"],"mappings":";;;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,iBAAiB;AACvB,MAAM,WAAW;AACjB,MAAM,UAAU,CAAC,4BAA4B,gCAAgC;AAC7E,MAAM,iBAAiB;CAAC;CAAU;CAAW;AAAO;;AAGpD,MAAa,wBAAwB;;AAGrC,IAAI;;;;;;;;;;;;;;AAeJ,IAAa,mBAAb,MAAsD;CAGpD,AAAO,YAAY,AAAiB,UAAkC;EAAlC;cAFb;CAEgD;CAEvE,AAAO,iBAAiB,OAA2C;EACjE,MAAM,MAAM,IAAI,IAAI,sBAAsB;EAE1C,IAAI,aAAa,IAAI,aAAa,KAAK,SAAS,QAAQ;EACxD,IAAI,aAAa,IAAI,gBAAgB,KAAK,SAAS,WAAW;EAC9D,IAAI,aAAa,IAAI,iBAAiB,MAAM;EAC5C,IAAI,aAAa,IAAI,UAAU,KAAK,SAAS,UAAU,eAAc,CAAE,KAAK,GAAG,CAAC;EAChF,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,SAAS,MAAM,KAAK;EACzC,IAAI,aAAa,IAAI,kBAAkB,cAAc,MAAM,YAAY,CAAC;EACxE,IAAI,aAAa,IAAI,yBAAyB,MAAM;EAEpD,OAAO,IAAI,SAAS;CACtB;CAEA,MAAa,eAAe,EAC1B,OACA,YACmD;EAEnD,MAAM,OAAO,MAAM,iBAA6B,QAAQ,qBAAqB;EAE7E,MAAM,OAAO,MAAM;EAEnB,IAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAC9C,MAAM,IAAI,6BAA6B,cAAc;EAGvD,MAAM,UAAU,MAAM,KAAK,aAAa,MAAM,SAAS,YAAY;EACnE,MAAM,SAAS,MAAM,KAAK,cAAc,MAAM,OAAO;EAErD,IAAI,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,SAAS,OAChE,MAAM,IAAI,6BAA6B,gBAAgB;EAGzD,IAAI,OAAO,OAAO,QAAQ,YAAY,OAAO,IAAI,WAAW,GAC1D,MAAM,IAAI,6BAA6B,iBAAiB;EAG1D,OAAO;GACL,UAAU,KAAK;GACf,gBAAgB,OAAO;GACvB,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;GAEzD,eAAe,OAAO,mBAAmB;GACzC,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GACtD,QAAQ,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;GAC9D,KAAK;EACP;CACF;;CAGA,MAAc,aAAa,MAAc,cAAuC;EAC9E,IAAI;EAEJ,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,gBAAgB;IAC3C,QAAQ;IACR,SAAS,EAAE,gBAAgB,oCAAoC;IAC/D,MAAM,IAAI,gBAAgB;KACxB;KACA,WAAW,KAAK,SAAS;KACzB,eAAe,KAAK,SAAS;KAC7B,cAAc,KAAK,SAAS;KAC5B,YAAY;KACZ,eAAe;IACjB,CAAC;GACH,CAAC;GAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,oCAAoC,SAAS,QAAQ;GAGvE,OAAQ,MAAM,SAAS,KAAK;EAC9B,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,wBAAwB,KAAK;EACtE;EAEA,IAAI,OAAO,KAAK,aAAa,UAC3B,MAAM,IAAI,6BAA6B,kBAAkB;EAG3D,OAAO,KAAK;CACd;;CAGA,MAAc,cAAc,MAAkB,SAAmD;EAC/F,mBAAmB,KAAK,mBAAmB,IAAI,IAAI,QAAQ,CAAC;EAE5D,IAAI;GACF,MAAM,EAAE,YAAY,MAAM,KAAK,UAAU,SAAS,gBAAgB;IAChE,QAAQ;IACR,UAAU,KAAK,SAAS;GAC1B,CAAC;GAED,OAAO;EACT,SAAS,OAAO;GACd,MAAM,IAAI,6BAA6B,oBAAoB,KAAK;EAClE;CACF;AACF"}
@@ -4,7 +4,7 @@ import { DeviceInfo, LoginResult } from "../contracts/types.mjs";
4
4
  import { Request, Response } from "@warlock.js/core";
5
5
  import { ChildModel } from "@warlock.js/cascade";
6
6
 
7
- //#region ../auth/src/providers/provider-login.d.ts
7
+ //#region ../../packages/auth/src/providers/provider-login.d.ts
8
8
  /**
9
9
  * Resolve a provider by name: a built-in name (`google`, `github`, `discord`,
10
10
  * `linkedin`, `apple`, `facebook`, `x`) from `auth.providers.<name>`, anything
@@ -1 +1 @@
1
- {"version":3,"file":"provider-login.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/provider-login.ts"],"mappings":";;;;;;;;;AA+DA;;;iBAAgB,mBAAA,CAAoB,IAAA,WAAe,YAAY;AAAA;AA8B/D;;;;;;;;;AA9B+D,iBA8BzC,kBAAA,CACpB,QAAA,EAAU,QAAA,EACV,YAAA,WACC,OAAO;AAgCV;;;;;;;;;;;;;;;;AAAA,iBAAsB,qBAAA,WAAgC,IAAA,EACpD,KAAA,EAAO,UAAA,CAAW,CAAA,GAClB,YAAA,UACA,OAAA,EAAS,OAAA,EACT,QAAA,EAAU,QAAA,EACV,UAAA,GAAa,UAAA,GACZ,OAAA,CAAQ,WAAA,CAAY,CAAA"}
1
+ {"version":3,"file":"provider-login.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/provider-login.ts"],"mappings":";;;;;;;;;AA+DA;;;iBAAgB,mBAAA,CAAoB,IAAA,WAAe,YAAY;AAAA;AA8B/D;;;;;;;;;AA9B+D,iBA8BzC,kBAAA,CACpB,QAAA,EAAU,QAAA,EACV,YAAA,WACC,OAAO;AAgCV;;;;;;;;;;;;;;;;AAAA,iBAAsB,qBAAA,WAAgC,IAAA,EACpD,KAAA,EAAO,UAAA,CAAW,CAAA,GAClB,YAAA,UACA,OAAA,EAAS,OAAA,EACT,QAAA,EAAU,QAAA,EACV,UAAA,GAAa,UAAA,GACZ,OAAA,CAAQ,WAAA,CAAY,CAAA"}
@@ -12,7 +12,7 @@ import { XProvider } from "./x-provider.mjs";
12
12
  import { randomUrlSafe, takeProviderState, writeProviderState } from "./provider-state-cookie.mjs";
13
13
  import { resolveProviderUser } from "./resolve-provider-user.mjs";
14
14
 
15
- //#region ../auth/src/providers/provider-login.ts
15
+ //#region ../../packages/auth/src/providers/provider-login.ts
16
16
  /** Providers auth ships, by name — each built from `auth.providers.<name>`. */
17
17
  const BUILT_IN_PROVIDERS = {
18
18
  google: () => {
@@ -1 +1 @@
1
- {"version":3,"file":"provider-login.mjs","names":[],"sources":["../../../../../../../auth/src/providers/provider-login.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport type { Request, Response } from \"@warlock.js/core\";\nimport type { AuthProvider } from \"../contracts/providers\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport { AppleProvider } from \"./apple-provider\";\nimport { DiscordProvider } from \"./discord-provider\";\nimport { FacebookProvider } from \"./facebook-provider\";\nimport { GitHubProvider } from \"./github-provider\";\nimport { GoogleProvider } from \"./google-provider\";\nimport { LinkedInProvider } from \"./linkedin-provider\";\nimport { XProvider } from \"./x-provider\";\nimport { safeEqual } from \"../utils/safe-equal\";\nimport { randomUrlSafe, takeProviderState, writeProviderState } from \"./provider-state-cookie\";\nimport { resolveProviderUser } from \"./resolve-provider-user\";\n\n/** Providers auth ships, by name — each built from `auth.providers.<name>`. */\nconst BUILT_IN_PROVIDERS: Record<string, (name: string) => AuthProvider | undefined> = {\n google: () => {\n const google = authConfig.providers.google();\n\n return google ? new GoogleProvider(google) : undefined;\n },\n github: () => {\n const github = authConfig.providers.github();\n\n return github ? new GitHubProvider(github) : undefined;\n },\n discord: () => {\n const discord = authConfig.providers.discord();\n\n return discord ? new DiscordProvider(discord) : undefined;\n },\n linkedin: () => {\n const linkedin = authConfig.providers.linkedin();\n\n return linkedin ? new LinkedInProvider(linkedin) : undefined;\n },\n apple: () => {\n const apple = authConfig.providers.apple();\n\n return apple ? new AppleProvider(apple) : undefined;\n },\n facebook: () => {\n const facebook = authConfig.providers.facebook();\n\n return facebook ? new FacebookProvider(facebook) : undefined;\n },\n x: () => {\n const x = authConfig.providers.x();\n\n return x ? new XProvider(x) : undefined;\n },\n};\n\n/**\n * Resolve a provider by name: a built-in name (`google`, `github`, `discord`,\n * `linkedin`, `apple`, `facebook`, `x`) from `auth.providers.<name>`, anything\n * else from `auth.providers.custom`. Throws naming the config key when absent.\n */\nexport function resolveAuthProvider(name: string): AuthProvider {\n const isBuiltIn = Object.hasOwn(BUILT_IN_PROVIDERS, name);\n\n if (isBuiltIn) {\n const provider = BUILT_IN_PROVIDERS[name]!(name);\n\n if (provider) return provider;\n } else {\n const custom = authConfig.providers.custom(name);\n\n if (custom) return custom;\n }\n\n throw new Error(\n `@warlock.js/auth: no login provider \"${name}\" — configure \\`auth.providers.${\n isBuiltIn ? name : `custom.${name}`\n }\\`.`,\n );\n}\n\n/**\n * Start a redirect login: generate `state`, `nonce` and a PKCE verifier, store\n * them in a signed 10-minute cookie, and resolve the provider URL to redirect\n * the browser to.\n *\n * @example\n * // GET /auth/google\n * export const googleRedirect: RequestHandler = async (_request, response) =>\n * response.redirect(await startProviderLogin(response, \"google\"));\n */\nexport async function startProviderLogin(\n response: Response,\n providerName: string,\n): Promise<string> {\n const provider = resolveAuthProvider(providerName);\n\n const state = {\n state: randomUrlSafe(),\n nonce: randomUrlSafe(),\n codeVerifier: randomUrlSafe(),\n };\n\n const url = await provider.authorizationUrl(state);\n\n writeProviderState(response, provider.name, state, provider.callbackMode ?? \"query\");\n\n return url;\n}\n\n/**\n * Finish a redirect login on the provider's callback: consume the state\n * cookie, reject a missing/forged/expired cookie, a cookie from another\n * provider, a provider-reported error or a `state` mismatch; let the provider\n * verify the callback; find-or-link the user; then log them in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * @throws InvalidProviderCallbackError (400, EC009) for any callback rejection.\n * @throws ProviderEmailNotVerifiedError (403, EC010) for an unlinked, unverified email.\n * @throws AuthProviderSdkMissingError when the provider's SDK is not installed.\n *\n * @example\n * // GET /auth/google/callback\n * const { user, tokens } = await completeProviderLogin(User, \"google\", request, response);\n * authService.setAuthCookie(response, tokens.accessToken);\n */\nexport async function completeProviderLogin<T extends Auth>(\n Model: ChildModel<T>,\n providerName: string,\n request: Request,\n response: Response,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<T>> {\n const provider = resolveAuthProvider(providerName);\n const expected = takeProviderState(request, response);\n\n if (!expected) {\n throw new InvalidProviderCallbackError(\"missing-or-invalid-state-cookie\");\n }\n\n if (expected.provider !== provider.name) {\n throw new InvalidProviderCallbackError(\"state-cookie-for-another-provider\");\n }\n\n const query: Record<string, unknown> = {\n code: request.input(\"code\"),\n state: request.input(\"state\"),\n error: request.input(\"error\"),\n // Apple's form_post callback carries the account name here, on the first authorization only.\n user: request.input(\"user\"),\n };\n\n if (query.error) {\n throw new InvalidProviderCallbackError(`provider-error:${String(query.error)}`);\n }\n\n if (typeof query.state !== \"string\" || !safeEqual(query.state, expected.state)) {\n throw new InvalidProviderCallbackError(\"state-mismatch\");\n }\n\n const profile = await provider.handleCallback({\n query,\n expected: { state: expected.state, nonce: expected.nonce, codeVerifier: expected.codeVerifier },\n });\n\n const user = await resolveProviderUser(Model, profile);\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,qBAAiF;CACrF,cAAc;EACZ,MAAM,SAAS,WAAW,UAAU,OAAO;EAE3C,OAAO,SAAS,IAAI,eAAe,MAAM,IAAI;CAC/C;CACA,cAAc;EACZ,MAAM,SAAS,WAAW,UAAU,OAAO;EAE3C,OAAO,SAAS,IAAI,eAAe,MAAM,IAAI;CAC/C;CACA,eAAe;EACb,MAAM,UAAU,WAAW,UAAU,QAAQ;EAE7C,OAAO,UAAU,IAAI,gBAAgB,OAAO,IAAI;CAClD;CACA,gBAAgB;EACd,MAAM,WAAW,WAAW,UAAU,SAAS;EAE/C,OAAO,WAAW,IAAI,iBAAiB,QAAQ,IAAI;CACrD;CACA,aAAa;EACX,MAAM,QAAQ,WAAW,UAAU,MAAM;EAEzC,OAAO,QAAQ,IAAI,cAAc,KAAK,IAAI;CAC5C;CACA,gBAAgB;EACd,MAAM,WAAW,WAAW,UAAU,SAAS;EAE/C,OAAO,WAAW,IAAI,iBAAiB,QAAQ,IAAI;CACrD;CACA,SAAS;EACP,MAAM,IAAI,WAAW,UAAU,EAAE;EAEjC,OAAO,IAAI,IAAI,UAAU,CAAC,IAAI;CAChC;AACF;;;;;;AAOA,SAAgB,oBAAoB,MAA4B;CAC9D,MAAM,YAAY,OAAO,OAAO,oBAAoB,IAAI;CAExD,IAAI,WAAW;EACb,MAAM,WAAW,mBAAmB,KAAK,CAAE,IAAI;EAE/C,IAAI,UAAU,OAAO;CACvB,OAAO;EACL,MAAM,SAAS,WAAW,UAAU,OAAO,IAAI;EAE/C,IAAI,QAAQ,OAAO;CACrB;CAEA,MAAM,IAAI,MACR,wCAAwC,KAAK,iCAC3C,YAAY,OAAO,UAAU,OAC9B,IACH;AACF;;;;;;;;;;;AAYA,eAAsB,mBACpB,UACA,cACiB;CACjB,MAAM,WAAW,oBAAoB,YAAY;CAEjD,MAAM,QAAQ;EACZ,OAAO,cAAc;EACrB,OAAO,cAAc;EACrB,cAAc,cAAc;CAC9B;CAEA,MAAM,MAAM,MAAM,SAAS,iBAAiB,KAAK;CAEjD,mBAAmB,UAAU,SAAS,MAAM,OAAO,SAAS,gBAAgB,OAAO;CAEnF,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,eAAsB,sBACpB,OACA,cACA,SACA,UACA,YACyB;CACzB,MAAM,WAAW,oBAAoB,YAAY;CACjD,MAAM,WAAW,kBAAkB,SAAS,QAAQ;CAEpD,IAAI,CAAC,UACH,MAAM,IAAI,6BAA6B,iCAAiC;CAG1E,IAAI,SAAS,aAAa,SAAS,MACjC,MAAM,IAAI,6BAA6B,mCAAmC;CAG5E,MAAM,QAAiC;EACrC,MAAM,QAAQ,MAAM,MAAM;EAC1B,OAAO,QAAQ,MAAM,OAAO;EAC5B,OAAO,QAAQ,MAAM,OAAO;EAE5B,MAAM,QAAQ,MAAM,MAAM;CAC5B;CAEA,IAAI,MAAM,OACR,MAAM,IAAI,6BAA6B,kBAAkB,OAAO,MAAM,KAAK,GAAG;CAGhF,IAAI,OAAO,MAAM,UAAU,YAAY,CAAC,UAAU,MAAM,OAAO,SAAS,KAAK,GAC3E,MAAM,IAAI,6BAA6B,gBAAgB;CAQzD,MAAM,OAAO,MAAM,oBAAoB,OAAO,MALxB,SAAS,eAAe;EAC5C;EACA,UAAU;GAAE,OAAO,SAAS;GAAO,OAAO,SAAS;GAAO,cAAc,SAAS;EAAa;CAChG,CAAC,CAEoD;CAErD,OAAO,YAAY,cAAc,MAAM,UAAU;AACnD"}
1
+ {"version":3,"file":"provider-login.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/provider-login.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport type { Request, Response } from \"@warlock.js/core\";\nimport type { AuthProvider } from \"../contracts/providers\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport { AppleProvider } from \"./apple-provider\";\nimport { DiscordProvider } from \"./discord-provider\";\nimport { FacebookProvider } from \"./facebook-provider\";\nimport { GitHubProvider } from \"./github-provider\";\nimport { GoogleProvider } from \"./google-provider\";\nimport { LinkedInProvider } from \"./linkedin-provider\";\nimport { XProvider } from \"./x-provider\";\nimport { safeEqual } from \"../utils/safe-equal\";\nimport { randomUrlSafe, takeProviderState, writeProviderState } from \"./provider-state-cookie\";\nimport { resolveProviderUser } from \"./resolve-provider-user\";\n\n/** Providers auth ships, by name — each built from `auth.providers.<name>`. */\nconst BUILT_IN_PROVIDERS: Record<string, (name: string) => AuthProvider | undefined> = {\n google: () => {\n const google = authConfig.providers.google();\n\n return google ? new GoogleProvider(google) : undefined;\n },\n github: () => {\n const github = authConfig.providers.github();\n\n return github ? new GitHubProvider(github) : undefined;\n },\n discord: () => {\n const discord = authConfig.providers.discord();\n\n return discord ? new DiscordProvider(discord) : undefined;\n },\n linkedin: () => {\n const linkedin = authConfig.providers.linkedin();\n\n return linkedin ? new LinkedInProvider(linkedin) : undefined;\n },\n apple: () => {\n const apple = authConfig.providers.apple();\n\n return apple ? new AppleProvider(apple) : undefined;\n },\n facebook: () => {\n const facebook = authConfig.providers.facebook();\n\n return facebook ? new FacebookProvider(facebook) : undefined;\n },\n x: () => {\n const x = authConfig.providers.x();\n\n return x ? new XProvider(x) : undefined;\n },\n};\n\n/**\n * Resolve a provider by name: a built-in name (`google`, `github`, `discord`,\n * `linkedin`, `apple`, `facebook`, `x`) from `auth.providers.<name>`, anything\n * else from `auth.providers.custom`. Throws naming the config key when absent.\n */\nexport function resolveAuthProvider(name: string): AuthProvider {\n const isBuiltIn = Object.hasOwn(BUILT_IN_PROVIDERS, name);\n\n if (isBuiltIn) {\n const provider = BUILT_IN_PROVIDERS[name]!(name);\n\n if (provider) return provider;\n } else {\n const custom = authConfig.providers.custom(name);\n\n if (custom) return custom;\n }\n\n throw new Error(\n `@warlock.js/auth: no login provider \"${name}\" — configure \\`auth.providers.${\n isBuiltIn ? name : `custom.${name}`\n }\\`.`,\n );\n}\n\n/**\n * Start a redirect login: generate `state`, `nonce` and a PKCE verifier, store\n * them in a signed 10-minute cookie, and resolve the provider URL to redirect\n * the browser to.\n *\n * @example\n * // GET /auth/google\n * export const googleRedirect: RequestHandler = async (_request, response) =>\n * response.redirect(await startProviderLogin(response, \"google\"));\n */\nexport async function startProviderLogin(\n response: Response,\n providerName: string,\n): Promise<string> {\n const provider = resolveAuthProvider(providerName);\n\n const state = {\n state: randomUrlSafe(),\n nonce: randomUrlSafe(),\n codeVerifier: randomUrlSafe(),\n };\n\n const url = await provider.authorizationUrl(state);\n\n writeProviderState(response, provider.name, state, provider.callbackMode ?? \"query\");\n\n return url;\n}\n\n/**\n * Finish a redirect login on the provider's callback: consume the state\n * cookie, reject a missing/forged/expired cookie, a cookie from another\n * provider, a provider-reported error or a `state` mismatch; let the provider\n * verify the callback; find-or-link the user; then log them in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * @throws InvalidProviderCallbackError (400, EC009) for any callback rejection.\n * @throws ProviderEmailNotVerifiedError (403, EC010) for an unlinked, unverified email.\n * @throws AuthProviderSdkMissingError when the provider's SDK is not installed.\n *\n * @example\n * // GET /auth/google/callback\n * const { user, tokens } = await completeProviderLogin(User, \"google\", request, response);\n * authService.setAuthCookie(response, tokens.accessToken);\n */\nexport async function completeProviderLogin<T extends Auth>(\n Model: ChildModel<T>,\n providerName: string,\n request: Request,\n response: Response,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<T>> {\n const provider = resolveAuthProvider(providerName);\n const expected = takeProviderState(request, response);\n\n if (!expected) {\n throw new InvalidProviderCallbackError(\"missing-or-invalid-state-cookie\");\n }\n\n if (expected.provider !== provider.name) {\n throw new InvalidProviderCallbackError(\"state-cookie-for-another-provider\");\n }\n\n const query: Record<string, unknown> = {\n code: request.input(\"code\"),\n state: request.input(\"state\"),\n error: request.input(\"error\"),\n // Apple's form_post callback carries the account name here, on the first authorization only.\n user: request.input(\"user\"),\n };\n\n if (query.error) {\n throw new InvalidProviderCallbackError(`provider-error:${String(query.error)}`);\n }\n\n if (typeof query.state !== \"string\" || !safeEqual(query.state, expected.state)) {\n throw new InvalidProviderCallbackError(\"state-mismatch\");\n }\n\n const profile = await provider.handleCallback({\n query,\n expected: { state: expected.state, nonce: expected.nonce, codeVerifier: expected.codeVerifier },\n });\n\n const user = await resolveProviderUser(Model, profile);\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,qBAAiF;CACrF,cAAc;EACZ,MAAM,SAAS,WAAW,UAAU,OAAO;EAE3C,OAAO,SAAS,IAAI,eAAe,MAAM,IAAI;CAC/C;CACA,cAAc;EACZ,MAAM,SAAS,WAAW,UAAU,OAAO;EAE3C,OAAO,SAAS,IAAI,eAAe,MAAM,IAAI;CAC/C;CACA,eAAe;EACb,MAAM,UAAU,WAAW,UAAU,QAAQ;EAE7C,OAAO,UAAU,IAAI,gBAAgB,OAAO,IAAI;CAClD;CACA,gBAAgB;EACd,MAAM,WAAW,WAAW,UAAU,SAAS;EAE/C,OAAO,WAAW,IAAI,iBAAiB,QAAQ,IAAI;CACrD;CACA,aAAa;EACX,MAAM,QAAQ,WAAW,UAAU,MAAM;EAEzC,OAAO,QAAQ,IAAI,cAAc,KAAK,IAAI;CAC5C;CACA,gBAAgB;EACd,MAAM,WAAW,WAAW,UAAU,SAAS;EAE/C,OAAO,WAAW,IAAI,iBAAiB,QAAQ,IAAI;CACrD;CACA,SAAS;EACP,MAAM,IAAI,WAAW,UAAU,EAAE;EAEjC,OAAO,IAAI,IAAI,UAAU,CAAC,IAAI;CAChC;AACF;;;;;;AAOA,SAAgB,oBAAoB,MAA4B;CAC9D,MAAM,YAAY,OAAO,OAAO,oBAAoB,IAAI;CAExD,IAAI,WAAW;EACb,MAAM,WAAW,mBAAmB,KAAK,CAAE,IAAI;EAE/C,IAAI,UAAU,OAAO;CACvB,OAAO;EACL,MAAM,SAAS,WAAW,UAAU,OAAO,IAAI;EAE/C,IAAI,QAAQ,OAAO;CACrB;CAEA,MAAM,IAAI,MACR,wCAAwC,KAAK,iCAC3C,YAAY,OAAO,UAAU,OAC9B,IACH;AACF;;;;;;;;;;;AAYA,eAAsB,mBACpB,UACA,cACiB;CACjB,MAAM,WAAW,oBAAoB,YAAY;CAEjD,MAAM,QAAQ;EACZ,OAAO,cAAc;EACrB,OAAO,cAAc;EACrB,cAAc,cAAc;CAC9B;CAEA,MAAM,MAAM,MAAM,SAAS,iBAAiB,KAAK;CAEjD,mBAAmB,UAAU,SAAS,MAAM,OAAO,SAAS,gBAAgB,OAAO;CAEnF,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,eAAsB,sBACpB,OACA,cACA,SACA,UACA,YACyB;CACzB,MAAM,WAAW,oBAAoB,YAAY;CACjD,MAAM,WAAW,kBAAkB,SAAS,QAAQ;CAEpD,IAAI,CAAC,UACH,MAAM,IAAI,6BAA6B,iCAAiC;CAG1E,IAAI,SAAS,aAAa,SAAS,MACjC,MAAM,IAAI,6BAA6B,mCAAmC;CAG5E,MAAM,QAAiC;EACrC,MAAM,QAAQ,MAAM,MAAM;EAC1B,OAAO,QAAQ,MAAM,OAAO;EAC5B,OAAO,QAAQ,MAAM,OAAO;EAE5B,MAAM,QAAQ,MAAM,MAAM;CAC5B;CAEA,IAAI,MAAM,OACR,MAAM,IAAI,6BAA6B,kBAAkB,OAAO,MAAM,KAAK,GAAG;CAGhF,IAAI,OAAO,MAAM,UAAU,YAAY,CAAC,UAAU,MAAM,OAAO,SAAS,KAAK,GAC3E,MAAM,IAAI,6BAA6B,gBAAgB;CAQzD,MAAM,OAAO,MAAM,oBAAoB,OAAO,MALxB,SAAS,eAAe;EAC5C;EACA,UAAU;GAAE,OAAO,SAAS;GAAO,OAAO,SAAS;GAAO,cAAc,SAAS;EAAa;CAChG,CAAC,CAEoD;CAErD,OAAO,YAAY,cAAc,MAAM,UAAU;AACnD"}
@@ -2,7 +2,7 @@ import { authConfig } from "../services/auth-config.mjs";
2
2
  import { safeEqual } from "../utils/safe-equal.mjs";
3
3
  import { createHmac, randomBytes } from "node:crypto";
4
4
 
5
- //#region ../auth/src/providers/provider-state-cookie.ts
5
+ //#region ../../packages/auth/src/providers/provider-state-cookie.ts
6
6
  /** Cookie carrying a redirect login's state from the start to the callback. */
7
7
  const PROVIDER_STATE_COOKIE = "auth_provider_state";
8
8
  /** How long a started provider login stays completable. */
@@ -1 +1 @@
1
- {"version":3,"file":"provider-state-cookie.mjs","names":[],"sources":["../../../../../../../auth/src/providers/provider-state-cookie.ts"],"sourcesContent":["import type { Request, Response } from \"@warlock.js/core\";\nimport { createHmac, randomBytes } from \"node:crypto\";\nimport type { ProviderAuthorizationState } from \"../contracts/providers\";\nimport { authConfig } from \"../services/auth-config\";\nimport { safeEqual } from \"../utils/safe-equal\";\n\n/** Cookie carrying a redirect login's state from the start to the callback. */\nexport const PROVIDER_STATE_COOKIE = \"auth_provider_state\";\n\n/** How long a started provider login stays completable. */\nconst PROVIDER_STATE_TTL_SECONDS = 600;\n\n/** What the signed cookie holds. */\nexport type StoredProviderState = ProviderAuthorizationState & {\n provider: string;\n expiresAt: number;\n callbackMode: \"query\" | \"form_post\";\n};\n\n/** 32 random bytes, base64url — used for `state`, `nonce` and the PKCE verifier. */\nexport function randomUrlSafe(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/**\n * HMAC over the payload with a key DERIVED from the access-token secret, so\n * the state cookie never shares a MAC key with the JWTs themselves.\n */\nfunction sign(payload: string): string {\n const key = createHmac(\"sha256\", authConfig.accessToken.secret())\n .update(\"@warlock.js/auth:provider-state\")\n .digest();\n\n return createHmac(\"sha256\", key).update(payload).digest(\"base64url\");\n}\n\n/**\n * Write the signed, 10-minute state cookie for a provider login. `HttpOnly`\n * comes from the `response.cookie()` secure defaults. `callbackMode` picks\n * the `SameSite`/`Secure` pair: `\"query\"` (default) keeps the framework's\n * `SameSite=Lax`, which still rides a top-level GET redirect back to the app;\n * `\"form_post\"` (Apple) overrides to `SameSite=None; Secure`, since a browser\n * drops a `Lax` cookie on the provider's cross-site POST callback. Either\n * way, the signed `state`/`nonce`/PKCE values — not the cookie's SameSite\n * attribute — are what actually defend against CSRF here.\n */\nexport function writeProviderState(\n response: Response,\n provider: string,\n state: ProviderAuthorizationState,\n callbackMode: \"query\" | \"form_post\" = \"query\",\n): void {\n const stored: StoredProviderState = {\n ...state,\n provider,\n expiresAt: Date.now() + PROVIDER_STATE_TTL_SECONDS * 1000,\n callbackMode,\n };\n const payload = Buffer.from(JSON.stringify(stored)).toString(\"base64url\");\n\n response.cookie(PROVIDER_STATE_COOKIE, `${payload}.${sign(payload)}`, {\n raw: true,\n path: \"/\",\n maxAge: PROVIDER_STATE_TTL_SECONDS,\n ...(callbackMode === \"form_post\" ? { sameSite: \"none\", secure: true } : {}),\n });\n}\n\n/**\n * Read AND clear the state cookie, so a started login completes at most once.\n * Resolves `undefined` for an absent, forged (bad MAC), malformed or expired\n * cookie. The clearing `Set-Cookie` repeats whatever `SameSite`/`Secure` pair\n * the cookie was WRITTEN with (recovered from the decoded payload itself, so\n * this needs no mode passed in) — a browser only honours a clearing cookie\n * when it matches the original's attributes, and for a `form_post` (Apple)\n * cookie that means `SameSite=None; Secure` too, or the original survives.\n */\nexport function takeProviderState(\n request: Request,\n response: Response,\n): StoredProviderState | undefined {\n const raw: unknown = request.cookie(PROVIDER_STATE_COOKIE);\n\n let stored: Partial<StoredProviderState> | undefined;\n\n if (typeof raw === \"string\") {\n const [payload, signature, ...rest] = raw.split(\".\");\n\n if (payload && signature && rest.length === 0 && safeEqual(signature, sign(payload))) {\n try {\n stored = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\"));\n } catch {\n stored = undefined;\n }\n }\n }\n\n response.clearCookie(PROVIDER_STATE_COOKIE, {\n path: \"/\",\n ...(stored?.callbackMode === \"form_post\" ? { sameSite: \"none\", secure: true } : {}),\n });\n\n if (\n typeof stored?.state !== \"string\" ||\n typeof stored.nonce !== \"string\" ||\n typeof stored.codeVerifier !== \"string\" ||\n typeof stored.provider !== \"string\" ||\n typeof stored.expiresAt !== \"number\" ||\n stored.expiresAt <= Date.now()\n ) {\n return undefined;\n }\n\n return stored as StoredProviderState;\n}\n"],"mappings":";;;;;;AAOA,MAAa,wBAAwB;;AAGrC,MAAM,6BAA6B;;AAUnC,SAAgB,gBAAwB;CACtC,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;AAC7C;;;;;AAMA,SAAS,KAAK,SAAyB;CAKrC,OAAO,WAAW,UAJN,WAAW,UAAU,WAAW,YAAY,OAAO,CAAC,CAAC,CAC9D,OAAO,iCAAiC,CAAC,CACzC,OAE2B,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,WAAW;AACrE;;;;;;;;;;;AAYA,SAAgB,mBACd,UACA,UACA,OACA,eAAsC,SAChC;CACN,MAAM,SAA8B;EAClC,GAAG;EACH;EACA,WAAW,KAAK,IAAI,IAAI,6BAA6B;EACrD;CACF;CACA,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC,CAAC,CAAC,SAAS,WAAW;CAExE,SAAS,OAAO,uBAAuB,GAAG,QAAQ,GAAG,KAAK,OAAO,KAAK;EACpE,KAAK;EACL,MAAM;EACN,QAAQ;EACR,GAAI,iBAAiB,cAAc;GAAE,UAAU;GAAQ,QAAQ;EAAK,IAAI,CAAC;CAC3E,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,kBACd,SACA,UACiC;CACjC,MAAM,MAAe,QAAQ,OAAO,qBAAqB;CAEzD,IAAI;CAEJ,IAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,CAAC,SAAS,WAAW,GAAG,QAAQ,IAAI,MAAM,GAAG;EAEnD,IAAI,WAAW,aAAa,KAAK,WAAW,KAAK,UAAU,WAAW,KAAK,OAAO,CAAC,GACjF,IAAI;GACF,SAAS,KAAK,MAAM,OAAO,KAAK,SAAS,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC;EACxE,QAAQ;GACN,SAAS;EACX;CAEJ;CAEA,SAAS,YAAY,uBAAuB;EAC1C,MAAM;EACN,GAAI,QAAQ,iBAAiB,cAAc;GAAE,UAAU;GAAQ,QAAQ;EAAK,IAAI,CAAC;CACnF,CAAC;CAED,IACE,OAAO,QAAQ,UAAU,YACzB,OAAO,OAAO,UAAU,YACxB,OAAO,OAAO,iBAAiB,YAC/B,OAAO,OAAO,aAAa,YAC3B,OAAO,OAAO,cAAc,YAC5B,OAAO,aAAa,KAAK,IAAI,GAE7B;CAGF,OAAO;AACT"}
1
+ {"version":3,"file":"provider-state-cookie.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/provider-state-cookie.ts"],"sourcesContent":["import type { Request, Response } from \"@warlock.js/core\";\nimport { createHmac, randomBytes } from \"node:crypto\";\nimport type { ProviderAuthorizationState } from \"../contracts/providers\";\nimport { authConfig } from \"../services/auth-config\";\nimport { safeEqual } from \"../utils/safe-equal\";\n\n/** Cookie carrying a redirect login's state from the start to the callback. */\nexport const PROVIDER_STATE_COOKIE = \"auth_provider_state\";\n\n/** How long a started provider login stays completable. */\nconst PROVIDER_STATE_TTL_SECONDS = 600;\n\n/** What the signed cookie holds. */\nexport type StoredProviderState = ProviderAuthorizationState & {\n provider: string;\n expiresAt: number;\n callbackMode: \"query\" | \"form_post\";\n};\n\n/** 32 random bytes, base64url — used for `state`, `nonce` and the PKCE verifier. */\nexport function randomUrlSafe(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\n/**\n * HMAC over the payload with a key DERIVED from the access-token secret, so\n * the state cookie never shares a MAC key with the JWTs themselves.\n */\nfunction sign(payload: string): string {\n const key = createHmac(\"sha256\", authConfig.accessToken.secret())\n .update(\"@warlock.js/auth:provider-state\")\n .digest();\n\n return createHmac(\"sha256\", key).update(payload).digest(\"base64url\");\n}\n\n/**\n * Write the signed, 10-minute state cookie for a provider login. `HttpOnly`\n * comes from the `response.cookie()` secure defaults. `callbackMode` picks\n * the `SameSite`/`Secure` pair: `\"query\"` (default) keeps the framework's\n * `SameSite=Lax`, which still rides a top-level GET redirect back to the app;\n * `\"form_post\"` (Apple) overrides to `SameSite=None; Secure`, since a browser\n * drops a `Lax` cookie on the provider's cross-site POST callback. Either\n * way, the signed `state`/`nonce`/PKCE values — not the cookie's SameSite\n * attribute — are what actually defend against CSRF here.\n */\nexport function writeProviderState(\n response: Response,\n provider: string,\n state: ProviderAuthorizationState,\n callbackMode: \"query\" | \"form_post\" = \"query\",\n): void {\n const stored: StoredProviderState = {\n ...state,\n provider,\n expiresAt: Date.now() + PROVIDER_STATE_TTL_SECONDS * 1000,\n callbackMode,\n };\n const payload = Buffer.from(JSON.stringify(stored)).toString(\"base64url\");\n\n response.cookie(PROVIDER_STATE_COOKIE, `${payload}.${sign(payload)}`, {\n raw: true,\n path: \"/\",\n maxAge: PROVIDER_STATE_TTL_SECONDS,\n ...(callbackMode === \"form_post\" ? { sameSite: \"none\", secure: true } : {}),\n });\n}\n\n/**\n * Read AND clear the state cookie, so a started login completes at most once.\n * Resolves `undefined` for an absent, forged (bad MAC), malformed or expired\n * cookie. The clearing `Set-Cookie` repeats whatever `SameSite`/`Secure` pair\n * the cookie was WRITTEN with (recovered from the decoded payload itself, so\n * this needs no mode passed in) — a browser only honours a clearing cookie\n * when it matches the original's attributes, and for a `form_post` (Apple)\n * cookie that means `SameSite=None; Secure` too, or the original survives.\n */\nexport function takeProviderState(\n request: Request,\n response: Response,\n): StoredProviderState | undefined {\n const raw: unknown = request.cookie(PROVIDER_STATE_COOKIE);\n\n let stored: Partial<StoredProviderState> | undefined;\n\n if (typeof raw === \"string\") {\n const [payload, signature, ...rest] = raw.split(\".\");\n\n if (payload && signature && rest.length === 0 && safeEqual(signature, sign(payload))) {\n try {\n stored = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\"));\n } catch {\n stored = undefined;\n }\n }\n }\n\n response.clearCookie(PROVIDER_STATE_COOKIE, {\n path: \"/\",\n ...(stored?.callbackMode === \"form_post\" ? { sameSite: \"none\", secure: true } : {}),\n });\n\n if (\n typeof stored?.state !== \"string\" ||\n typeof stored.nonce !== \"string\" ||\n typeof stored.codeVerifier !== \"string\" ||\n typeof stored.provider !== \"string\" ||\n typeof stored.expiresAt !== \"number\" ||\n stored.expiresAt <= Date.now()\n ) {\n return undefined;\n }\n\n return stored as StoredProviderState;\n}\n"],"mappings":";;;;;;AAOA,MAAa,wBAAwB;;AAGrC,MAAM,6BAA6B;;AAUnC,SAAgB,gBAAwB;CACtC,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;AAC7C;;;;;AAMA,SAAS,KAAK,SAAyB;CAKrC,OAAO,WAAW,UAJN,WAAW,UAAU,WAAW,YAAY,OAAO,CAAC,CAAC,CAC9D,OAAO,iCAAiC,CAAC,CACzC,OAE2B,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,OAAO,WAAW;AACrE;;;;;;;;;;;AAYA,SAAgB,mBACd,UACA,UACA,OACA,eAAsC,SAChC;CACN,MAAM,SAA8B;EAClC,GAAG;EACH;EACA,WAAW,KAAK,IAAI,IAAI,6BAA6B;EACrD;CACF;CACA,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC,CAAC,CAAC,SAAS,WAAW;CAExE,SAAS,OAAO,uBAAuB,GAAG,QAAQ,GAAG,KAAK,OAAO,KAAK;EACpE,KAAK;EACL,MAAM;EACN,QAAQ;EACR,GAAI,iBAAiB,cAAc;GAAE,UAAU;GAAQ,QAAQ;EAAK,IAAI,CAAC;CAC3E,CAAC;AACH;;;;;;;;;;AAWA,SAAgB,kBACd,SACA,UACiC;CACjC,MAAM,MAAe,QAAQ,OAAO,qBAAqB;CAEzD,IAAI;CAEJ,IAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,CAAC,SAAS,WAAW,GAAG,QAAQ,IAAI,MAAM,GAAG;EAEnD,IAAI,WAAW,aAAa,KAAK,WAAW,KAAK,UAAU,WAAW,KAAK,OAAO,CAAC,GACjF,IAAI;GACF,SAAS,KAAK,MAAM,OAAO,KAAK,SAAS,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC;EACxE,QAAQ;GACN,SAAS;EACX;CAEJ;CAEA,SAAS,YAAY,uBAAuB;EAC1C,MAAM;EACN,GAAI,QAAQ,iBAAiB,cAAc;GAAE,UAAU;GAAQ,QAAQ;EAAK,IAAI,CAAC;CACnF,CAAC;CAED,IACE,OAAO,QAAQ,UAAU,YACzB,OAAO,OAAO,UAAU,YACxB,OAAO,OAAO,iBAAiB,YAC/B,OAAO,OAAO,aAAa,YAC3B,OAAO,OAAO,cAAc,YAC5B,OAAO,aAAa,KAAK,IAAI,GAE7B;CAGF,OAAO;AACT"}
@@ -4,7 +4,7 @@ import { ProviderEmailNotVerifiedError } from "../errors/provider-email-not-veri
4
4
  import { ProviderAccount } from "../models/provider-account/provider-account.model.mjs";
5
5
  import "../models/provider-account/index.mjs";
6
6
 
7
- //#region ../auth/src/providers/resolve-provider-user.ts
7
+ //#region ../../packages/auth/src/providers/resolve-provider-user.ts
8
8
  /** Default creation for a first provider login: email, name, and the verified stamp. */
9
9
  async function createUserFor(Model, profile) {
10
10
  const createUser = authConfig.providers.createUser();
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-provider-user.mjs","names":[],"sources":["../../../../../../../auth/src/providers/resolve-provider-user.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport type { ProviderProfile } from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { ProviderEmailNotVerifiedError } from \"../errors/provider-email-not-verified.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { ProviderAccount } from \"../models/provider-account\";\nimport { authConfig } from \"../services/auth-config\";\n\n/** Default creation for a first provider login: email, name, and the verified stamp. */\nasync function createUserFor<T extends Auth>(\n Model: ChildModel<T>,\n profile: ProviderProfile,\n): Promise<T> {\n const createUser = authConfig.providers.createUser();\n\n if (createUser) {\n return (await createUser(profile, Model as unknown as ChildModel<Auth>)) as T;\n }\n\n return (await Model.create({\n [authConfig.providers.emailField()]: profile.email,\n ...(profile.name ? { name: profile.name } : {}),\n // The provider verified this address; record it the way verifyEmail does.\n [authConfig.verification.field()]: new Date(),\n } as never)) as T;\n}\n\n/**\n * Find or create the app user for a provider profile, and link them.\n *\n * 1. An existing `provider_accounts` link wins — the provider's stable id, not\n * the email, identifies the account from then on.\n * 2. Otherwise the provider email MUST be verified. An unverified (or absent)\n * email throws {@link ProviderEmailNotVerifiedError} and nothing is linked\n * or created: linking on it would let anyone who can add an unverified\n * address at the provider take over the matching account.\n * 3. Verified: the user whose `auth.providers.emailField` matches, else a new\n * user (`auth.providers.createUser`, or the default) — then the link is stored.\n */\nexport async function resolveProviderUser<T extends Auth>(\n Model: ChildModel<T>,\n profile: ProviderProfile,\n): Promise<T> {\n const link = await ProviderAccount.findLink(profile.provider, profile.providerUserId);\n\n if (link) {\n const linked = (await Model.find(link.userId)) as T | null;\n\n if (!linked || linked.userType !== link.userType) {\n throw new InvalidProviderCallbackError(\"link-points-to-missing-user\");\n }\n\n return linked;\n }\n\n if (!profile.emailVerified || !profile.email) {\n throw new ProviderEmailNotVerifiedError();\n }\n\n const existing = (await Model.first({\n [authConfig.providers.emailField()]: profile.email,\n })) as T | null;\n\n const user = existing ?? (await createUserFor(Model, profile));\n\n await ProviderAccount.link(profile.provider, profile.providerUserId, user, profile.email);\n\n return user;\n}\n"],"mappings":";;;;;;;;AASA,eAAe,cACb,OACA,SACY;CACZ,MAAM,aAAa,WAAW,UAAU,WAAW;CAEnD,IAAI,YACF,OAAQ,MAAM,WAAW,SAAS,KAAoC;CAGxE,OAAQ,MAAM,MAAM,OAAO;GACxB,WAAW,UAAU,WAAW,IAAI,QAAQ;EAC7C,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAE5C,WAAW,aAAa,MAAM,oBAAI,IAAI,KAAK;CAC9C,CAAU;AACZ;;;;;;;;;;;;;AAcA,eAAsB,oBACpB,OACA,SACY;CACZ,MAAM,OAAO,MAAM,gBAAgB,SAAS,QAAQ,UAAU,QAAQ,cAAc;CAEpF,IAAI,MAAM;EACR,MAAM,SAAU,MAAM,MAAM,KAAK,KAAK,MAAM;EAE5C,IAAI,CAAC,UAAU,OAAO,aAAa,KAAK,UACtC,MAAM,IAAI,6BAA6B,6BAA6B;EAGtE,OAAO;CACT;CAEA,IAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,OACrC,MAAM,IAAI,8BAA8B;CAO1C,MAAM,OAAO,MAJW,MAAM,MAAM,GACjC,WAAW,UAAU,WAAW,IAAI,QAAQ,MAC/C,CAAC,KAEyB,MAAM,cAAc,OAAO,OAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,UAAU,QAAQ,gBAAgB,MAAM,QAAQ,KAAK;CAExF,OAAO;AACT"}
1
+ {"version":3,"file":"resolve-provider-user.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/providers/resolve-provider-user.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport type { ProviderProfile } from \"../contracts/providers\";\nimport { InvalidProviderCallbackError } from \"../errors/invalid-provider-callback.error\";\nimport { ProviderEmailNotVerifiedError } from \"../errors/provider-email-not-verified.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { ProviderAccount } from \"../models/provider-account\";\nimport { authConfig } from \"../services/auth-config\";\n\n/** Default creation for a first provider login: email, name, and the verified stamp. */\nasync function createUserFor<T extends Auth>(\n Model: ChildModel<T>,\n profile: ProviderProfile,\n): Promise<T> {\n const createUser = authConfig.providers.createUser();\n\n if (createUser) {\n return (await createUser(profile, Model as unknown as ChildModel<Auth>)) as T;\n }\n\n return (await Model.create({\n [authConfig.providers.emailField()]: profile.email,\n ...(profile.name ? { name: profile.name } : {}),\n // The provider verified this address; record it the way verifyEmail does.\n [authConfig.verification.field()]: new Date(),\n } as never)) as T;\n}\n\n/**\n * Find or create the app user for a provider profile, and link them.\n *\n * 1. An existing `provider_accounts` link wins — the provider's stable id, not\n * the email, identifies the account from then on.\n * 2. Otherwise the provider email MUST be verified. An unverified (or absent)\n * email throws {@link ProviderEmailNotVerifiedError} and nothing is linked\n * or created: linking on it would let anyone who can add an unverified\n * address at the provider take over the matching account.\n * 3. Verified: the user whose `auth.providers.emailField` matches, else a new\n * user (`auth.providers.createUser`, or the default) — then the link is stored.\n */\nexport async function resolveProviderUser<T extends Auth>(\n Model: ChildModel<T>,\n profile: ProviderProfile,\n): Promise<T> {\n const link = await ProviderAccount.findLink(profile.provider, profile.providerUserId);\n\n if (link) {\n const linked = (await Model.find(link.userId)) as T | null;\n\n if (!linked || linked.userType !== link.userType) {\n throw new InvalidProviderCallbackError(\"link-points-to-missing-user\");\n }\n\n return linked;\n }\n\n if (!profile.emailVerified || !profile.email) {\n throw new ProviderEmailNotVerifiedError();\n }\n\n const existing = (await Model.first({\n [authConfig.providers.emailField()]: profile.email,\n })) as T | null;\n\n const user = existing ?? (await createUserFor(Model, profile));\n\n await ProviderAccount.link(profile.provider, profile.providerUserId, user, profile.email);\n\n return user;\n}\n"],"mappings":";;;;;;;;AASA,eAAe,cACb,OACA,SACY;CACZ,MAAM,aAAa,WAAW,UAAU,WAAW;CAEnD,IAAI,YACF,OAAQ,MAAM,WAAW,SAAS,KAAoC;CAGxE,OAAQ,MAAM,MAAM,OAAO;GACxB,WAAW,UAAU,WAAW,IAAI,QAAQ;EAC7C,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;GAE5C,WAAW,aAAa,MAAM,oBAAI,IAAI,KAAK;CAC9C,CAAU;AACZ;;;;;;;;;;;;;AAcA,eAAsB,oBACpB,OACA,SACY;CACZ,MAAM,OAAO,MAAM,gBAAgB,SAAS,QAAQ,UAAU,QAAQ,cAAc;CAEpF,IAAI,MAAM;EACR,MAAM,SAAU,MAAM,MAAM,KAAK,KAAK,MAAM;EAE5C,IAAI,CAAC,UAAU,OAAO,aAAa,KAAK,UACtC,MAAM,IAAI,6BAA6B,6BAA6B;EAGtE,OAAO;CACT;CAEA,IAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,OACrC,MAAM,IAAI,8BAA8B;CAO1C,MAAM,OAAO,MAJW,MAAM,MAAM,GACjC,WAAW,UAAU,WAAW,IAAI,QAAQ,MAC/C,CAAC,KAEyB,MAAM,cAAc,OAAO,OAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,UAAU,QAAQ,gBAAgB,MAAM,QAAQ,KAAK;CAExF,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
1
  import { AuthProvider, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile, XProviderConfig } from "../contracts/providers.mjs";
2
2
 
3
- //#region ../auth/src/providers/x-provider.d.ts
3
+ //#region ../../packages/auth/src/providers/x-provider.d.ts
4
4
  /**
5
5
  * X (Twitter) sign-in: OAuth 2.0 authorization code flow with PKCE (S256). X
6
6
  * is not OpenID Connect — there is no `id_token`. X's confidential clients
@@ -1 +1 @@
1
- {"version":3,"file":"x-provider.d.mts","names":[],"sources":["../../../../../../../auth/src/providers/x-provider.ts"],"mappings":";;;;;AAoCA;;;;;;;;;;;;cAAa,SAAA,YAAqB,YAAA;EAAA,iBAGI,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,eAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAnBA;EAAA,QA8C5B,SAAA;EA5CuB;EAAA,QAmDjB,YAAA;EAAA,QAuCA,SAAA;AAAA"}
1
+ {"version":3,"file":"x-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/x-provider.ts"],"mappings":";;;;;AAoCA;;;;;;;;;;;;cAAa,SAAA,YAAqB,YAAA;EAAA,iBAGI,QAAA;EAAA,SAFpB,IAAA;cAEoB,QAAA,EAAU,eAAA;EAEvC,gBAAA,CAAiB,KAAA,EAAO,0BAAA;EAclB,cAAA;IACX,KAAA;IACA;EAAA,GACC,sBAAA,GAAyB,OAAA,CAAQ,eAAA;EAnBA;EAAA,QA8C5B,SAAA;EA5CuB;EAAA,QAmDjB,YAAA;EAAA,QAuCA,SAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { InvalidProviderCallbackError } from "../errors/invalid-provider-callback.error.mjs";
2
2
  import { pkceChallenge } from "./google-provider.mjs";
3
3
 
4
- //#region ../auth/src/providers/x-provider.ts
4
+ //#region ../../packages/auth/src/providers/x-provider.ts
5
5
  const AUTHORIZATION_ENDPOINT = "https://twitter.com/i/oauth2/authorize";
6
6
  const TOKEN_ENDPOINT = "https://api.twitter.com/2/oauth2/token";
7
7
  const USER_ENDPOINT = "https://api.twitter.com/2/users/me";