@warlock.js/auth 5.31.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 +6 -0
- 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/jwt-secret-generator-command.d.mts +1 -2
- package/esm/commands/jwt-secret-generator-command.d.mts.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/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/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 -6
- 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 -4
- 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 -5
- 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 -4
- 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 -4
- 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 +7 -7
- 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/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/package.json +8 -8
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Auth } from "../auth.model.mjs";
|
|
2
2
|
import { DeviceInfo } from "../../contracts/types.mjs";
|
|
3
3
|
import { Model } from "@warlock.js/cascade";
|
|
4
|
-
|
|
5
4
|
//#region ../../packages/auth/src/models/refresh-token/refresh-token.model.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Seal schema for the persisted refresh-token record. Exported so an override
|
|
@@ -15,7 +14,7 @@ import { Model } from "@warlock.js/cascade";
|
|
|
15
14
|
* });
|
|
16
15
|
* }
|
|
17
16
|
*/
|
|
18
|
-
declare const refreshTokenSchema: import("@warlock.js/seal").ObjectValidator<{
|
|
17
|
+
export declare const refreshTokenSchema: import("@warlock.js/seal").ObjectValidator<{
|
|
19
18
|
token: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
20
19
|
user_id: import("@warlock.js/seal").ScalarValidator & import("@warlock.js/seal").StandardSchemaV1<string | number | boolean, string | number | boolean>;
|
|
21
20
|
user_type: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
@@ -26,7 +25,8 @@ declare const refreshTokenSchema: import("@warlock.js/seal").ObjectValidator<{
|
|
|
26
25
|
};
|
|
27
26
|
revoked_at: import("@warlock.js/seal").DateValidator & import("@warlock.js/seal").StandardSchemaV1<Date, Date> & {
|
|
28
27
|
isOptional: true;
|
|
29
|
-
};
|
|
28
|
+
};
|
|
29
|
+
/** The successor pair a bounded concurrent renewal may reuse. */
|
|
30
30
|
successor_access_token_id: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
31
31
|
isOptional: true;
|
|
32
32
|
};
|
|
@@ -42,7 +42,7 @@ declare const refreshTokenSchema: import("@warlock.js/seal").ObjectValidator<{
|
|
|
42
42
|
/**
|
|
43
43
|
* Extra attributes captured when a refresh token is issued.
|
|
44
44
|
*/
|
|
45
|
-
type RefreshTokenIssueOptions = {
|
|
45
|
+
export type RefreshTokenIssueOptions = {
|
|
46
46
|
familyId: string;
|
|
47
47
|
expiresAt: string;
|
|
48
48
|
deviceInfo?: DeviceInfo;
|
|
@@ -57,7 +57,7 @@ type RefreshTokenIssueOptions = {
|
|
|
57
57
|
* structurally impossible and lets an override rename or add columns by
|
|
58
58
|
* extending this class and registering it under `config.auth.refreshToken.model`.
|
|
59
59
|
*/
|
|
60
|
-
declare class RefreshToken extends Model {
|
|
60
|
+
export declare class RefreshToken extends Model {
|
|
61
61
|
static table: string;
|
|
62
62
|
static schema: import("@warlock.js/seal").ObjectValidator<{
|
|
63
63
|
token: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string>;
|
|
@@ -70,7 +70,8 @@ declare class RefreshToken extends Model {
|
|
|
70
70
|
};
|
|
71
71
|
revoked_at: import("@warlock.js/seal").DateValidator & import("@warlock.js/seal").StandardSchemaV1<Date, Date> & {
|
|
72
72
|
isOptional: true;
|
|
73
|
-
};
|
|
73
|
+
};
|
|
74
|
+
/** The successor pair a bounded concurrent renewal may reuse. */
|
|
74
75
|
successor_access_token_id: import("@warlock.js/seal").StringValidator & import("@warlock.js/seal").StandardSchemaV1<string, string> & {
|
|
75
76
|
isOptional: true;
|
|
76
77
|
};
|
|
@@ -208,5 +209,4 @@ declare class RefreshToken extends Model {
|
|
|
208
209
|
static purgeNeverExpiring(): Promise<RefreshToken[]>;
|
|
209
210
|
}
|
|
210
211
|
//#endregion
|
|
211
|
-
export { RefreshToken, RefreshTokenIssueOptions, refreshTokenSchema };
|
|
212
212
|
//# sourceMappingURL=refresh-token.model.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-token.model.d.mts","names":[],"sources":["../../../../../../../../../packages/auth/src/models/refresh-token/refresh-token.model.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"refresh-token.model.d.mts","names":[],"sources":["../../../../../../../../../packages/auth/src/models/refresh-token/refresh-token.model.ts"],"mappings":";;;;;;;;;;;;;;;;qBAkBa,+CAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAiBnB;EACV;EACA;EACA,aAAa;;;;;;;;;;;;qBAaF,qBAAqB;SAClB;SAEA,mCAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAQN;;;;;;;SAQA;;MAGH;;;;;;;;;;;MAcA;;;;;MAYA;;MAKA;;MAKA;;;;EAOE,UAAU;;;;;;;;EAWV,kBAAkB;;;;EAclB,cAAc;;;;SAOb,MAAM,MAAM,MAAM,eAAe,SAAS,2BAAwB,QAAA;;;;SAoBlE,YAAY,gBAAgB,QAAQ;;;;;SAQpC,YAAY,MAAM,MAAM,gBAAgB,QAAQ;;SAKhD,aAAa,mBAAmB,QAAQ;;;;;SAQxC,gBAAgB,MAAM,OAAO,QAAQ;;;;SAOrC,cAAc,MAAM,MAAM,gBAAa;;;;SAOvC,UAAU,MAAM,OAAO,QAAQ;;;;;;;;;SAgBzB,aAAa,MAAM,OAAO,QAAQ;;;;;SAgBlC,aAAa,mBAAmB,QAAQ;;;;;;SAexC,WAAW,MAAM,MAAM,cAAc;;;;;;SAoBrC,gBAAgB,QAAQ;;;;;;;SAgBxB,qBAAqB,QAAQ;;;;;;;;;;SAe7B,sBAAsB,QAAQ"}
|
package/esm/otp/index.d.mts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import { requestOtp, verifyOtp } from "./otp.mjs";
|
|
1
|
+
import { requestOtp, verifyOtp } from "./otp.mjs";
|
|
2
|
+
export { requestOtp, verifyOtp };
|
package/esm/otp/index.mjs
CHANGED
package/esm/otp/otp.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Auth } from "../models/auth.model.mjs";
|
|
2
2
|
import { DeviceInfo, LoginResult } from "../contracts/types.mjs";
|
|
3
3
|
import { ChildModel } from "@warlock.js/cascade";
|
|
4
|
-
|
|
5
4
|
//#region ../../packages/auth/src/otp/otp.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Send a 6-digit login code to the account whose `auth.otp.phoneField`
|
|
@@ -17,7 +16,7 @@ import { ChildModel } from "@warlock.js/cascade";
|
|
|
17
16
|
* @throws AuthProviderSdkMissingError when `@warlock.js/notifications` is not
|
|
18
17
|
* installed and no `auth.otp.send` is configured.
|
|
19
18
|
*/
|
|
20
|
-
declare function requestOtp<T extends Auth>(Model: ChildModel<T>, phone: string, options?: {
|
|
19
|
+
export declare function requestOtp<T extends Auth>(Model: ChildModel<T>, phone: string, options?: {
|
|
21
20
|
channel?: string;
|
|
22
21
|
}): Promise<void>;
|
|
23
22
|
/**
|
|
@@ -32,7 +31,6 @@ declare function requestOtp<T extends Auth>(Model: ChildModel<T>, phone: string,
|
|
|
32
31
|
* @throws InvalidOneTimeTokenError (400, EC008) — the same for an unknown
|
|
33
32
|
* phone, a wrong, expired, used or exhausted code.
|
|
34
33
|
*/
|
|
35
|
-
declare function verifyOtp<T extends Auth>(Model: ChildModel<T>, phone: string, code: string, deviceInfo?: DeviceInfo): Promise<LoginResult<T>>;
|
|
34
|
+
export declare function verifyOtp<T extends Auth>(Model: ChildModel<T>, phone: string, code: string, deviceInfo?: DeviceInfo): Promise<LoginResult<T>>;
|
|
36
35
|
//#endregion
|
|
37
|
-
export { requestOtp, verifyOtp };
|
|
38
36
|
//# sourceMappingURL=otp.d.mts.map
|
package/esm/otp/otp.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otp.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/otp/otp.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"otp.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/otp/otp.ts"],"mappings":";;;;;;;;;;;;;;;;;;wBAuFsB,WAAW,UAAU,MACzC,OAAO,WAAW,IAClB,eACA;EAAW;IACV;;;;;;;;;;;;;wBAkCmB,UAAU,UAAU,MACxC,OAAO,WAAW,IAClB,eACA,cACA,aAAa,aACZ,QAAQ,YAAY"}
|
package/esm/otp/otp.mjs
CHANGED
|
@@ -19,7 +19,8 @@ function invalidCode() {
|
|
|
19
19
|
* value unique.
|
|
20
20
|
*/
|
|
21
21
|
function macCode(user, salt, code) {
|
|
22
|
-
|
|
22
|
+
const key = createHmac("sha256", authConfig.accessToken.secret()).update("@warlock.js/auth:otp").digest();
|
|
23
|
+
return createHmac("sha256", key).update(`${salt}:${user.userType}:${user.id}:${code}`).digest("base64url");
|
|
23
24
|
}
|
|
24
25
|
/** Stored form: `<salt>.<mac>` — 16 + 1 + 43 chars, inside the 64-char `token_hash` column. */
|
|
25
26
|
function storedCodeHash(user, code) {
|
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"}
|