@schemavaults/auth-common 0.22.5 → 0.22.11

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.
@@ -17,4 +17,11 @@ export interface AudienceSchemaOverrides {
17
17
  auth_server_app_id?: string;
18
18
  }
19
19
  export declare function createAudienceSchema(z: typeof zod, environment?: SchemaVaultsAppEnvironment, overrides?: AudienceSchemaOverrides): zod.ZodUnion<[zod.ZodEffects<zod.ZodString, string, string>, zod.ZodEffects<zod.ZodEffects<zod.ZodEffects<zod.ZodString, string, string>, string, string>, string, string>]>;
20
+ /**
21
+ * A list of token audiences. The list may be empty: at the token endpoints
22
+ * an empty audience list means the grant mints no access tokens —
23
+ * authenticate (or rotate the refresh token) only, which is how clients
24
+ * configured with no default token audiences complete a login. Call sites
25
+ * that require at least one audience must enforce non-emptiness themselves.
26
+ */
20
27
  export declare function createAudienceListSchema(z: typeof zod, environment?: SchemaVaultsAppEnvironment, overrides?: AudienceSchemaOverrides): zod.ZodArray<zod.ZodUnion<[zod.ZodEffects<zod.ZodString, string, string>, zod.ZodEffects<zod.ZodEffects<zod.ZodEffects<zod.ZodString, string, string>, string, string>, string, string>]>, "many">;
@@ -15,10 +15,16 @@ export function createAudienceSchema(z, environment = getAppEnvironment(), overr
15
15
  return z.union([authServerUrlSchema, apiServerIdWithoutAuthServerIdSchema]);
16
16
  }
17
17
  const MAX_APPS_IN_AUDIENCE_LIST = 10;
18
+ /**
19
+ * A list of token audiences. The list may be empty: at the token endpoints
20
+ * an empty audience list means the grant mints no access tokens —
21
+ * authenticate (or rotate the refresh token) only, which is how clients
22
+ * configured with no default token audiences complete a login. Call sites
23
+ * that require at least one audience must enforce non-emptiness themselves.
24
+ */
18
25
  export function createAudienceListSchema(z, environment = getAppEnvironment(), overrides) {
19
26
  return createAudienceSchema(z, environment, overrides)
20
27
  .array()
21
- .min(1, "Audience list may not be empty")
22
28
  .max(MAX_APPS_IN_AUDIENCE_LIST, `Audience list may not contain more than ${MAX_APPS_IN_AUDIENCE_LIST} audience references.`);
23
29
  }
