@warlock.js/auth 5.30.0 → 5.32.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.
- package/CHANGELOG.md +18 -1
- package/esm/commands/auth-cleanup-command.d.mts +1 -2
- package/esm/commands/auth-cleanup-command.d.mts.map +1 -1
- package/esm/commands/auth-purge-never-expiring-command.d.mts +1 -2
- package/esm/commands/auth-purge-never-expiring-command.d.mts.map +1 -1
- package/esm/commands/auth-purge-never-expiring-command.mjs.map +1 -1
- package/esm/commands/jwt-secret-generator-command.d.mts +1 -2
- package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -1
- package/esm/commands/jwt-secret-generator-command.mjs.map +1 -1
- package/esm/contracts/auth-contract.d.mts +1 -3
- package/esm/contracts/auth-contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -1
- package/esm/contracts/providers.d.mts +82 -50
- package/esm/contracts/providers.d.mts.map +1 -1
- package/esm/contracts/request-locals.d.mts +2 -3
- package/esm/contracts/request-locals.d.mts.map +1 -1
- package/esm/contracts/types.d.mts +59 -43
- package/esm/contracts/types.d.mts.map +1 -1
- package/esm/errors/auth-provider-sdk-missing.error.d.mts +1 -3
- package/esm/errors/auth-provider-sdk-missing.error.d.mts.map +1 -1
- package/esm/errors/email-not-verified.error.d.mts +1 -3
- package/esm/errors/email-not-verified.error.d.mts.map +1 -1
- package/esm/errors/index.d.mts +2 -1
- package/esm/errors/index.mjs +1 -1
- package/esm/errors/invalid-one-time-token.error.d.mts +1 -3
- package/esm/errors/invalid-one-time-token.error.d.mts.map +1 -1
- package/esm/errors/invalid-passkey.error.d.mts +1 -3
- package/esm/errors/invalid-passkey.error.d.mts.map +1 -1
- package/esm/errors/invalid-provider-callback.error.d.mts +1 -3
- package/esm/errors/invalid-provider-callback.error.d.mts.map +1 -1
- package/esm/errors/notifications-unavailable.error.d.mts +1 -3
- package/esm/errors/notifications-unavailable.error.d.mts.map +1 -1
- package/esm/errors/provider-email-not-verified.error.d.mts +1 -3
- package/esm/errors/provider-email-not-verified.error.d.mts.map +1 -1
- package/esm/index.d.mts +8 -0
- package/esm/localization/auth-default-translations.d.mts +1 -3
- package/esm/localization/auth-default-translations.d.mts.map +1 -1
- package/esm/localization/auth-translations.type.d.mts +11 -7
- package/esm/localization/auth-translations.type.d.mts.map +1 -1
- package/esm/localization/index.mjs +1 -1
- package/esm/localization/register-auth-translations.d.mts +1 -2
- package/esm/localization/register-auth-translations.d.mts.map +1 -1
- package/esm/middleware/auth.middleware.d.mts +10 -10
- package/esm/middleware/auth.middleware.d.mts.map +1 -1
- package/esm/middleware/auth.middleware.mjs +2 -2
- package/esm/middleware/auth.middleware.mjs.map +1 -1
- package/esm/middleware/csrf-origin-check.d.mts +4 -6
- package/esm/middleware/csrf-origin-check.d.mts.map +1 -1
- package/esm/middleware/csrf-origin-check.mjs +1 -1
- package/esm/middleware/csrf-origin-check.mjs.map +1 -1
- package/esm/middleware/index.d.mts +2 -1
- package/esm/middleware/index.mjs +1 -1
- package/esm/middleware/login-throttle.middleware.d.mts +4 -6
- package/esm/middleware/login-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/login-throttle.middleware.mjs +1 -1
- package/esm/middleware/login-throttle.middleware.mjs.map +1 -1
- package/esm/middleware/one-time-token-throttle.middleware.d.mts +2 -4
- package/esm/middleware/one-time-token-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/otp-throttle.middleware.d.mts +2 -4
- package/esm/middleware/otp-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/require-verified-email.middleware.d.mts +1 -3
- package/esm/middleware/require-verified-email.middleware.d.mts.map +1 -1
- package/esm/models/access-token/access-token.model.d.mts +6 -30
- package/esm/models/access-token/access-token.model.d.mts.map +1 -1
- package/esm/models/access-token/index.d.mts +2 -1
- package/esm/models/access-token/index.mjs +1 -1
- package/esm/models/auth-token-family/auth-token-family.model.d.mts +2 -28
- package/esm/models/auth-token-family/auth-token-family.model.d.mts.map +1 -1
- package/esm/models/auth-token-family/index.d.mts +2 -1
- package/esm/models/auth-token-family/index.mjs +1 -1
- package/esm/models/auth.model.d.mts +1 -3
- package/esm/models/auth.model.d.mts.map +1 -1
- package/esm/models/index.d.mts +8 -2
- package/esm/models/index.d.mts.map +1 -1
- package/esm/models/index.mjs +1 -1
- package/esm/models/one-time-token/index.d.mts +2 -1
- package/esm/models/one-time-token/index.mjs +1 -1
- package/esm/models/one-time-token/one-time-token.model.d.mts +3 -37
- package/esm/models/one-time-token/one-time-token.model.d.mts.map +1 -1
- package/esm/models/passkey-credential/index.d.mts +2 -1
- package/esm/models/passkey-credential/index.mjs +1 -1
- package/esm/models/passkey-credential/passkey-credential.model.d.mts +2 -32
- package/esm/models/passkey-credential/passkey-credential.model.d.mts.map +1 -1
- package/esm/models/provider-account/index.d.mts +2 -1
- package/esm/models/provider-account/index.mjs +1 -1
- package/esm/models/provider-account/provider-account.model.d.mts +2 -28
- package/esm/models/provider-account/provider-account.model.d.mts.map +1 -1
- package/esm/models/refresh-token/index.d.mts +2 -1
- package/esm/models/refresh-token/index.mjs +1 -1
- package/esm/models/refresh-token/refresh-token.model.d.mts +11 -55
- package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -1
- package/esm/otp/index.d.mts +2 -1
- package/esm/otp/index.mjs +1 -1
- package/esm/otp/otp.d.mts +2 -4
- package/esm/otp/otp.d.mts.map +1 -1
- package/esm/otp/otp.mjs +2 -1
- package/esm/otp/otp.mjs.map +1 -1
- package/esm/passkeys/index.d.mts +2 -1
- package/esm/passkeys/index.mjs +1 -1
- package/esm/passkeys/passkey-authentication.d.mts +2 -4
- package/esm/passkeys/passkey-authentication.d.mts.map +1 -1
- package/esm/passkeys/passkey-authentication.mjs +2 -1
- package/esm/passkeys/passkey-authentication.mjs.map +1 -1
- package/esm/passkeys/passkey-registration.d.mts +3 -4
- package/esm/passkeys/passkey-registration.d.mts.map +1 -1
- package/esm/passkeys/simplewebauthn.d.mts +2 -3
- package/esm/passkeys/simplewebauthn.d.mts.map +1 -1
- package/esm/passkeys/simplewebauthn.mjs +1 -1
- package/esm/providers/apple-provider.d.mts +5 -9
- package/esm/providers/apple-provider.d.mts.map +1 -1
- package/esm/providers/discord-provider.d.mts +2 -7
- package/esm/providers/discord-provider.d.mts.map +1 -1
- package/esm/providers/facebook-provider.d.mts +2 -7
- package/esm/providers/facebook-provider.d.mts.map +1 -1
- package/esm/providers/github-provider.d.mts +2 -7
- package/esm/providers/github-provider.d.mts.map +1 -1
- package/esm/providers/google-provider.d.mts +5 -10
- package/esm/providers/google-provider.d.mts.map +1 -1
- package/esm/providers/index.d.mts +2 -1
- package/esm/providers/index.mjs +1 -1
- package/esm/providers/linkedin-provider.d.mts +3 -8
- package/esm/providers/linkedin-provider.d.mts.map +1 -1
- package/esm/providers/provider-login.d.mts +3 -5
- package/esm/providers/provider-login.d.mts.map +1 -1
- package/esm/providers/provider-login.mjs +3 -2
- package/esm/providers/provider-login.mjs.map +1 -1
- package/esm/providers/provider-state-cookie.mjs +3 -2
- package/esm/providers/provider-state-cookie.mjs.map +1 -1
- package/esm/providers/x-provider.d.mts +2 -7
- package/esm/providers/x-provider.d.mts.map +1 -1
- package/esm/services/auth-events.d.mts +5 -6
- package/esm/services/auth-events.d.mts.map +1 -1
- package/esm/services/auth.service.d.mts +10 -9
- package/esm/services/auth.service.d.mts.map +1 -1
- package/esm/services/current-user.d.mts +1 -3
- package/esm/services/current-user.d.mts.map +1 -1
- package/esm/services/email-verification.d.mts +3 -5
- package/esm/services/email-verification.d.mts.map +1 -1
- package/esm/services/generate-jwt-secret.d.mts +1 -2
- package/esm/services/generate-jwt-secret.d.mts.map +1 -1
- package/esm/services/index.d.mts +2 -1
- package/esm/services/index.mjs +1 -1
- package/esm/services/jwt.d.mts +8 -29
- package/esm/services/jwt.d.mts.map +1 -1
- package/esm/services/jwt.mjs +2 -2
- package/esm/services/jwt.mjs.map +1 -1
- package/esm/services/one-time-tokens.d.mts +6 -6
- package/esm/services/one-time-tokens.d.mts.map +1 -1
- package/esm/services/password-reset.d.mts +2 -4
- package/esm/services/password-reset.d.mts.map +1 -1
- package/esm/services/resolve-request-user.d.mts +19 -14
- package/esm/services/resolve-request-user.d.mts.map +1 -1
- package/esm/services/resolve-request-user.mjs.map +1 -1
- package/esm/services/token-family-operation.mjs +1 -1
- package/esm/session/index.d.mts +2 -1
- package/esm/session/index.mjs +1 -1
- package/esm/session/page-session.d.mts +26 -18
- package/esm/session/page-session.d.mts.map +1 -1
- package/esm/utils/auth-error-codes.d.mts +11 -22
- package/esm/utils/auth-error-codes.d.mts.map +1 -1
- package/esm/utils/index.mjs +1 -1
- package/esm/utils/safe-equal.d.mts +1 -2
- package/esm/utils/safe-equal.d.mts.map +1 -1
- package/esm/utils/token-expiry.d.mts +3 -4
- package/esm/utils/token-expiry.d.mts.map +1 -1
- package/llms-full.txt +189 -111
- package/package.json +8 -8
- package/skills/auth-basics/SKILL.md +10 -13
- package/skills/customize-token-storage/SKILL.md +24 -19
- package/skills/customize-user-type/SKILL.md +3 -1
- package/skills/index.md +4 -1
- package/skills/login-with-providers/SKILL.md +54 -29
- package/skills/manage-tokens/SKILL.md +2 -2
- package/skills/overview/SKILL.md +10 -1
- package/skills/protect-routes/SKILL.md +23 -14
- package/skills/register-user/SKILL.md +4 -4
- package/skills/run-auth-commands/SKILL.md +1 -1
- package/skills/throttle-login-attempts/SKILL.md +51 -22
- package/skills/verify-email-and-reset-password/SKILL.md +7 -5
package/esm/otp/otp.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otp.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/otp/otp.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport { createHmac, randomBytes, randomInt } from \"node:crypto\";\nimport type { OtpMessage, OtpSender } from \"../contracts/providers\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidOneTimeTokenError } from \"../errors/invalid-one-time-token.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport { oneTimeTokenModel } from \"../services/one-time-tokens\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport { safeEqual } from \"../utils/safe-equal\";\n\n/** The slice of `@warlock.js/notifications` OTP delivery uses (an optional peer). */\ntype NotifyModule = {\n notify: {\n channel: (name: string) => { send: (to: string, payload: unknown) => Promise<unknown> };\n };\n};\n\nconst CODE_PATTERN = /^\\d{6}$/;\n\n/** One error for every rejection: unknown phone, no code, wrong, expired, used, over the cap. */\nfunction invalidCode(): InvalidOneTimeTokenError {\n return new InvalidOneTimeTokenError(\"Invalid or expired code.\");\n}\n\n/**\n * Keyed hash of a code. A plain SHA-256 of a 6-digit code is reversed by\n * trying all million values, so the MAC key (derived from the access-token\n * secret) is what keeps a leaked table useless. `salt` makes each stored\n * value unique.\n */\nfunction macCode(user: Auth, salt: string, code: string): string {\n const key = createHmac(\"sha256\", authConfig.accessToken.secret())\n .update(\"@warlock.js/auth:otp\")\n .digest();\n\n return createHmac(\"sha256\", key)\n .update(`${salt}:${user.userType}:${user.id}:${code}`)\n .digest(\"base64url\");\n}\n\n/** Stored form: `<salt>.<mac>` — 16 + 1 + 43 chars, inside the 64-char `token_hash` column. */\nfunction storedCodeHash(user: Auth, code: string): string {\n const salt = randomBytes(12).toString(\"base64url\");\n\n return `${salt}.${macCode(user, salt, code)}`;\n}\n\nfunction codeMatches(user: Auth, stored: string, code: string): boolean {\n const [salt, mac] = stored.split(\".\");\n\n return !!salt && !!mac && safeEqual(mac, macCode(user, salt, code));\n}\n\n/**\n * Resolve how a code is sent: `auth.otp.send`, else the app's notifications\n * channel. Resolved BEFORE the user lookup, so a missing notifications package\n * fails identically for known and unknown phones.\n */\nasync function resolveDelivery(channel: string): Promise<OtpSender> {\n const custom = authConfig.otp.send();\n\n if (custom) return custom;\n\n const { notify } = await loadOptionalPeer<NotifyModule>(\n \"@warlock.js/notifications\",\n \"notifications\",\n );\n\n return (phone, message, channelName) => notify.channel(channelName).send(phone, message);\n}\n\n/**\n * Send a 6-digit login code to the account whose `auth.otp.phoneField`\n * (default `phone`) equals `phone`, through the notifications channel named by\n * `options.channel` / `auth.otp.channel` (default `\"sms\"`). Auth ships no\n * SMS/WhatsApp driver — the app registers that channel.\n *\n * The code is stored only as a keyed hash in `one_time_tokens` (purpose\n * `otp`), expires after `auth.otp.expiresIn` (default 5m), and replaces any\n * earlier unused code. **Anti-enumeration:** an unknown phone resolves the same\n * way and sends nothing. Throttle the route with `otpRequestThrottleMiddleware()`.\n *\n * @throws AuthProviderSdkMissingError when `@warlock.js/notifications` is not\n * installed and no `auth.otp.send` is configured.\n */\nexport async function requestOtp<T extends Auth>(\n Model: ChildModel<T>,\n phone: string,\n options: { channel?: string } = {},\n): Promise<void> {\n const channel = options.channel ?? authConfig.otp.channel();\n const deliver = await resolveDelivery(channel);\n\n if (typeof phone !== \"string\" || phone.length === 0) return;\n\n const user = (await Model.first({ [authConfig.otp.phoneField()]: phone })) as T | null;\n\n if (!user) return;\n\n const code = String(randomInt(0, 1_000_000)).padStart(6, \"0\");\n const expiresAt = new Date(Date.now() + authConfig.otp.expiresInMs());\n const TokenModel = oneTimeTokenModel();\n\n await TokenModel.invalidateActiveFor(user, \"otp\");\n await TokenModel.issue(user, \"otp\", storedCodeHash(user, code), expiresAt);\n\n const message: OtpMessage = { body: authConfig.otp.message(code), code, expiresAt };\n\n await deliver(phone, message, channel);\n}\n\n/**\n * Verify a phone login code and log the user in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * Each check first counts an attempt atomically; once `auth.otp.maxAttempts`\n * (default 5) have been counted the code is invalidated, so it is compared at\n * most that many times even under concurrent guesses. A correct code is\n * consumed single-use. Throttle the route with `otpVerifyThrottleMiddleware()`.\n *\n * @throws InvalidOneTimeTokenError (400, EC008) — the same for an unknown\n * phone, a wrong, expired, used or exhausted code.\n */\nexport async function verifyOtp<T extends Auth>(\n Model: ChildModel<T>,\n phone: string,\n code: string,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<T>> {\n if (typeof phone !== \"string\" || phone.length === 0) throw invalidCode();\n if (typeof code !== \"string\" || !CODE_PATTERN.test(code)) throw invalidCode();\n\n const user = (await Model.first({ [authConfig.otp.phoneField()]: phone })) as T | null;\n\n if (!user) throw invalidCode();\n\n const row = await oneTimeTokenModel().findActiveFor(user, \"otp\");\n\n if (!row || row.isConsumed || row.isExpired) throw invalidCode();\n\n if (!(await row.recordAttempt(authConfig.otp.maxAttempts()))) {\n await row.consumeIfActive();\n\n throw invalidCode();\n }\n\n if (!codeMatches(user, String(row.get(\"token_hash\") ?? \"\"), code)) throw invalidCode();\n\n if (!(await row.consumeIfActive())) throw invalidCode();\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;AAmBA,MAAM,eAAe;;AAGrB,SAAS,cAAwC;CAC/C,OAAO,IAAI,yBAAyB,0BAA0B;AAChE;;;;;;;AAQA,SAAS,QAAQ,MAAY,MAAc,MAAsB;
|
|
1
|
+
{"version":3,"file":"otp.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/otp/otp.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport { createHmac, randomBytes, randomInt } from \"node:crypto\";\nimport type { OtpMessage, OtpSender } from \"../contracts/providers\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidOneTimeTokenError } from \"../errors/invalid-one-time-token.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport { oneTimeTokenModel } from \"../services/one-time-tokens\";\nimport { loadOptionalPeer } from \"../services/optional-peer\";\nimport { safeEqual } from \"../utils/safe-equal\";\n\n/** The slice of `@warlock.js/notifications` OTP delivery uses (an optional peer). */\ntype NotifyModule = {\n notify: {\n channel: (name: string) => { send: (to: string, payload: unknown) => Promise<unknown> };\n };\n};\n\nconst CODE_PATTERN = /^\\d{6}$/;\n\n/** One error for every rejection: unknown phone, no code, wrong, expired, used, over the cap. */\nfunction invalidCode(): InvalidOneTimeTokenError {\n return new InvalidOneTimeTokenError(\"Invalid or expired code.\");\n}\n\n/**\n * Keyed hash of a code. A plain SHA-256 of a 6-digit code is reversed by\n * trying all million values, so the MAC key (derived from the access-token\n * secret) is what keeps a leaked table useless. `salt` makes each stored\n * value unique.\n */\nfunction macCode(user: Auth, salt: string, code: string): string {\n const key = createHmac(\"sha256\", authConfig.accessToken.secret())\n .update(\"@warlock.js/auth:otp\")\n .digest();\n\n return createHmac(\"sha256\", key)\n .update(`${salt}:${user.userType}:${user.id}:${code}`)\n .digest(\"base64url\");\n}\n\n/** Stored form: `<salt>.<mac>` — 16 + 1 + 43 chars, inside the 64-char `token_hash` column. */\nfunction storedCodeHash(user: Auth, code: string): string {\n const salt = randomBytes(12).toString(\"base64url\");\n\n return `${salt}.${macCode(user, salt, code)}`;\n}\n\nfunction codeMatches(user: Auth, stored: string, code: string): boolean {\n const [salt, mac] = stored.split(\".\");\n\n return !!salt && !!mac && safeEqual(mac, macCode(user, salt, code));\n}\n\n/**\n * Resolve how a code is sent: `auth.otp.send`, else the app's notifications\n * channel. Resolved BEFORE the user lookup, so a missing notifications package\n * fails identically for known and unknown phones.\n */\nasync function resolveDelivery(channel: string): Promise<OtpSender> {\n const custom = authConfig.otp.send();\n\n if (custom) return custom;\n\n const { notify } = await loadOptionalPeer<NotifyModule>(\n \"@warlock.js/notifications\",\n \"notifications\",\n );\n\n return (phone, message, channelName) => notify.channel(channelName).send(phone, message);\n}\n\n/**\n * Send a 6-digit login code to the account whose `auth.otp.phoneField`\n * (default `phone`) equals `phone`, through the notifications channel named by\n * `options.channel` / `auth.otp.channel` (default `\"sms\"`). Auth ships no\n * SMS/WhatsApp driver — the app registers that channel.\n *\n * The code is stored only as a keyed hash in `one_time_tokens` (purpose\n * `otp`), expires after `auth.otp.expiresIn` (default 5m), and replaces any\n * earlier unused code. **Anti-enumeration:** an unknown phone resolves the same\n * way and sends nothing. Throttle the route with `otpRequestThrottleMiddleware()`.\n *\n * @throws AuthProviderSdkMissingError when `@warlock.js/notifications` is not\n * installed and no `auth.otp.send` is configured.\n */\nexport async function requestOtp<T extends Auth>(\n Model: ChildModel<T>,\n phone: string,\n options: { channel?: string } = {},\n): Promise<void> {\n const channel = options.channel ?? authConfig.otp.channel();\n const deliver = await resolveDelivery(channel);\n\n if (typeof phone !== \"string\" || phone.length === 0) return;\n\n const user = (await Model.first({ [authConfig.otp.phoneField()]: phone })) as T | null;\n\n if (!user) return;\n\n const code = String(randomInt(0, 1_000_000)).padStart(6, \"0\");\n const expiresAt = new Date(Date.now() + authConfig.otp.expiresInMs());\n const TokenModel = oneTimeTokenModel();\n\n await TokenModel.invalidateActiveFor(user, \"otp\");\n await TokenModel.issue(user, \"otp\", storedCodeHash(user, code), expiresAt);\n\n const message: OtpMessage = { body: authConfig.otp.message(code), code, expiresAt };\n\n await deliver(phone, message, channel);\n}\n\n/**\n * Verify a phone login code and log the user in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * Each check first counts an attempt atomically; once `auth.otp.maxAttempts`\n * (default 5) have been counted the code is invalidated, so it is compared at\n * most that many times even under concurrent guesses. A correct code is\n * consumed single-use. Throttle the route with `otpVerifyThrottleMiddleware()`.\n *\n * @throws InvalidOneTimeTokenError (400, EC008) — the same for an unknown\n * phone, a wrong, expired, used or exhausted code.\n */\nexport async function verifyOtp<T extends Auth>(\n Model: ChildModel<T>,\n phone: string,\n code: string,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<T>> {\n if (typeof phone !== \"string\" || phone.length === 0) throw invalidCode();\n if (typeof code !== \"string\" || !CODE_PATTERN.test(code)) throw invalidCode();\n\n const user = (await Model.first({ [authConfig.otp.phoneField()]: phone })) as T | null;\n\n if (!user) throw invalidCode();\n\n const row = await oneTimeTokenModel().findActiveFor(user, \"otp\");\n\n if (!row || row.isConsumed || row.isExpired) throw invalidCode();\n\n if (!(await row.recordAttempt(authConfig.otp.maxAttempts()))) {\n await row.consumeIfActive();\n\n throw invalidCode();\n }\n\n if (!codeMatches(user, String(row.get(\"token_hash\") ?? \"\"), code)) throw invalidCode();\n\n if (!(await row.consumeIfActive())) throw invalidCode();\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;AAmBA,MAAM,eAAe;;AAGrB,SAAS,cAAwC;CAC/C,OAAO,IAAI,yBAAyB,0BAA0B;AAChE;;;;;;;AAQA,SAAS,QAAQ,MAAY,MAAc,MAAsB;CAC/D,MAAM,MAAM,WAAW,UAAU,WAAW,YAAY,OAAO,CAAC,CAAC,CAC9D,OAAO,sBAAsB,CAAC,CAC9B,OAAO;CAEV,OAAO,WAAW,UAAU,GAAG,CAAC,CAC7B,OAAO,GAAG,KAAK,GAAG,KAAK,SAAS,GAAG,KAAK,GAAG,GAAG,MAAM,CAAC,CACrD,OAAO,WAAW;AACvB;;AAGA,SAAS,eAAe,MAAY,MAAsB;CACxD,MAAM,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAEjD,OAAO,GAAG,KAAK,GAAG,QAAQ,MAAM,MAAM,IAAI;AAC5C;AAEA,SAAS,YAAY,MAAY,QAAgB,MAAuB;CACtE,MAAM,CAAC,MAAM,OAAO,OAAO,MAAM,GAAG;CAEpC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,UAAU,KAAK,QAAQ,MAAM,MAAM,IAAI,CAAC;AACpE;;;;;;AAOA,eAAe,gBAAgB,SAAqC;CAClE,MAAM,SAAS,WAAW,IAAI,KAAK;CAEnC,IAAI,QAAQ,OAAO;CAEnB,MAAM,EAAE,WAAW,MAAM,iBACvB,6BACA,eACF;CAEA,QAAQ,OAAO,SAAS,gBAAgB,OAAO,QAAQ,WAAW,CAAC,CAAC,KAAK,OAAO,OAAO;AACzF;;;;;;;;;;;;;;;AAgBA,eAAsB,WACpB,OACA,OACA,UAAgC,CAAC,GAClB;CACf,MAAM,UAAU,QAAQ,WAAW,WAAW,IAAI,QAAQ;CAC1D,MAAM,UAAU,MAAM,gBAAgB,OAAO;CAE7C,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;CAErD,MAAM,OAAQ,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,WAAW,IAAI,MAAM,CAAC;CAExE,IAAI,CAAC,MAAM;CAEX,MAAM,OAAO,OAAO,UAAU,GAAG,GAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC5D,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW,IAAI,YAAY,CAAC;CACpE,MAAM,aAAa,kBAAkB;CAErC,MAAM,WAAW,oBAAoB,MAAM,KAAK;CAChD,MAAM,WAAW,MAAM,MAAM,OAAO,eAAe,MAAM,IAAI,GAAG,SAAS;CAIzE,MAAM,QAAQ,OAAO;EAFS,MAAM,WAAW,IAAI,QAAQ,IAAI;EAAG;EAAM;CAE7C,GAAG,OAAO;AACvC;;;;;;;;;;;;;AAcA,eAAsB,UACpB,OACA,OACA,MACA,YACyB;CACzB,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG,MAAM,YAAY;CACvE,IAAI,OAAO,SAAS,YAAY,CAAC,aAAa,KAAK,IAAI,GAAG,MAAM,YAAY;CAE5E,MAAM,OAAQ,MAAM,MAAM,MAAM,GAAG,WAAW,IAAI,WAAW,IAAI,MAAM,CAAC;CAExE,IAAI,CAAC,MAAM,MAAM,YAAY;CAE7B,MAAM,MAAM,MAAM,kBAAkB,CAAC,CAAC,cAAc,MAAM,KAAK;CAE/D,IAAI,CAAC,OAAO,IAAI,cAAc,IAAI,WAAW,MAAM,YAAY;CAE/D,IAAI,CAAE,MAAM,IAAI,cAAc,WAAW,IAAI,YAAY,CAAC,GAAI;EAC5D,MAAM,IAAI,gBAAgB;EAE1B,MAAM,YAAY;CACpB;CAEA,IAAI,CAAC,YAAY,MAAM,OAAO,IAAI,IAAI,YAAY,KAAK,EAAE,GAAG,IAAI,GAAG,MAAM,YAAY;CAErF,IAAI,CAAE,MAAM,IAAI,gBAAgB,GAAI,MAAM,YAAY;CAEtD,OAAO,YAAY,cAAc,MAAM,UAAU;AACnD"}
|
package/esm/passkeys/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { PasskeyOptionsJSON, PasskeyResponseJSON } from "./simplewebauthn.mjs";
|
|
2
2
|
import { generatePasskeyAuthenticationOptions, verifyPasskeyAuthentication } from "./passkey-authentication.mjs";
|
|
3
|
-
import { generatePasskeyRegistrationOptions, verifyPasskeyRegistration } from "./passkey-registration.mjs";
|
|
3
|
+
import { generatePasskeyRegistrationOptions, verifyPasskeyRegistration } from "./passkey-registration.mjs";
|
|
4
|
+
export { type PasskeyOptionsJSON, type PasskeyResponseJSON, generatePasskeyAuthenticationOptions, generatePasskeyRegistrationOptions, verifyPasskeyAuthentication, verifyPasskeyRegistration };
|
package/esm/passkeys/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { generatePasskeyAuthenticationOptions, verifyPasskeyAuthentication } from "./passkey-authentication.mjs";
|
|
2
2
|
import { generatePasskeyRegistrationOptions, verifyPasskeyRegistration } from "./passkey-registration.mjs";
|
|
3
3
|
|
|
4
|
-
export {
|
|
4
|
+
export { generatePasskeyAuthenticationOptions, generatePasskeyRegistrationOptions, verifyPasskeyAuthentication, verifyPasskeyRegistration };
|
|
@@ -2,7 +2,6 @@ import { Auth } from "../models/auth.model.mjs";
|
|
|
2
2
|
import { DeviceInfo, LoginResult } from "../contracts/types.mjs";
|
|
3
3
|
import { PasskeyOptionsJSON, PasskeyResponseJSON } from "./simplewebauthn.mjs";
|
|
4
4
|
import { Request } from "@warlock.js/core";
|
|
5
|
-
|
|
6
5
|
//#region ../../packages/auth/src/passkeys/passkey-authentication.d.ts
|
|
7
6
|
/**
|
|
8
7
|
* Authentication options for a passkey login. No user is identified yet
|
|
@@ -13,7 +12,7 @@ import { Request } from "@warlock.js/core";
|
|
|
13
12
|
* // POST /auth/passkeys/login/options
|
|
14
13
|
* response.success(await generatePasskeyAuthenticationOptions());
|
|
15
14
|
*/
|
|
16
|
-
declare function generatePasskeyAuthenticationOptions(): Promise<PasskeyOptionsJSON>;
|
|
15
|
+
export declare function generatePasskeyAuthenticationOptions(): Promise<PasskeyOptionsJSON>;
|
|
17
16
|
/**
|
|
18
17
|
* Verify a passkey assertion and log its owner in through
|
|
19
18
|
* `authService.completeLogin` — the same outcome as password login.
|
|
@@ -27,7 +26,6 @@ declare function generatePasskeyAuthenticationOptions(): Promise<PasskeyOptionsJ
|
|
|
27
26
|
*
|
|
28
27
|
* @throws InvalidPasskeyError (400, EC011) on any rejection.
|
|
29
28
|
*/
|
|
30
|
-
declare function verifyPasskeyAuthentication(request: Request, credential: PasskeyResponseJSON, deviceInfo?: DeviceInfo): Promise<LoginResult<Auth>>;
|
|
29
|
+
export declare function verifyPasskeyAuthentication(request: Request, credential: PasskeyResponseJSON, deviceInfo?: DeviceInfo): Promise<LoginResult<Auth>>;
|
|
31
30
|
//#endregion
|
|
32
|
-
export { generatePasskeyAuthenticationOptions, verifyPasskeyAuthentication };
|
|
33
31
|
//# sourceMappingURL=passkey-authentication.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkey-authentication.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-authentication.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"passkey-authentication.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-authentication.ts"],"mappings":";;;;;;;;;;;;;;wBAiDsB,wCAAwC,QAAQ;;;;;;;;;;;;;;wBA0BhD,4BACpB,SAAS,SACT,YAAY,qBACZ,aAAa,aACZ,QAAQ,YAAY"}
|
|
@@ -75,7 +75,8 @@ async function verifyPasskeyAuthentication(request, credential, deviceInfo) {
|
|
|
75
75
|
requireUserVerification: false
|
|
76
76
|
});
|
|
77
77
|
} catch (error) {
|
|
78
|
-
|
|
78
|
+
const reason = counterRegressed(stored.counter, presentedCounter(credential)) ? "counter-regression" : "authentication-verification-failed";
|
|
79
|
+
throw new InvalidPasskeyError(reason, error);
|
|
79
80
|
}
|
|
80
81
|
if (!verification.verified) throw new InvalidPasskeyError("authentication-not-verified");
|
|
81
82
|
const previous = stored.counter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkey-authentication.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-authentication.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport { config, type Request } from \"@warlock.js/core\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidPasskeyError } from \"../errors/invalid-passkey.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { PasskeyCredential } from \"../models/passkey-credential\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport {\n assertPasskeyOrigin,\n storePasskeyChallenge,\n takePasskeyChallenge,\n} from \"./passkey-challenges\";\nimport {\n loadSimpleWebAuthn,\n type PasskeyOptionsJSON,\n type PasskeyResponseJSON,\n} from \"./simplewebauthn\";\n\n/** A counter that did not advance past the stored one (when either is non-zero): a cloned authenticator. */\nfunction counterRegressed(stored: number, presented: number | undefined): boolean {\n if (presented === undefined) return false;\n\n return (stored > 0 || presented > 0) && !(presented > stored);\n}\n\n/**\n * The signature counter an assertion CLAIMS (bytes 33–36 of authenticatorData).\n * Unverified — only used to label a rejection the SDK already made.\n */\nfunction presentedCounter(credential: PasskeyResponseJSON): number | undefined {\n const encoded = credential.response.authenticatorData;\n\n if (typeof encoded !== \"string\") return undefined;\n\n const authData = Buffer.from(encoded, \"base64url\");\n\n return authData.length >= 37 ? authData.readUInt32BE(33) : undefined;\n}\n\n/**\n * Authentication options for a passkey login. No user is identified yet\n * (discoverable credentials), so the stored single-use challenge has no owner;\n * the credential the browser returns decides the user.\n *\n * @example\n * // POST /auth/passkeys/login/options\n * response.success(await generatePasskeyAuthenticationOptions());\n */\nexport async function generatePasskeyAuthenticationOptions(): Promise<PasskeyOptionsJSON> {\n const webauthn = await loadSimpleWebAuthn();\n\n const options = await webauthn.generateAuthenticationOptions({\n rpID: authConfig.passkeys.settings().rpID,\n userVerification: \"preferred\",\n });\n\n await storePasskeyChallenge(\"passkey-authentication\", options.challenge);\n\n return options;\n}\n\n/**\n * Verify a passkey assertion and log its owner in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * Checks, in order: the request `Origin` (CSRF), the single-use challenge\n * (consumed even if what follows fails), the credential is registered, the\n * assertion via `@simplewebauthn/server`, then that the signature counter\n * ADVANCED — a counter that did not move past the stored one (when either is\n * non-zero) means a cloned authenticator and is rejected. The new counter is\n * written with a compare-and-set.\n *\n * @throws InvalidPasskeyError (400, EC011) on any rejection.\n */\nexport async function verifyPasskeyAuthentication(\n request: Request,\n credential: PasskeyResponseJSON,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<Auth>> {\n const webauthn = await loadSimpleWebAuthn();\n const origins = assertPasskeyOrigin(request);\n const { challenge } = await takePasskeyChallenge(\"passkey-authentication\", credential);\n\n const stored =\n typeof credential.id === \"string\"\n ? await PasskeyCredential.findByCredentialId(credential.id)\n : null;\n\n if (!stored) {\n throw new InvalidPasskeyError(\"unknown-credential\");\n }\n\n let verification: Awaited<ReturnType<typeof webauthn.verifyAuthenticationResponse>>;\n\n try {\n verification = await webauthn.verifyAuthenticationResponse({\n response: credential,\n expectedChallenge: challenge,\n expectedOrigin: origins,\n expectedRPID: authConfig.passkeys.settings().rpID,\n credential: {\n id: stored.credentialId,\n publicKey: new Uint8Array(Buffer.from(String(stored.get(\"public_key\")), \"base64url\")),\n counter: stored.counter,\n transports: stored.get(\"transports\") ?? undefined,\n },\n requireUserVerification: false,\n });\n } catch (error) {\n // The SDK enforces the counter rule itself and throws a generic Error for\n // it; keep clone detection visible as its own reason in the logs.\n const reason = counterRegressed(stored.counter, presentedCounter(credential))\n ? \"counter-regression\"\n : \"authentication-verification-failed\";\n\n throw new InvalidPasskeyError(reason, error);\n }\n\n if (!verification.verified) {\n throw new InvalidPasskeyError(\"authentication-not-verified\");\n }\n\n const previous = stored.counter;\n const next = Number(verification.authenticationInfo.newCounter);\n\n if (counterRegressed(previous, next)) {\n throw new InvalidPasskeyError(\"counter-regression\");\n }\n\n if (!(await stored.advanceCounter(previous, next))) {\n throw new InvalidPasskeyError(\"counter-changed-concurrently\");\n }\n\n const UserModel = config.key<ChildModel<Auth> | undefined>(`auth.userType.${stored.userType}`);\n const user = UserModel ? ((await UserModel.find(stored.userId)) as Auth | null) : null;\n\n if (!user) {\n throw new InvalidPasskeyError(\"credential-owner-missing\");\n }\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;;;AAoBA,SAAS,iBAAiB,QAAgB,WAAwC;CAChF,IAAI,cAAc,QAAW,OAAO;CAEpC,QAAQ,SAAS,KAAK,YAAY,MAAM,EAAE,YAAY;AACxD;;;;;AAMA,SAAS,iBAAiB,YAAqD;CAC7E,MAAM,UAAU,WAAW,SAAS;CAEpC,IAAI,OAAO,YAAY,UAAU,OAAO;CAExC,MAAM,WAAW,OAAO,KAAK,SAAS,WAAW;CAEjD,OAAO,SAAS,UAAU,KAAK,SAAS,aAAa,EAAE,IAAI;AAC7D;;;;;;;;;;AAWA,eAAsB,uCAAoE;CAGxF,MAAM,UAAU,OAAM,MAFC,mBAAmB,EAEZ,CAAC,8BAA8B;EAC3D,MAAM,WAAW,SAAS,SAAS,CAAC,CAAC;EACrC,kBAAkB;CACpB,CAAC;CAED,MAAM,sBAAsB,0BAA0B,QAAQ,SAAS;CAEvE,OAAO;AACT;;;;;;;;;;;;;;AAeA,eAAsB,4BACpB,SACA,YACA,YAC4B;CAC5B,MAAM,WAAW,MAAM,mBAAmB;CAC1C,MAAM,UAAU,oBAAoB,OAAO;CAC3C,MAAM,EAAE,cAAc,MAAM,qBAAqB,0BAA0B,UAAU;CAErF,MAAM,SACJ,OAAO,WAAW,OAAO,WACrB,MAAM,kBAAkB,mBAAmB,WAAW,EAAE,IACxD;CAEN,IAAI,CAAC,QACH,MAAM,IAAI,oBAAoB,oBAAoB;CAGpD,IAAI;CAEJ,IAAI;EACF,eAAe,MAAM,SAAS,6BAA6B;GACzD,UAAU;GACV,mBAAmB;GACnB,gBAAgB;GAChB,cAAc,WAAW,SAAS,SAAS,CAAC,CAAC;GAC7C,YAAY;IACV,IAAI,OAAO;IACX,WAAW,IAAI,WAAW,OAAO,KAAK,OAAO,OAAO,IAAI,YAAY,CAAC,GAAG,WAAW,CAAC;IACpF,SAAS,OAAO;IAChB,YAAY,OAAO,IAAI,YAAY,KAAK;GAC1C;GACA,yBAAyB;EAC3B,CAAC;CACH,SAAS,OAAO;
|
|
1
|
+
{"version":3,"file":"passkey-authentication.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-authentication.ts"],"sourcesContent":["import type { ChildModel } from \"@warlock.js/cascade\";\nimport { config, type Request } from \"@warlock.js/core\";\nimport type { DeviceInfo, LoginResult } from \"../contracts/types\";\nimport { InvalidPasskeyError } from \"../errors/invalid-passkey.error\";\nimport type { Auth } from \"../models/auth.model\";\nimport { PasskeyCredential } from \"../models/passkey-credential\";\nimport { authConfig } from \"../services/auth-config\";\nimport { authService } from \"../services/auth.service\";\nimport {\n assertPasskeyOrigin,\n storePasskeyChallenge,\n takePasskeyChallenge,\n} from \"./passkey-challenges\";\nimport {\n loadSimpleWebAuthn,\n type PasskeyOptionsJSON,\n type PasskeyResponseJSON,\n} from \"./simplewebauthn\";\n\n/** A counter that did not advance past the stored one (when either is non-zero): a cloned authenticator. */\nfunction counterRegressed(stored: number, presented: number | undefined): boolean {\n if (presented === undefined) return false;\n\n return (stored > 0 || presented > 0) && !(presented > stored);\n}\n\n/**\n * The signature counter an assertion CLAIMS (bytes 33–36 of authenticatorData).\n * Unverified — only used to label a rejection the SDK already made.\n */\nfunction presentedCounter(credential: PasskeyResponseJSON): number | undefined {\n const encoded = credential.response.authenticatorData;\n\n if (typeof encoded !== \"string\") return undefined;\n\n const authData = Buffer.from(encoded, \"base64url\");\n\n return authData.length >= 37 ? authData.readUInt32BE(33) : undefined;\n}\n\n/**\n * Authentication options for a passkey login. No user is identified yet\n * (discoverable credentials), so the stored single-use challenge has no owner;\n * the credential the browser returns decides the user.\n *\n * @example\n * // POST /auth/passkeys/login/options\n * response.success(await generatePasskeyAuthenticationOptions());\n */\nexport async function generatePasskeyAuthenticationOptions(): Promise<PasskeyOptionsJSON> {\n const webauthn = await loadSimpleWebAuthn();\n\n const options = await webauthn.generateAuthenticationOptions({\n rpID: authConfig.passkeys.settings().rpID,\n userVerification: \"preferred\",\n });\n\n await storePasskeyChallenge(\"passkey-authentication\", options.challenge);\n\n return options;\n}\n\n/**\n * Verify a passkey assertion and log its owner in through\n * `authService.completeLogin` — the same outcome as password login.\n *\n * Checks, in order: the request `Origin` (CSRF), the single-use challenge\n * (consumed even if what follows fails), the credential is registered, the\n * assertion via `@simplewebauthn/server`, then that the signature counter\n * ADVANCED — a counter that did not move past the stored one (when either is\n * non-zero) means a cloned authenticator and is rejected. The new counter is\n * written with a compare-and-set.\n *\n * @throws InvalidPasskeyError (400, EC011) on any rejection.\n */\nexport async function verifyPasskeyAuthentication(\n request: Request,\n credential: PasskeyResponseJSON,\n deviceInfo?: DeviceInfo,\n): Promise<LoginResult<Auth>> {\n const webauthn = await loadSimpleWebAuthn();\n const origins = assertPasskeyOrigin(request);\n const { challenge } = await takePasskeyChallenge(\"passkey-authentication\", credential);\n\n const stored =\n typeof credential.id === \"string\"\n ? await PasskeyCredential.findByCredentialId(credential.id)\n : null;\n\n if (!stored) {\n throw new InvalidPasskeyError(\"unknown-credential\");\n }\n\n let verification: Awaited<ReturnType<typeof webauthn.verifyAuthenticationResponse>>;\n\n try {\n verification = await webauthn.verifyAuthenticationResponse({\n response: credential,\n expectedChallenge: challenge,\n expectedOrigin: origins,\n expectedRPID: authConfig.passkeys.settings().rpID,\n credential: {\n id: stored.credentialId,\n publicKey: new Uint8Array(Buffer.from(String(stored.get(\"public_key\")), \"base64url\")),\n counter: stored.counter,\n transports: stored.get(\"transports\") ?? undefined,\n },\n requireUserVerification: false,\n });\n } catch (error) {\n // The SDK enforces the counter rule itself and throws a generic Error for\n // it; keep clone detection visible as its own reason in the logs.\n const reason = counterRegressed(stored.counter, presentedCounter(credential))\n ? \"counter-regression\"\n : \"authentication-verification-failed\";\n\n throw new InvalidPasskeyError(reason, error);\n }\n\n if (!verification.verified) {\n throw new InvalidPasskeyError(\"authentication-not-verified\");\n }\n\n const previous = stored.counter;\n const next = Number(verification.authenticationInfo.newCounter);\n\n if (counterRegressed(previous, next)) {\n throw new InvalidPasskeyError(\"counter-regression\");\n }\n\n if (!(await stored.advanceCounter(previous, next))) {\n throw new InvalidPasskeyError(\"counter-changed-concurrently\");\n }\n\n const UserModel = config.key<ChildModel<Auth> | undefined>(`auth.userType.${stored.userType}`);\n const user = UserModel ? ((await UserModel.find(stored.userId)) as Auth | null) : null;\n\n if (!user) {\n throw new InvalidPasskeyError(\"credential-owner-missing\");\n }\n\n return authService.completeLogin(user, deviceInfo);\n}\n"],"mappings":";;;;;;;;;;;AAoBA,SAAS,iBAAiB,QAAgB,WAAwC;CAChF,IAAI,cAAc,QAAW,OAAO;CAEpC,QAAQ,SAAS,KAAK,YAAY,MAAM,EAAE,YAAY;AACxD;;;;;AAMA,SAAS,iBAAiB,YAAqD;CAC7E,MAAM,UAAU,WAAW,SAAS;CAEpC,IAAI,OAAO,YAAY,UAAU,OAAO;CAExC,MAAM,WAAW,OAAO,KAAK,SAAS,WAAW;CAEjD,OAAO,SAAS,UAAU,KAAK,SAAS,aAAa,EAAE,IAAI;AAC7D;;;;;;;;;;AAWA,eAAsB,uCAAoE;CAGxF,MAAM,UAAU,OAAM,MAFC,mBAAmB,EAEZ,CAAC,8BAA8B;EAC3D,MAAM,WAAW,SAAS,SAAS,CAAC,CAAC;EACrC,kBAAkB;CACpB,CAAC;CAED,MAAM,sBAAsB,0BAA0B,QAAQ,SAAS;CAEvE,OAAO;AACT;;;;;;;;;;;;;;AAeA,eAAsB,4BACpB,SACA,YACA,YAC4B;CAC5B,MAAM,WAAW,MAAM,mBAAmB;CAC1C,MAAM,UAAU,oBAAoB,OAAO;CAC3C,MAAM,EAAE,cAAc,MAAM,qBAAqB,0BAA0B,UAAU;CAErF,MAAM,SACJ,OAAO,WAAW,OAAO,WACrB,MAAM,kBAAkB,mBAAmB,WAAW,EAAE,IACxD;CAEN,IAAI,CAAC,QACH,MAAM,IAAI,oBAAoB,oBAAoB;CAGpD,IAAI;CAEJ,IAAI;EACF,eAAe,MAAM,SAAS,6BAA6B;GACzD,UAAU;GACV,mBAAmB;GACnB,gBAAgB;GAChB,cAAc,WAAW,SAAS,SAAS,CAAC,CAAC;GAC7C,YAAY;IACV,IAAI,OAAO;IACX,WAAW,IAAI,WAAW,OAAO,KAAK,OAAO,OAAO,IAAI,YAAY,CAAC,GAAG,WAAW,CAAC;IACpF,SAAS,OAAO;IAChB,YAAY,OAAO,IAAI,YAAY,KAAK;GAC1C;GACA,yBAAyB;EAC3B,CAAC;CACH,SAAS,OAAO;EAGd,MAAM,SAAS,iBAAiB,OAAO,SAAS,iBAAiB,UAAU,CAAC,IACxE,uBACA;EAEJ,MAAM,IAAI,oBAAoB,QAAQ,KAAK;CAC7C;CAEA,IAAI,CAAC,aAAa,UAChB,MAAM,IAAI,oBAAoB,6BAA6B;CAG7D,MAAM,WAAW,OAAO;CACxB,MAAM,OAAO,OAAO,aAAa,mBAAmB,UAAU;CAE9D,IAAI,iBAAiB,UAAU,IAAI,GACjC,MAAM,IAAI,oBAAoB,oBAAoB;CAGpD,IAAI,CAAE,MAAM,OAAO,eAAe,UAAU,IAAI,GAC9C,MAAM,IAAI,oBAAoB,8BAA8B;CAG9D,MAAM,YAAY,OAAO,IAAkC,iBAAiB,OAAO,UAAU;CAC7F,MAAM,OAAO,YAAc,MAAM,UAAU,KAAK,OAAO,MAAM,IAAqB;CAElF,IAAI,CAAC,MACH,MAAM,IAAI,oBAAoB,0BAA0B;CAG1D,OAAO,YAAY,cAAc,MAAM,UAAU;AACnD"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Auth } from "../models/auth.model.mjs";
|
|
2
2
|
import { PasskeyCredential } from "../models/passkey-credential/passkey-credential.model.mjs";
|
|
3
|
+
import "../models/passkey-credential/index.mjs";
|
|
3
4
|
import { PasskeyOptionsJSON, PasskeyResponseJSON } from "./simplewebauthn.mjs";
|
|
4
5
|
import { Request } from "@warlock.js/core";
|
|
5
|
-
|
|
6
6
|
//#region ../../packages/auth/src/passkeys/passkey-registration.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Registration options for a LOGGED-IN user adding a passkey. The challenge is
|
|
@@ -13,7 +13,7 @@ import { Request } from "@warlock.js/core";
|
|
|
13
13
|
* // POST /auth/passkeys/register/options (authMiddleware)
|
|
14
14
|
* response.success(await generatePasskeyRegistrationOptions(request.locals.user));
|
|
15
15
|
*/
|
|
16
|
-
declare function generatePasskeyRegistrationOptions(user: Auth): Promise<PasskeyOptionsJSON>;
|
|
16
|
+
export declare function generatePasskeyRegistrationOptions(user: Auth): Promise<PasskeyOptionsJSON>;
|
|
17
17
|
/**
|
|
18
18
|
* Verify the browser's registration response and store the credential.
|
|
19
19
|
* Checks, in order: the request `Origin` (CSRF), the single-use challenge
|
|
@@ -23,7 +23,6 @@ declare function generatePasskeyRegistrationOptions(user: Auth): Promise<Passkey
|
|
|
23
23
|
*
|
|
24
24
|
* @throws InvalidPasskeyError (400, EC011) on any rejection.
|
|
25
25
|
*/
|
|
26
|
-
declare function verifyPasskeyRegistration(request: Request, user: Auth, credential: PasskeyResponseJSON): Promise<PasskeyCredential>;
|
|
26
|
+
export declare function verifyPasskeyRegistration(request: Request, user: Auth, credential: PasskeyResponseJSON): Promise<PasskeyCredential>;
|
|
27
27
|
//#endregion
|
|
28
|
-
export { generatePasskeyRegistrationOptions, verifyPasskeyRegistration };
|
|
29
28
|
//# sourceMappingURL=passkey-registration.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkey-registration.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-registration.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"passkey-registration.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/passkey-registration.ts"],"mappings":";;;;;;;;;;;;;;;wBAoCsB,mCAAmC,MAAM,OAAO,QAAQ;;;;;;;;;;wBAgCxD,0BACpB,SAAS,SACT,MAAM,MACN,YAAY,sBACX,QAAQ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region ../../packages/auth/src/passkeys/simplewebauthn.d.ts
|
|
2
2
|
/** The browser's JSON-serialized credential (`startRegistration` / `startAuthentication` result). */
|
|
3
|
-
type PasskeyResponseJSON = {
|
|
3
|
+
export type PasskeyResponseJSON = {
|
|
4
4
|
id: string;
|
|
5
5
|
rawId: string;
|
|
6
6
|
type: string;
|
|
@@ -12,10 +12,9 @@ type PasskeyResponseJSON = {
|
|
|
12
12
|
[key: string]: unknown;
|
|
13
13
|
};
|
|
14
14
|
/** Options JSON handed to the browser; `challenge` is what auth stores. */
|
|
15
|
-
type PasskeyOptionsJSON = {
|
|
15
|
+
export type PasskeyOptionsJSON = {
|
|
16
16
|
challenge: string;
|
|
17
17
|
[key: string]: unknown;
|
|
18
18
|
};
|
|
19
19
|
//#endregion
|
|
20
|
-
export { PasskeyOptionsJSON, PasskeyResponseJSON };
|
|
21
20
|
//# sourceMappingURL=simplewebauthn.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simplewebauthn.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/simplewebauthn.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"simplewebauthn.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/passkeys/simplewebauthn.ts"],"mappings":";;YAMY;EACV;EACA;EACA;EACA;IACE;IACA;KACC;;GAEF;;;YAIS;EACV;GACC"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { AppleProviderConfig, AuthProvider, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
2
|
import { JoseModule } from "./google-provider.mjs";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/auth/src/providers/apple-provider.d.ts
|
|
5
4
|
/** The `warlock add` feature that installs `jose`. */
|
|
6
|
-
declare const APPLE_AUTH_FEATURE = "auth-apple";
|
|
5
|
+
export declare const APPLE_AUTH_FEATURE = "auth-apple";
|
|
7
6
|
type AppleSignJwtBuilder = {
|
|
8
7
|
setIssuer: (issuer: string) => AppleSignJwtBuilder;
|
|
9
8
|
setIssuedAt: () => AppleSignJwtBuilder;
|
|
@@ -17,7 +16,7 @@ type AppleSignJwtBuilder = {
|
|
|
17
16
|
* `importPKCS8` and `SignJWT` sign the ES256 client-secret JWT Apple requires
|
|
18
17
|
* instead of a static client secret.
|
|
19
18
|
*/
|
|
20
|
-
type AppleJoseModule = JoseModule & {
|
|
19
|
+
export type AppleJoseModule = JoseModule & {
|
|
21
20
|
importPKCS8: (pkcs8: string, alg: string) => Promise<unknown>;
|
|
22
21
|
SignJWT: new (payload: Record<string, unknown>) => {
|
|
23
22
|
setProtectedHeader: (header: Record<string, unknown>) => AppleSignJwtBuilder;
|
|
@@ -51,17 +50,14 @@ type AppleJoseModule = JoseModule & {
|
|
|
51
50
|
* `false`) as unverified. A private-relay address (`@privaterelay.appleid.com`)
|
|
52
51
|
* is still a real, working forwarding address and is accepted as any other.
|
|
53
52
|
*/
|
|
54
|
-
declare class AppleProvider implements AuthProvider {
|
|
53
|
+
export declare class AppleProvider implements AuthProvider {
|
|
55
54
|
private readonly settings;
|
|
56
55
|
readonly name = "apple";
|
|
57
56
|
/** Apple's default scopes (`name`, `email`) force `response_mode=form_post`. */
|
|
58
57
|
readonly callbackMode = "form_post";
|
|
59
58
|
constructor(settings: AppleProviderConfig);
|
|
60
59
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
61
|
-
handleCallback({
|
|
62
|
-
query,
|
|
63
|
-
expected
|
|
64
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
60
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
65
61
|
/** Sign a fresh ES256 client-secret JWT — Apple has no static one. */
|
|
66
62
|
private clientSecret;
|
|
67
63
|
/** Redeem the authorization code (with the PKCE verifier) for an id_token. */
|
|
@@ -70,5 +66,5 @@ declare class AppleProvider implements AuthProvider {
|
|
|
70
66
|
private verifyIdToken;
|
|
71
67
|
}
|
|
72
68
|
//#endregion
|
|
73
|
-
export {
|
|
69
|
+
export {};
|
|
74
70
|
//# sourceMappingURL=apple-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/apple-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apple-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/apple-provider.ts"],"mappings":";;;;qBAqBa;KAER;EACH,YAAY,mBAAmB;EAC/B,mBAAmB;EACnB,oBAAoB,iBAAiB;EACrC,cAAc,qBAAqB;EACnC,aAAa,oBAAoB;EACjC,OAAO,iBAAiB;;;;;;;YAQd,kBAAkB;EAC5B,cAAc,eAAe,gBAAgB;EAC7C,cAAc,SAAS;IACrB,qBAAqB,QAAQ,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAmChD,yBAAyB;mBAKA;WAJpB;;WAEA;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAqBlB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UAgCtB;;UAmBA;;UAuCA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AuthProvider, DiscordProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/auth/src/providers/discord-provider.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Discord sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
|
|
@@ -7,19 +6,15 @@ import { AuthProvider, DiscordProviderConfig, ProviderAuthorizationState, Provid
|
|
|
7
6
|
* access token is redeemed once for `/users/@me` over a direct HTTPS call.
|
|
8
7
|
* No vendor SDK is involved.
|
|
9
8
|
*/
|
|
10
|
-
declare class DiscordProvider implements AuthProvider {
|
|
9
|
+
export declare class DiscordProvider implements AuthProvider {
|
|
11
10
|
private readonly settings;
|
|
12
11
|
readonly name = "discord";
|
|
13
12
|
constructor(settings: DiscordProviderConfig);
|
|
14
13
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
15
|
-
handleCallback({
|
|
16
|
-
query,
|
|
17
|
-
expected
|
|
18
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
14
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
19
15
|
/** Redeem the authorization code (with the PKCE verifier) for an access token. */
|
|
20
16
|
private exchangeCode;
|
|
21
17
|
private fetchUser;
|
|
22
18
|
}
|
|
23
19
|
//#endregion
|
|
24
|
-
export { DiscordProvider };
|
|
25
20
|
//# sourceMappingURL=discord-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discord-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/discord-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"discord-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/discord-provider.ts"],"mappings":";;;;;;;;qBA8Ba,2BAA2B;mBAGF;WAFpB;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAclB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UA4BtB;UAiCA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AuthProvider, FacebookProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/auth/src/providers/facebook-provider.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Facebook sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
|
|
@@ -18,19 +17,15 @@ import { AuthProvider, FacebookProviderConfig, ProviderAuthorizationState, Provi
|
|
|
18
17
|
* already rejects as an unverified/missing email, the same path GitHub's
|
|
19
18
|
* unverified addresses take.
|
|
20
19
|
*/
|
|
21
|
-
declare class FacebookProvider implements AuthProvider {
|
|
20
|
+
export declare class FacebookProvider implements AuthProvider {
|
|
22
21
|
private readonly settings;
|
|
23
22
|
readonly name = "facebook";
|
|
24
23
|
constructor(settings: FacebookProviderConfig);
|
|
25
24
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
26
|
-
handleCallback({
|
|
27
|
-
query,
|
|
28
|
-
expected
|
|
29
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
25
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
30
26
|
/** Redeem the authorization code (with the PKCE verifier) for an access token. */
|
|
31
27
|
private exchangeCode;
|
|
32
28
|
private fetchUser;
|
|
33
29
|
}
|
|
34
30
|
//#endregion
|
|
35
|
-
export { FacebookProvider };
|
|
36
31
|
//# sourceMappingURL=facebook-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/facebook-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"facebook-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/facebook-provider.ts"],"mappings":";;;;;;;;;;;;;;;;;;;qBAuCa,4BAA4B;mBAGH;WAFpB;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAclB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UA2BtB;UAkCA"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AuthProvider, GitHubProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/auth/src/providers/github-provider.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* GitHub sign-in: plain OAuth 2 authorization code flow with PKCE (S256).
|
|
@@ -11,15 +10,12 @@ import { AuthProvider, GitHubProviderConfig, ProviderAuthorizationState, Provide
|
|
|
11
10
|
* `/user.email` is `null` unless the user made it public — the emails
|
|
12
11
|
* endpoint is also where GitHub reports whether an address is verified.
|
|
13
12
|
*/
|
|
14
|
-
declare class GitHubProvider implements AuthProvider {
|
|
13
|
+
export declare class GitHubProvider implements AuthProvider {
|
|
15
14
|
private readonly settings;
|
|
16
15
|
readonly name = "github";
|
|
17
16
|
constructor(settings: GitHubProviderConfig);
|
|
18
17
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
19
|
-
handleCallback({
|
|
20
|
-
query,
|
|
21
|
-
expected
|
|
22
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
18
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
23
19
|
/** Redeem the authorization code (with the PKCE verifier) for an access token. */
|
|
24
20
|
private exchangeCode;
|
|
25
21
|
private fetchUser;
|
|
@@ -27,5 +23,4 @@ declare class GitHubProvider implements AuthProvider {
|
|
|
27
23
|
private fetchPrimaryEmail;
|
|
28
24
|
}
|
|
29
25
|
//#endregion
|
|
30
|
-
export { GitHubProvider };
|
|
31
26
|
//# sourceMappingURL=github-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/github-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"github-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/github-provider.ts"],"mappings":";;;;;;;;;;;;qBA0Ca,0BAA0B;mBAGD;WAFpB;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAclB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UA2BtB;UAwCA;;UAqBA"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { AuthProvider, GoogleProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/auth/src/providers/google-provider.d.ts
|
|
4
3
|
/** The `warlock add` feature that installs `jose`. */
|
|
5
|
-
declare const GOOGLE_AUTH_FEATURE = "auth-google";
|
|
4
|
+
export declare const GOOGLE_AUTH_FEATURE = "auth-google";
|
|
6
5
|
/** The slice of `jose` the Google provider uses — declared locally because `jose` is an optional peer. */
|
|
7
|
-
type JoseModule = {
|
|
6
|
+
export type JoseModule = {
|
|
8
7
|
createRemoteJWKSet: (url: URL) => unknown;
|
|
9
8
|
jwtVerify: (token: string, key: unknown, options: {
|
|
10
9
|
issuer: string[];
|
|
@@ -14,27 +13,23 @@ type JoseModule = {
|
|
|
14
13
|
}>;
|
|
15
14
|
};
|
|
16
15
|
/** PKCE S256 code challenge for a verifier. */
|
|
17
|
-
declare function pkceChallenge(codeVerifier: string): string;
|
|
16
|
+
export declare function pkceChallenge(codeVerifier: string): string;
|
|
18
17
|
/**
|
|
19
18
|
* Google sign-in: OpenID Connect authorization code flow with PKCE (S256),
|
|
20
19
|
* `state` and `nonce`. The code is exchanged with plain `fetch`; the id_token
|
|
21
20
|
* is verified with `jose` against Google's JWKS (signature, `iss`, `aud` = the
|
|
22
21
|
* client id, `exp`), then its `nonce` is compared. No Google SDK.
|
|
23
22
|
*/
|
|
24
|
-
declare class GoogleProvider implements AuthProvider {
|
|
23
|
+
export declare class GoogleProvider implements AuthProvider {
|
|
25
24
|
private readonly settings;
|
|
26
25
|
readonly name = "google";
|
|
27
26
|
constructor(settings: GoogleProviderConfig);
|
|
28
27
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
29
|
-
handleCallback({
|
|
30
|
-
query,
|
|
31
|
-
expected
|
|
32
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
28
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
33
29
|
/** Redeem the authorization code (with the PKCE verifier) for an id_token. */
|
|
34
30
|
private exchangeCode;
|
|
35
31
|
/** Verify signature, issuer, audience and expiry; resolve the claims. */
|
|
36
32
|
private verifyIdToken;
|
|
37
33
|
}
|
|
38
34
|
//#endregion
|
|
39
|
-
export { GOOGLE_AUTH_FEATURE, GoogleProvider, JoseModule, pkceChallenge };
|
|
40
35
|
//# sourceMappingURL=google-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/google-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"google-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/google-provider.ts"],"mappings":";;;qBAkBa;;YAGD;EACV,qBAAqB,KAAK;EAC1B,YACE,eACA,cACA;IAAW;IAAkB;QAC1B;IAAU,SAAS;;;;wBAOV,cAAc;;;;;;;qBAUjB,0BAA0B;mBAGD;WAFpB;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAelB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UAkCtB;;UAkCA"}
|
|
@@ -5,4 +5,5 @@ import { FacebookProvider } from "./facebook-provider.mjs";
|
|
|
5
5
|
import { GitHubProvider } from "./github-provider.mjs";
|
|
6
6
|
import { LINKEDIN_AUTH_FEATURE, LinkedInProvider } from "./linkedin-provider.mjs";
|
|
7
7
|
import { completeProviderLogin, resolveAuthProvider, startProviderLogin } from "./provider-login.mjs";
|
|
8
|
-
import { XProvider } from "./x-provider.mjs";
|
|
8
|
+
import { XProvider } from "./x-provider.mjs";
|
|
9
|
+
export { APPLE_AUTH_FEATURE, AppleJoseModule, AppleProvider, DiscordProvider, FacebookProvider, GOOGLE_AUTH_FEATURE, GitHubProvider, GoogleProvider, JoseModule, LINKEDIN_AUTH_FEATURE, LinkedInProvider, XProvider, completeProviderLogin, pkceChallenge, resolveAuthProvider, startProviderLogin };
|
package/esm/providers/index.mjs
CHANGED
|
@@ -7,4 +7,4 @@ import { LINKEDIN_AUTH_FEATURE, LinkedInProvider } from "./linkedin-provider.mjs
|
|
|
7
7
|
import { XProvider } from "./x-provider.mjs";
|
|
8
8
|
import { completeProviderLogin, resolveAuthProvider, startProviderLogin } from "./provider-login.mjs";
|
|
9
9
|
|
|
10
|
-
export {
|
|
10
|
+
export { APPLE_AUTH_FEATURE, AppleProvider, DiscordProvider, FacebookProvider, GOOGLE_AUTH_FEATURE, GitHubProvider, GoogleProvider, LINKEDIN_AUTH_FEATURE, LinkedInProvider, XProvider, completeProviderLogin, pkceChallenge, resolveAuthProvider, startProviderLogin };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { AuthProvider, LinkedInProviderConfig, ProviderAuthorizationState, ProviderCallbackParams, ProviderProfile } from "../contracts/providers.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/auth/src/providers/linkedin-provider.d.ts
|
|
4
3
|
/** The `warlock add` feature that installs `jose`. */
|
|
5
|
-
declare const LINKEDIN_AUTH_FEATURE = "auth-linkedin";
|
|
4
|
+
export declare const LINKEDIN_AUTH_FEATURE = "auth-linkedin";
|
|
6
5
|
/**
|
|
7
6
|
* LinkedIn sign-in: OpenID Connect authorization code flow with PKCE (S256)
|
|
8
7
|
* and `state` and `nonce` — LinkedIn's "Sign In with LinkedIn using OpenID
|
|
@@ -16,20 +15,16 @@ declare const LINKEDIN_AUTH_FEATURE = "auth-linkedin";
|
|
|
16
15
|
* OIDC docs, not from a live exchange — this package has no LinkedIn test
|
|
17
16
|
* credentials. See the worker report for what is and is not proven.
|
|
18
17
|
*/
|
|
19
|
-
declare class LinkedInProvider implements AuthProvider {
|
|
18
|
+
export declare class LinkedInProvider implements AuthProvider {
|
|
20
19
|
private readonly settings;
|
|
21
20
|
readonly name = "linkedin";
|
|
22
21
|
constructor(settings: LinkedInProviderConfig);
|
|
23
22
|
authorizationUrl(state: ProviderAuthorizationState): string;
|
|
24
|
-
handleCallback({
|
|
25
|
-
query,
|
|
26
|
-
expected
|
|
27
|
-
}: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
23
|
+
handleCallback({ query, expected }: ProviderCallbackParams): Promise<ProviderProfile>;
|
|
28
24
|
/** Redeem the authorization code (with the PKCE verifier) for an id_token. */
|
|
29
25
|
private exchangeCode;
|
|
30
26
|
/** Verify signature, issuer, audience and expiry; resolve the claims. */
|
|
31
27
|
private verifyIdToken;
|
|
32
28
|
}
|
|
33
29
|
//#endregion
|
|
34
|
-
export { LINKEDIN_AUTH_FEATURE, LinkedInProvider };
|
|
35
30
|
//# sourceMappingURL=linkedin-provider.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linkedin-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/linkedin-provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"linkedin-provider.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/linkedin-provider.ts"],"mappings":";;;qBAmBa;;;;;;;;;;;;;;qBAkBA,4BAA4B;mBAGH;WAFpB;EAEhB,YAAoC,UAAU;EAEvC,iBAAiB,OAAO;EAelB,iBACX,OACA,YACC,yBAAyB,QAAQ;;UAkCtB;;UAkCA"}
|
|
@@ -3,14 +3,13 @@ import { AuthProvider } from "../contracts/providers.mjs";
|
|
|
3
3
|
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
|
-
|
|
7
6
|
//#region ../../packages/auth/src/providers/provider-login.d.ts
|
|
8
7
|
/**
|
|
9
8
|
* Resolve a provider by name: a built-in name (`google`, `github`, `discord`,
|
|
10
9
|
* `linkedin`, `apple`, `facebook`, `x`) from `auth.providers.<name>`, anything
|
|
11
10
|
* else from `auth.providers.custom`. Throws naming the config key when absent.
|
|
12
11
|
*/
|
|
13
|
-
declare function resolveAuthProvider(name: string): AuthProvider;
|
|
12
|
+
export declare function resolveAuthProvider(name: string): AuthProvider;
|
|
14
13
|
/**
|
|
15
14
|
* Start a redirect login: generate `state`, `nonce` and a PKCE verifier, store
|
|
16
15
|
* them in a signed 10-minute cookie, and resolve the provider URL to redirect
|
|
@@ -21,7 +20,7 @@ declare function resolveAuthProvider(name: string): AuthProvider;
|
|
|
21
20
|
* export const googleRedirect: RequestHandler = async (_request, response) =>
|
|
22
21
|
* response.redirect(await startProviderLogin(response, "google"));
|
|
23
22
|
*/
|
|
24
|
-
declare function startProviderLogin(response: Response, providerName: string): Promise<string>;
|
|
23
|
+
export declare function startProviderLogin(response: Response, providerName: string): Promise<string>;
|
|
25
24
|
/**
|
|
26
25
|
* Finish a redirect login on the provider's callback: consume the state
|
|
27
26
|
* cookie, reject a missing/forged/expired cookie, a cookie from another
|
|
@@ -38,7 +37,6 @@ declare function startProviderLogin(response: Response, providerName: string): P
|
|
|
38
37
|
* const { user, tokens } = await completeProviderLogin(User, "google", request, response);
|
|
39
38
|
* authService.setAuthCookie(response, tokens.accessToken);
|
|
40
39
|
*/
|
|
41
|
-
declare function completeProviderLogin<T extends Auth>(Model: ChildModel<T>, providerName: string, request: Request, response: Response, deviceInfo?: DeviceInfo): Promise<LoginResult<T>>;
|
|
40
|
+
export declare function completeProviderLogin<T extends Auth>(Model: ChildModel<T>, providerName: string, request: Request, response: Response, deviceInfo?: DeviceInfo): Promise<LoginResult<T>>;
|
|
42
41
|
//#endregion
|
|
43
|
-
export { completeProviderLogin, resolveAuthProvider, startProviderLogin };
|
|
44
42
|
//# sourceMappingURL=provider-login.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-login.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/provider-login.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider-login.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/providers/provider-login.ts"],"mappings":";;;;;;;;;;;wBA+DgB,oBAAoB,eAAe;;;;;;;;;;;wBA8B7B,mBACpB,UAAU,UACV,uBACC;;;;;;;;;;;;;;;;;wBAgCmB,sBAAsB,UAAU,MACpD,OAAO,WAAW,IAClB,sBACA,SAAS,SACT,UAAU,UACV,aAAa,aACZ,QAAQ,YAAY"}
|
|
@@ -110,14 +110,15 @@ async function completeProviderLogin(Model, providerName, request, response, dev
|
|
|
110
110
|
};
|
|
111
111
|
if (query.error) throw new InvalidProviderCallbackError(`provider-error:${String(query.error)}`);
|
|
112
112
|
if (typeof query.state !== "string" || !safeEqual(query.state, expected.state)) throw new InvalidProviderCallbackError("state-mismatch");
|
|
113
|
-
const
|
|
113
|
+
const profile = await provider.handleCallback({
|
|
114
114
|
query,
|
|
115
115
|
expected: {
|
|
116
116
|
state: expected.state,
|
|
117
117
|
nonce: expected.nonce,
|
|
118
118
|
codeVerifier: expected.codeVerifier
|
|
119
119
|
}
|
|
120
|
-
})
|
|
120
|
+
});
|
|
121
|
+
const user = await resolveProviderUser(Model, profile);
|
|
121
122
|
return authService.completeLogin(user, deviceInfo);
|
|
122
123
|
}
|
|
123
124
|
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;CAGzD,MAAM,UAAU,MAAM,SAAS,eAAe;EAC5C;EACA,UAAU;GAAE,OAAO,SAAS;GAAO,OAAO,SAAS;GAAO,cAAc,SAAS;EAAa;CAChG,CAAC;CAED,MAAM,OAAO,MAAM,oBAAoB,OAAO,OAAO;CAErD,OAAO,YAAY,cAAc,MAAM,UAAU;AACnD"}
|
|
@@ -16,7 +16,8 @@ function randomUrlSafe() {
|
|
|
16
16
|
* the state cookie never shares a MAC key with the JWTs themselves.
|
|
17
17
|
*/
|
|
18
18
|
function sign(payload) {
|
|
19
|
-
|
|
19
|
+
const key = createHmac("sha256", authConfig.accessToken.secret()).update("@warlock.js/auth:provider-state").digest();
|
|
20
|
+
return createHmac("sha256", key).update(payload).digest("base64url");
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* Write the signed, 10-minute state cookie for a provider login. `HttpOnly`
|
|
@@ -78,5 +79,5 @@ function takeProviderState(request, response) {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
//#endregion
|
|
81
|
-
export { randomUrlSafe, takeProviderState, writeProviderState };
|
|
82
|
+
export { PROVIDER_STATE_COOKIE, randomUrlSafe, takeProviderState, writeProviderState };
|
|
82
83
|
//# sourceMappingURL=provider-state-cookie.mjs.map
|