24
30
  //# sourceMappingURL=audience-schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audience-schema.js","sourceRoot":"","sources":["../src/audience-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,+BAA+B,CAAC;AA0BvC,MAAM,UAAU,oBAAoB,CAClC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,MAAM,eAAe,GACnB,SAAS,EAAE,eAAe,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GACtB,SAAS,EAAE,kBAAkB,IAAI,kBAAkB,EAAE,CAAC;IAExD,MAAM,mBAAmB,GAAG,CAAC;SAC1B,MAAM,EAAE;SACR,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,GAAG,EAAiC,EAAE,CAAC,GAAG,KAAK,eAAe,CAAC;SACvE,QAAQ,CAAC,8DAA8D,CAAC,CAAC;IAE5E,MAAM,oCAAoC,GAAG,iBAAiB;SAC3D,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QACb,OAAO,EAAE,KAAK,kBAAkB,CAAC;IACnC,CAAC,EAAE,iBAAiB,kBAAkB,qGAAqG,CAAC;SAC3I,QAAQ,CACP,4CAA4C,kBAAkB,iBAAiB,CAChF,CAAC;IAEJ,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,mBAAmB,EAAE,oCAAoC,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,yBAAyB,GAAG,EAA4B,CAAC;AAE/D,MAAM,UAAU,wBAAwB,CACtC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,oBAAoB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SACnD,KAAK,EAAE;SACP,GAAG,CAAC,CAAC,EAAE,gCAAgC,CAAC;SACxC,GAAG,CACF,yBAAyB,EACzB,2CAA2C,yBAAyB,uBAAuB,CAC5F,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"audience-schema.js","sourceRoot":"","sources":["../src/audience-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,+BAA+B,CAAC;AA0BvC,MAAM,UAAU,oBAAoB,CAClC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,MAAM,eAAe,GACnB,SAAS,EAAE,eAAe,IAAI,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GACtB,SAAS,EAAE,kBAAkB,IAAI,kBAAkB,EAAE,CAAC;IAExD,MAAM,mBAAmB,GAAG,CAAC;SAC1B,MAAM,EAAE;SACR,GAAG,EAAE;SACL,MAAM,CAAC,CAAC,GAAG,EAAiC,EAAE,CAAC,GAAG,KAAK,eAAe,CAAC;SACvE,QAAQ,CAAC,8DAA8D,CAAC,CAAC;IAE5E,MAAM,oCAAoC,GAAG,iBAAiB;SAC3D,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QACb,OAAO,EAAE,KAAK,kBAAkB,CAAC;IACnC,CAAC,EAAE,iBAAiB,kBAAkB,qGAAqG,CAAC;SAC3I,QAAQ,CACP,4CAA4C,kBAAkB,iBAAiB,CAChF,CAAC;IAEJ,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,mBAAmB,EAAE,oCAAoC,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,yBAAyB,GAAG,EAA4B,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,oBAAoB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SACnD,KAAK,EAAE;SACP,GAAG,CACF,yBAAyB,EACzB,2CAA2C,yBAAyB,uBAAuB,CAC5F,CAAC;AACN,CAAC"}
@@ -7,6 +7,10 @@ export const grant_types = [
7
7
  ];
8
8
  function createTokenEndpointBaseSchema(z, environment = getAppEnvironment(), overrides) {
9
9
  return z.object({
10
+ // A single audience, or a (possibly empty) audience list. An empty list
11
+ // mints no access tokens — the grant authenticates (or rotates the
12
+ // refresh token) only, which is how clients configured with no default
13
+ // token audiences complete a login.
10
14
  audience: z.union([
11
15
  createAudienceSchema(z, environment, overrides),
12
16
  createAudienceListSchema(z, environment, overrides),
@@ -44,6 +48,10 @@ export function createRefreshTokenPOSTBodySchema(z, environment = getAppEnvironm
44
48
  return createTokenEndpointBaseSchema(z, environment, overrides)
45
49
  .extend({
46
50
  grant_type: z.literal("refresh_token"),
51
+ // Deprecated and ignored: the refresh grant now always rotates the
52
+ // refresh token. Still tolerated (the body schema is strict) so
53
+ // requests from older SDK versions — which unconditionally send the
54
+ // flag — keep parsing.
47
55
  replaceRefreshToo: z.boolean().optional(),
48
56
  })
49
57
  .required({
@@ -1 +1 @@
1
- {"version":3,"file":"auth_acquire_tokens_grant_types.js","sourceRoot":"","sources":["../src/auth_acquire_tokens_grant_types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAEL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,iBAAiB,GAElB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,oBAAoB;IACpB,eAAe;CACqB,CAAC;AAEvC,SAAS,6BAA6B,CACpC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC;YAChB,oBAAoB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;YAC/C,wBAAwB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SACpD,CAAC;QACF,aAAa,EAAE,WAAW;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,6BAA6B,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SAC5D,MAAM,CAAC;QACN,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE;QAC9C,aAAa,EAAE,oBAAoB,CAAC,mBAAmB;QACvD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;QACxC,8DAA8D;QAC9D,mEAAmE;QACnE,iEAAiE;QACjE,6DAA6D;QAC7D,+DAA+D;QAC/D,kEAAkE;QAClE,qCAAqC;QACrC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;KACrD,CAAC;SACD,QAAQ,CAAC;QACR,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,IAAI;QACnB,IAAI,EAAE,IAAI;QACV,aAAa,EAAE,IAAI;QACnB,cAAc,EAAE,IAAI;KACrB,CAAC;SACD,MAAM,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,6BAA6B,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SAC5D,MAAM,CAAC;QACN,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACtC,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KAC1C,CAAC;SACD,QAAQ,CAAC;QACR,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,IAAI;KACpB,CAAC;SACD,MAAM,EAAE,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"auth_acquire_tokens_grant_types.js","sourceRoot":"","sources":["../src/auth_acquire_tokens_grant_types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAEL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,iBAAiB,GAElB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,oBAAoB;IACpB,eAAe;CACqB,CAAC;AAEvC,SAAS,6BAA6B,CACpC,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,oCAAoC;QACpC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC;YAChB,oBAAoB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;YAC/C,wBAAwB,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SACpD,CAAC;QACF,aAAa,EAAE,WAAW;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,6BAA6B,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SAC5D,MAAM,CAAC;QACN,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE;QAC9C,aAAa,EAAE,oBAAoB,CAAC,mBAAmB;QACvD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;QACxC,8DAA8D;QAC9D,mEAAmE;QACnE,iEAAiE;QACjE,6DAA6D;QAC7D,+DAA+D;QAC/D,kEAAkE;QAClE,qCAAqC;QACrC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;KACrD,CAAC;SACD,QAAQ,CAAC;QACR,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,IAAI;QACnB,IAAI,EAAE,IAAI;QACV,aAAa,EAAE,IAAI;QACnB,cAAc,EAAE,IAAI;KACrB,CAAC;SACD,MAAM,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC9C,CAAa,EACb,cAA0C,iBAAiB,EAAE,EAC7D,SAAmC;IAEnC,OAAO,6BAA6B,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,CAAC;SAC5D,MAAM,CAAC;QACN,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACtC,mEAAmE;QACnE,gEAAgE;QAChE,oEAAoE;QACpE,uBAAuB;QACvB,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KAC1C,CAAC;SACD,QAAQ,CAAC;QACR,UAAU,EAAE,IAAI;QAChB,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,IAAI;KACpB,CAAC;SACD,MAAM,EAAE,CAAC;AACd,CAAC"}
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { passwordSchema } from './password_requirements';
3
+ import { normalizedEmailSchema } from './normalize-email';
3
4
  export const emailCredentialsSchema = z.object({
4
- email: z.string().email(),
5
+ email: normalizedEmailSchema,
5
6
  password: passwordSchema
6
7
  }).required({
7
8
  email: true,
@@ -1 +1 @@
1
- {"version":3,"file":"email_credentials.js","sourceRoot":"","sources":["../../src/credentials/email_credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IACzB,QAAQ,EAAE,cAAc;CACzB,CAAC,CAAC,QAAQ,CAAC;IACV,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,IAAI;CACf,CAAC,CAAC,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"email_credentials.js","sourceRoot":"","sources":["../../src/credentials/email_credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,KAAK,EAAE,qBAAqB;IAC5B,QAAQ,EAAE,cAAc;CACzB,CAAC,CAAC,QAAQ,CAAC;IACV,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,IAAI;CACf,CAAC,CAAC,MAAM,EAAE,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export { passwordSchema } from './password_requirements';
2
+ export { normalizeEmail, normalizedEmailSchema } from './normalize-email';
2
3
  export { emailCredentialsSchema, type EmailCredentials } from './email_credentials';
3
4
  export { emailRegistrationCredentialsSchema, type EmailRegistrationCredentials } from './register_credentials';
@@ -1,4 +1,5 @@
1
1
  export { passwordSchema } from './password_requirements';
2
+ export { normalizeEmail, normalizedEmailSchema } from './normalize-email';
2
3
  export { emailCredentialsSchema } from './email_credentials';
3
4
  export { emailRegistrationCredentialsSchema } from './register_credentials';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAyB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAqC,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAyB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,kCAAkC,EAAqC,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Canonical form of an email address for storage and comparison: trimmed
4
+ * and lowercased. RFC 5321 technically allows a case-sensitive local part,
5
+ * but mainstream mail providers treat addresses case-insensitively, and
6
+ * treating case-variants as distinct accounts would let one mailbox
7
+ * register multiple times (e.g. `user@example.com` vs `User@example.com`).
8
+ */
9
+ export declare function normalizeEmail(email: string): string;
10
+ /**
11
+ * Email schema that normalizes (trims + lowercases) at parse time, so any
12
+ * payload validated with it carries the canonical form of the address.
13
+ */
14
+ export declare const normalizedEmailSchema: z.ZodString;
15
+ export default normalizeEmail;
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Canonical form of an email address for storage and comparison: trimmed
4
+ * and lowercased. RFC 5321 technically allows a case-sensitive local part,
5
+ * but mainstream mail providers treat addresses case-insensitively, and
6
+ * treating case-variants as distinct accounts would let one mailbox
7
+ * register multiple times (e.g. `user@example.com` vs `User@example.com`).
8
+ */
9
+ export function normalizeEmail(email) {
10
+ return email.trim().toLowerCase();
11
+ }
12
+ /**
13
+ * Email schema that normalizes (trims + lowercases) at parse time, so any
14
+ * payload validated with it carries the canonical form of the address.
15
+ */
16
+ export const normalizedEmailSchema = z.string().trim().toLowerCase().email();
17
+ export default normalizeEmail;
18
+ //# sourceMappingURL=normalize-email.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-email.js","sourceRoot":"","sources":["../../src/credentials/normalize-email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;AAE7E,eAAe,cAAc,CAAC"}
package/dist/index.d.ts CHANGED
@@ -35,6 +35,7 @@ export { authorizeClientApplicationFormType } from "./authorize-client-applicati
35
35
  export { paginationOptionsSchema, DEFAULT_PAGINATION_PAGE_INDEX, DEFAULT_PAGINATION_PAGE_SIZE, isValidPaginationOptions, } from "./pagination";
36
36
  export type { PaginationOptions } from "./pagination";
37
37
  export { timingSafeStringEqual } from "./timing-safe-string-equal";
38
+ export { sanitizeNextHref, MAX_NEXT_HREF_LENGTH, } from "./sanitize-next-href";
38
39
  export { isOriginInAllowedList } from "./cors";
39
40
  export { oauth2StateSchema, OAUTH2_STATE_VSCHAR_REGEX, parseOAuth2State, OAuth2StateValidationError, } from "./oauth2-state-schema";
40
41
  export type { OAuth2State } from "./oauth2-state-schema";
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export { determineRefreshTokenCookieSameSiteValue } from "./determineRefreshToke
24
24
  export { authorizeClientApplicationFormType } from "./authorize-client-application-form-type";
25
25
  export { paginationOptionsSchema, DEFAULT_PAGINATION_PAGE_INDEX, DEFAULT_PAGINATION_PAGE_SIZE, isValidPaginationOptions, } from "./pagination";
26
26
  export { timingSafeStringEqual } from "./timing-safe-string-equal";
27
+ export { sanitizeNextHref, MAX_NEXT_HREF_LENGTH, } from "./sanitize-next-href";
27
28
  export { isOriginInAllowedList } from "./cors";
28
29
  export { oauth2StateSchema, OAUTH2_STATE_VSCHAR_REGEX, parseOAuth2State, OAuth2StateValidationError, } from "./oauth2-state-schema";
29
30
  export { createAuthorizationCodePOSTBodySchema, createRefreshTokenPOSTBodySchema, } from "./auth_acquire_tokens_grant_types";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,cAAc,GACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,mCAAmC,EACnC,qCAAqC,GACtC,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,cAAc,EAAiB,MAAM,aAAa,CAAC;AAE5D,cAAc,eAAe,CAAC;AAG9B,mBAAmB;AACnB,cAAc,cAAc,CAAC;AAM7B,cAAc,cAAc,CAAC;AAG7B,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EACL,wBAAwB,EACxB,qCAAqC,EACrC,mCAAmC,EACnC,+BAA+B,EAC/B,wCAAwC,EACxC,wBAAwB,EACxB,uBAAuB,GAOxB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,OAAO,CAAC;AACtB,OAAO,EACL,+BAA+B,EAE/B,6CAA6C,GAE9C,MAAM,yBAAyB,CAAC;AAEjC,cAAc,mCAAmC,CAAC;AAGlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,OAAO,EACL,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,4BAA4B,EAC5B,gBAAgB,EAChB,gCAAgC,EAChC,yCAAyC,EACzC,kCAAkC,EAClC,2CAA2C,EAC3C,0BAA0B,EAC1B,8BAA8B,EAC9B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,iCAAiC,EACjC,kCAAkC,EAClC,4BAA4B,EAC5B,+BAA+B,EAC/B,oCAAoC,EACpC,qCAAqC,EACrC,uCAAuC,EACvC,wCAAwC,GACzC,MAAM,iBAAiB,CAAC;AAezB,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,wCAAwC,EAAE,MAAM,4CAA4C,CAAC;AAEtG,OAAO,EAAE,kCAAkC,EAAE,MAAM,0CAA0C,CAAC;AAE9F,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,qCAAqC,EACrC,gCAAgC,GACjC,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,cAAc,GACf,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,mCAAmC,EACnC,qCAAqC,GACtC,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,cAAc,EAAiB,MAAM,aAAa,CAAC;AAE5D,cAAc,eAAe,CAAC;AAG9B,mBAAmB;AACnB,cAAc,cAAc,CAAC;AAM7B,cAAc,cAAc,CAAC;AAG7B,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EACL,wBAAwB,EACxB,qCAAqC,EACrC,mCAAmC,EACnC,+BAA+B,EAC/B,wCAAwC,EACxC,wBAAwB,EACxB,uBAAuB,GAOxB,MAAM,uBAAuB,CAAC;AAE/B,cAAc,OAAO,CAAC;AACtB,OAAO,EACL,+BAA+B,EAE/B,6CAA6C,GAE9C,MAAM,yBAAyB,CAAC;AAEjC,cAAc,mCAAmC,CAAC;AAGlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAE5D,OAAO,EACL,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,4BAA4B,EAC5B,gBAAgB,EAChB,gCAAgC,EAChC,yCAAyC,EACzC,kCAAkC,EAClC,2CAA2C,EAC3C,0BAA0B,EAC1B,8BAA8B,EAC9B,8BAA8B,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,iCAAiC,EACjC,kCAAkC,EAClC,4BAA4B,EAC5B,+BAA+B,EAC/B,oCAAoC,EACpC,qCAAqC,EACrC,uCAAuC,EACvC,wCAAwC,GACzC,MAAM,iBAAiB,CAAC;AAezB,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE,OAAO,EACL,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,wCAAwC,EAAE,MAAM,4CAA4C,CAAC;AAEtG,OAAO,EAAE,kCAAkC,EAAE,MAAM,0CAA0C,CAAC;AAE9F,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,qCAAqC,EACrC,gCAAgC,GACjC,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Maximum accepted `next_href` length. Generous enough for deep paths
3
+ * with query strings while keeping the value log- and cookie-safe.
4
+ */
5
+ export declare const MAX_NEXT_HREF_LENGTH: 2048;
6
+ /**
7
+ * @name sanitizeNextHref
8
+ * @description Validate a post-login redirect target from an untrusted
9
+ * source (query param, form field). Returns the normalized same-origin
10
+ * path (`pathname + search + hash`) when the value is a safe internal
11
+ * absolute-path reference, or `null` when it is absent or unsafe.
12
+ * Callers should treat `null` as "use the default destination".
13
+ */
14
+ export declare function sanitizeNextHref(value: unknown): string | null;
15
+ export default sanitizeNextHref;
@@ -0,0 +1,71 @@
1
+ // The `next_href` parameter carries the URL a user was trying to reach
2
+ // when an auth route guard bounced them to the login page, so that the
3
+ // post-login redirect can return them to it. Because the value round-trips
4
+ // through a user-controlled query string, it is an open-redirect vector
5
+ // unless it is strictly confined to a same-origin path.
6
+ //
7
+ // This sanitizer accepts only an absolute-path reference (RFC 3986
8
+ // §4.2: begins with "/" but not "//") and returns the normalized
9
+ // `pathname + search + hash`. Everything else — absolute URLs,
10
+ // protocol-relative URLs ("//evil.example"), backslash variants
11
+ // ("/\evil.example", which browsers treat as "//"), control characters,
12
+ // or values a URL parser resolves off-origin — is rejected with `null`
13
+ // so callers can fall back to their default destination.
14
+ /**
15
+ * Maximum accepted `next_href` length. Generous enough for deep paths
16
+ * with query strings while keeping the value log- and cookie-safe.
17
+ */
18
+ export const MAX_NEXT_HREF_LENGTH = 2048;
19
+ // Sentinel base origin used to resolve the candidate path. If the
20
+ // parsed result escapes this origin, the value was not a plain path.
21
+ const SANITIZE_BASE_ORIGIN = "https://next-href.invalid";
22
+ // eslint-disable-next-line no-control-regex
23
+ const CONTROL_CHARS_REGEX = /[\u0000-\u001f\u007f]/;
24
+ /**
25
+ * @name sanitizeNextHref
26
+ * @description Validate a post-login redirect target from an untrusted
27
+ * source (query param, form field). Returns the normalized same-origin
28
+ * path (`pathname + search + hash`) when the value is a safe internal
29
+ * absolute-path reference, or `null` when it is absent or unsafe.
30
+ * Callers should treat `null` as "use the default destination".
31
+ */
32
+ export function sanitizeNextHref(value) {
33
+ if (typeof value !== "string") {
34
+ return null;
35
+ }
36
+ if (value.length === 0 || value.length > MAX_NEXT_HREF_LENGTH) {
37
+ return null;
38
+ }
39
+ // Must be an absolute-path reference: "/..." but never "//..." (a
40
+ // protocol-relative URL) nor "/\..." (browser-normalized to "//").
41
+ if (!value.startsWith("/")) {
42
+ return null;
43
+ }
44
+ if (value.startsWith("//") || value.startsWith("/\\")) {
45
+ return null;
46
+ }
47
+ if (CONTROL_CHARS_REGEX.test(value)) {
48
+ return null;
49
+ }
50
+ let parsed;
51
+ try {
52
+ parsed = new URL(value, SANITIZE_BASE_ORIGIN);
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ // A plain path resolved against the sentinel base must stay on the
58
+ // sentinel origin; anything else smuggled in a scheme or authority.
59
+ if (parsed.origin !== SANITIZE_BASE_ORIGIN) {
60
+ return null;
61
+ }
62
+ // Defense-in-depth: reject if normalization still yields a
63
+ // protocol-relative shape (e.g. via backslash tricks the guards
64
+ // above didn't anticipate).
65
+ if (parsed.pathname.startsWith("//")) {
66
+ return null;
67
+ }
68
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
69
+ }
70
+ export default sanitizeNextHref;
71
+ //# sourceMappingURL=sanitize-next-href.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-next-href.js","sourceRoot":"","sources":["../src/sanitize-next-href.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,2EAA2E;AAC3E,wEAAwE;AACxE,wDAAwD;AACxD,EAAE;AACF,mEAAmE;AACnE,iEAAiE;AACjE,+DAA+D;AAC/D,gEAAgE;AAChE,wEAAwE;AACxE,uEAAuE;AACvE,yDAAyD;AAEzD;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAa,CAAC;AAElD,kEAAkE;AAClE,qEAAqE;AACrE,MAAM,oBAAoB,GAAG,2BAAoC,CAAC;AAElE,4CAA4C;AAC5C,MAAM,mBAAmB,GAAW,uBAAuB,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,kEAAkE;IAClE,mEAAmE;IACnE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mEAAmE;IACnE,oEAAoE;IACpE,IAAI,MAAM,CAAC,MAAM,KAAK,oBAAoB,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,2DAA2D;IAC3D,gEAAgE;IAChE,4BAA4B;IAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,eAAe,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schemavaults/auth-common",
3
3
  "description": "Types and utility functions for authentication and authorization",
4
- "version": "0.22.5",
4
+ "version": "0.22.11",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
7
  "repository": {