@growth-labs/auth 0.9.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +22 -9
  2. package/dist/config-store.d.ts.map +1 -1
  3. package/dist/config-store.js +17 -2
  4. package/dist/config-store.js.map +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +8 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/middleware/auth.d.ts.map +1 -1
  9. package/dist/middleware/auth.js +24 -4
  10. package/dist/middleware/auth.js.map +1 -1
  11. package/dist/options.d.ts +7 -6
  12. package/dist/options.d.ts.map +1 -1
  13. package/dist/options.js +16 -2
  14. package/dist/options.js.map +1 -1
  15. package/dist/routes/api/code-request.d.ts.map +1 -1
  16. package/dist/routes/api/code-request.js +5 -1
  17. package/dist/routes/api/code-request.js.map +1 -1
  18. package/dist/routes/api/code-verify.d.ts.map +1 -1
  19. package/dist/routes/api/code-verify.js +5 -1
  20. package/dist/routes/api/code-verify.js.map +1 -1
  21. package/dist/routes/api/password-reset-confirm.d.ts.map +1 -1
  22. package/dist/routes/api/password-reset-confirm.js +5 -1
  23. package/dist/routes/api/password-reset-confirm.js.map +1 -1
  24. package/dist/routes/api/password-reset-request.d.ts.map +1 -1
  25. package/dist/routes/api/password-reset-request.js +5 -1
  26. package/dist/routes/api/password-reset-request.js.map +1 -1
  27. package/dist/routes/api/password-verify.d.ts.map +1 -1
  28. package/dist/routes/api/password-verify.js +5 -1
  29. package/dist/routes/api/password-verify.js.map +1 -1
  30. package/dist/routes/api/shared.d.ts +1 -0
  31. package/dist/routes/api/shared.d.ts.map +1 -1
  32. package/dist/routes/api/shared.js +26 -2
  33. package/dist/routes/api/shared.js.map +1 -1
  34. package/dist/routes/callback.d.ts.map +1 -1
  35. package/dist/routes/callback.js +12 -3
  36. package/dist/routes/callback.js.map +1 -1
  37. package/dist/routes/logout.d.ts.map +1 -1
  38. package/dist/routes/logout.js +35 -6
  39. package/dist/routes/logout.js.map +1 -1
  40. package/dist/server.d.ts +2 -0
  41. package/dist/server.d.ts.map +1 -1
  42. package/dist/server.js +16 -5
  43. package/dist/server.js.map +1 -1
  44. package/dist/utils/client-secret-literal.d.ts +13 -0
  45. package/dist/utils/client-secret-literal.d.ts.map +1 -0
  46. package/dist/utils/client-secret-literal.js +50 -0
  47. package/dist/utils/client-secret-literal.js.map +1 -0
  48. package/dist/utils/client-secret.d.ts +12 -1
  49. package/dist/utils/client-secret.d.ts.map +1 -1
  50. package/dist/utils/client-secret.js +108 -14
  51. package/dist/utils/client-secret.js.map +1 -1
  52. package/dist/utils/fail-closed.d.ts +14 -0
  53. package/dist/utils/fail-closed.d.ts.map +1 -0
  54. package/dist/utils/fail-closed.js +53 -0
  55. package/dist/utils/fail-closed.js.map +1 -0
  56. package/dist/utils/index.d.ts +4 -1
  57. package/dist/utils/index.d.ts.map +1 -1
  58. package/dist/utils/index.js +3 -1
  59. package/dist/utils/index.js.map +1 -1
  60. package/dist/utils/openauth.d.ts +7 -1
  61. package/dist/utils/openauth.d.ts.map +1 -1
  62. package/dist/utils/openauth.js +29 -12
  63. package/dist/utils/openauth.js.map +1 -1
  64. package/dist/utils/tokens.d.ts +1 -0
  65. package/dist/utils/tokens.d.ts.map +1 -1
  66. package/dist/utils/tokens.js +52 -24
  67. package/dist/utils/tokens.js.map +1 -1
  68. package/package.json +2 -2
  69. package/src/config-store.ts +18 -2
  70. package/src/index.ts +11 -1
  71. package/src/middleware/auth.ts +31 -4
  72. package/src/options.ts +17 -2
  73. package/src/routes/api/code-request.ts +5 -0
  74. package/src/routes/api/code-verify.ts +5 -0
  75. package/src/routes/api/password-reset-confirm.ts +5 -0
  76. package/src/routes/api/password-reset-request.ts +5 -0
  77. package/src/routes/api/password-verify.ts +5 -0
  78. package/src/routes/api/shared.ts +33 -2
  79. package/src/routes/callback.ts +12 -2
  80. package/src/routes/logout.ts +35 -6
  81. package/src/server.ts +23 -6
  82. package/src/utils/client-secret-literal.ts +71 -0
  83. package/src/utils/client-secret.ts +132 -18
  84. package/src/utils/fail-closed.ts +74 -0
  85. package/src/utils/index.ts +14 -1
  86. package/src/utils/openauth.ts +46 -13
  87. package/src/utils/tokens.ts +67 -28
@@ -0,0 +1,53 @@
1
+ // Shared no-store headers for all secret-resolution failure responses.
2
+ const SECURITY_HEADERS = {
3
+ 'Cache-Control': 'private, no-store',
4
+ Pragma: 'no-cache',
5
+ Expires: '0',
6
+ };
7
+ function acceptsJson(request) {
8
+ const accept = request?.headers.get('accept') ?? '';
9
+ return accept
10
+ .split(',')
11
+ .some((value) => value.trim().toLowerCase().split(';')[0] === 'application/json');
12
+ }
13
+ // Fixed-vocabulary 503 for secret-resolution failures on all surfaces.
14
+ // Browser callers receive bounded plain text; JSON callers receive one fixed
15
+ // JSON error. Neither class reflects a path, provider, error code, raw error,
16
+ // secret value, or stack. Callers must invoke this before issuer I/O.
17
+ export function reportClientSecretResolutionFailure(signal) {
18
+ try {
19
+ console.error('[growth-labs/auth] client_secret_resolution_failed', {
20
+ surface: signal.surface,
21
+ code: signal.code,
22
+ });
23
+ }
24
+ catch {
25
+ // Observability must never replace the bounded fail-closed response.
26
+ }
27
+ }
28
+ export function secretResolutionFailureResponse(request, signal) {
29
+ if (signal)
30
+ reportClientSecretResolutionFailure(signal);
31
+ if (acceptsJson(request)) {
32
+ return Response.json({ error: 'auth_service_unavailable' }, { status: 503, headers: new Headers(SECURITY_HEADERS) });
33
+ }
34
+ return new Response('Service unavailable', {
35
+ status: 503,
36
+ headers: new Headers({
37
+ ...SECURITY_HEADERS,
38
+ 'Content-Type': 'text/plain; charset=UTF-8',
39
+ }),
40
+ });
41
+ }
42
+ // Typed error thrown by server-helper headersFor() on secret binding failure.
43
+ // Exported so consumers can distinguish secret-resolution failure from
44
+ // network/credential failure in custom server-side auth flows.
45
+ export class ClientSecretResolutionError extends Error {
46
+ code;
47
+ name = 'ClientSecretResolutionError';
48
+ constructor(code) {
49
+ super('client_secret_binding_unavailable');
50
+ this.code = code;
51
+ }
52
+ }
53
+ //# sourceMappingURL=fail-closed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fail-closed.js","sourceRoot":"","sources":["../../src/utils/fail-closed.ts"],"names":[],"mappings":"AAeA,uEAAuE;AACvE,MAAM,gBAAgB,GAA2B;IAChD,eAAe,EAAE,mBAAmB;IACpC,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,GAAG;CACZ,CAAA;AAED,SAAS,WAAW,CAAC,OAA4B;IAChD,MAAM,MAAM,GAAG,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IACnD,OAAO,MAAM;SACX,KAAK,CAAC,GAAG,CAAC;SACV,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAA;AACnF,CAAC;AAED,uEAAuE;AACvE,6EAA6E;AAC7E,8EAA8E;AAC9E,sEAAsE;AACtE,MAAM,UAAU,mCAAmC,CAAC,MAAiC;IACpF,IAAI,CAAC;QACJ,OAAO,CAAC,KAAK,CAAC,oDAAoD,EAAE;YACnE,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;SACjB,CAAC,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACR,qEAAqE;IACtE,CAAC;AACF,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC9C,OAAiB,EACjB,MAAkC;IAElC,IAAI,MAAM;QAAE,mCAAmC,CAAC,MAAM,CAAC,CAAA;IACvD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,OAAO,QAAQ,CAAC,IAAI,CACnB,EAAE,KAAK,EAAE,0BAA0B,EAAE,EACrC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,gBAA+B,CAAC,EAAE,CACtE,CAAA;IACF,CAAC;IAED,OAAO,IAAI,QAAQ,CAAC,qBAAqB,EAAE;QAC1C,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,IAAI,OAAO,CAAC;YACpB,GAAG,gBAAgB;YACnB,cAAc,EAAE,2BAA2B;SAC3C,CAAC;KACF,CAAC,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,uEAAuE;AACvE,+DAA+D;AAC/D,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAEzB;IADV,IAAI,GAAG,6BAA6B,CAAA;IACtD,YAA4B,IAAqC;QAChE,KAAK,CAAC,mCAAmC,CAAC,CAAA;QADf,SAAI,GAAJ,IAAI,CAAiC;IAEjE,CAAC;CACD"}
@@ -1,7 +1,10 @@
1
1
  export { type AuthAnalyticsProvider, type AuthFailureEventInput, type AuthSuccessEventInput, authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
+ export type { ClientSecretLiteral, ClientSecretResolutionErrorCode, ClientSecretResult, } from './client-secret.js';
3
+ export { isClientSecretLiteral, literalClientSecret } from './client-secret.js';
4
+ export { ClientSecretResolutionError } from './fail-closed.js';
2
5
  export { type BridgeMigrationOptions, type BridgeMigrationStatement, buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
6
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
- export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
7
+ export { EXCHANGE_SECRET_FAILURE, type ExchangeSecretFailure, getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl, } from './openauth.js';
5
8
  export { createPKCEChallenge, generatePKCE } from './pkce.js';
6
9
  export { generateState, verifyState } from './state.js';
7
10
  export type { VerifiedClaims } from './tokens.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACX,mBAAmB,EACnB,+BAA+B,EAC/B,kBAAkB,GAClB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EACN,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA"}
@@ -1,7 +1,9 @@
1
1
  export { authSuccessEventName, emitAuthEvent, emitAuthFailureEvent, emitAuthSuccessEvent, normalizeAuthFailureReason, normalizeAuthProvider, trackAuthEvent, } from './analytics.js';
2
+ export { isClientSecretLiteral, literalClientSecret } from './client-secret.js';
3
+ export { ClientSecretResolutionError } from './fail-closed.js';
2
4
  export { buildLegacyIdentitySubjectCanary, buildLegacyIdentitySubjectMigration, canonicalIdentitySubject, extractIdentityId, findLegacyIdentitySubjects, IDENTITY_SUB_FORMAT_DRIFT, IDENTITY_SUBJECT_PREFIX, IdentitySubFormatDriftError, normalizeIdentitySubject, rewriteLegacyIdentitySubjects, } from './identity.js';
3
5
  export { signIdentityCookie, verifyIdentityCookie } from './identity-cookie.js';
4
- export { getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl } from './openauth.js';
6
+ export { EXCHANGE_SECRET_FAILURE, getAuthorizationUrl, getMagicLinkUrl, getPasswordLoginUrl, } from './openauth.js';
5
7
  export { createPKCEChallenge, generatePKCE } from './pkce.js';
6
8
  export { generateState, verifyState } from './state.js';
7
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAIN,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,cAAc,GACd,MAAM,gBAAgB,CAAA;AAMvB,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAGN,gCAAgC,EAChC,mCAAmC,EACnC,wBAAwB,EACxB,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,GAC7B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EACN,uBAAuB,EAEvB,mBAAmB,EACnB,eAAe,EACf,mBAAmB,GACnB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import type { TokenExchangeResult } from '../types.js';
2
+ import { type ClientSecretResolutionErrorCode } from './client-secret.js';
2
3
  export interface AuthFlow {
3
4
  verifier: string;
4
5
  challenge: string;
@@ -24,6 +25,11 @@ export declare function getMagicLinkUrl(_context: {
24
25
  }): string;
25
26
  export declare function getForgotPasswordUrl(): string;
26
27
  export declare function getResetPasswordUrl(): string;
27
- export declare function exchangeCode(callbackUrl: URL, code: string, codeVerifier: string, providerFromCookie?: TokenExchangeResult['provider']): Promise<TokenExchangeResult | null>;
28
+ export interface ExchangeSecretFailure {
29
+ readonly secretFailed: true;
30
+ readonly code: ClientSecretResolutionErrorCode;
31
+ }
32
+ export declare const EXCHANGE_SECRET_FAILURE: ExchangeSecretFailure;
33
+ export declare function exchangeCode(callbackUrl: URL, code: string, codeVerifier: string, providerFromCookie?: TokenExchangeResult['provider'], request?: Request): Promise<TokenExchangeResult | ExchangeSecretFailure | null>;
28
34
  export declare function revokeToken(refreshToken: string, clientId?: string, clientSecret?: string): Promise<void>;
29
35
  //# sourceMappingURL=openauth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openauth.d.ts","sourceRoot":"","sources":["../../src/utils/openauth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAOtD,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAY7E;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB/D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAGlE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAG9D;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAED,wBAAsB,YAAY,CACjC,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,kBAAkB,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAClD,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAoDrC;AAED,wBAAsB,WAAW,CAChC,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
1
+ {"version":3,"file":"openauth.d.ts","sourceRoot":"","sources":["../../src/utils/openauth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EACN,KAAK,+BAA+B,EAGpC,MAAM,oBAAoB,CAAA;AAM3B,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAY7E;AAED,wBAAsB,mBAAmB,CACxC,OAAO,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,EACrB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqB/D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAGlE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,MAAM,CAG9D;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAgBD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAA;CAC9C;AAID,eAAO,MAAM,uBAAuB,EAAE,qBAGpC,CAAA;AAEF,wBAAsB,YAAY,CACjC,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,kBAAkB,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,EACpD,OAAO,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,mBAAmB,GAAG,qBAAqB,GAAG,IAAI,CAAC,CAsD7D;AAED,wBAAsB,WAAW,CAChC,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -1,9 +1,9 @@
1
1
  import { getConfig } from '../config-store.js';
2
- import { createClientSecretBasicAuthHeader, resolveClientSecret } from './client-secret.js';
2
+ import { createClientSecretBasicAuthHeader, resolveClientSecret, } from './client-secret.js';
3
3
  import { resolveInternalIssuer } from './issuer.js';
4
4
  import { generatePKCE } from './pkce.js';
5
5
  import { generateState } from './state.js';
6
- import { decodeJwtPayload, extractTokenClaims } from './tokens.js';
6
+ import { verifyIssuedAccessTokenClaims } from './tokens.js';
7
7
  export async function createAuthFlow(context) {
8
8
  const options = getConfig();
9
9
  const { verifier, challenge } = await generatePKCE();
@@ -28,9 +28,8 @@ export async function getAuthorizationUrl(context, provider, _redirectPath, auth
28
28
  code_challenge: flow.challenge,
29
29
  code_challenge_method: 'S256',
30
30
  provider,
31
+ resource: options.resource,
31
32
  });
32
- if (options.resource)
33
- params.set('resource', options.resource);
34
33
  return {
35
34
  url: `${options.issuer}/authorize?${params}`,
36
35
  pkceVerifier: flow.verifier,
@@ -53,17 +52,32 @@ export function getResetPasswordUrl() {
53
52
  const options = getConfig();
54
53
  return `${options.issuer}/reset-password`;
55
54
  }
56
- export async function exchangeCode(callbackUrl, code, codeVerifier, providerFromCookie) {
55
+ function tokenResponseHasTokens(data) {
56
+ if (typeof data !== 'object' || data === null)
57
+ return false;
58
+ return (typeof data.access_token === 'string' &&
59
+ typeof data.refresh_token === 'string');
60
+ }
61
+ // Backward-compatible exported sentinel for callers that only need a bounded
62
+ // representative failure. Runtime exchangeCode results carry the actual code.
63
+ export const EXCHANGE_SECRET_FAILURE = Object.freeze({
64
+ secretFailed: true,
65
+ code: 'BINDING_REJECTED',
66
+ });
67
+ export async function exchangeCode(callbackUrl, code, codeVerifier, providerFromCookie, request) {
57
68
  const options = getConfig();
58
69
  const issuer = resolveInternalIssuer(options);
59
70
  const clientId = options.clientId || callbackUrl.hostname;
60
71
  const redirectUri = new URL(options.callbackPath, callbackUrl.origin).toString();
61
- const clientSecret = await resolveClientSecret(options.clientSecret);
72
+ const secretResult = await resolveClientSecret(options.clientSecret, request);
73
+ if (!secretResult.ok) {
74
+ return Object.freeze({ secretFailed: true, code: secretResult.code });
75
+ }
62
76
  const headers = {
63
77
  'Content-Type': 'application/x-www-form-urlencoded',
64
78
  };
65
- if (clientSecret) {
66
- headers.Authorization = createClientSecretBasicAuthHeader(clientId, clientSecret);
79
+ if (secretResult.secret) {
80
+ headers.Authorization = createClientSecretBasicAuthHeader(clientId, secretResult.secret);
67
81
  }
68
82
  const body = new URLSearchParams({
69
83
  grant_type: 'authorization_code',
@@ -71,9 +85,8 @@ export async function exchangeCode(callbackUrl, code, codeVerifier, providerFrom
71
85
  code_verifier: codeVerifier,
72
86
  redirect_uri: redirectUri,
73
87
  client_id: clientId,
88
+ resource: options.resource,
74
89
  });
75
- if (options.resource)
76
- body.set('resource', options.resource);
77
90
  try {
78
91
  const response = await fetch(`${issuer}/token`, {
79
92
  method: 'POST',
@@ -82,8 +95,12 @@ export async function exchangeCode(callbackUrl, code, codeVerifier, providerFrom
82
95
  });
83
96
  if (!response.ok)
84
97
  return null;
85
- const data = (await response.json());
86
- const claims = extractTokenClaims(decodeJwtPayload(data.access_token));
98
+ const data = await response.json();
99
+ if (!tokenResponseHasTokens(data))
100
+ return null;
101
+ const claims = await verifyIssuedAccessTokenClaims(issuer, data.access_token, options.resource);
102
+ if (!claims)
103
+ return null;
87
104
  return {
88
105
  accessToken: data.access_token,
89
106
  refreshToken: data.refresh_token,
@@ -1 +1 @@
1
- {"version":3,"file":"openauth.js","sourceRoot":"","sources":["../../src/utils/openauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openauth.js","sourceRoot":"","sources":["../../src/utils/openauth.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ export interface VerifyAccessTokenOptions {
18
18
  algorithms?: string[];
19
19
  }
20
20
  export declare function verifyAccessToken(issuer: string, token: string, options?: VerifyAccessTokenOptions): Promise<VerifiedClaims | null>;
21
+ export declare function verifyIssuedAccessTokenClaims(issuer: string, token: string, audience: string | undefined): Promise<TokenPayloadClaims | null>;
21
22
  export declare function refreshTokens(issuer: string, refreshToken: string, clientId?: string, clientSecret?: string, resource?: string): Promise<TokenRefreshResult | null>;
22
23
  export declare function decodeJwtPayload(jwt: string): Record<string, unknown>;
23
24
  /** @internal Test-only helper. Not exported from @growth-labs/auth/utils. */
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA8DrD,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACzD,SAAS,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAWvF;AAYD,MAAM,WAAW,wBAAwB;IACxC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC5B,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA4BhC;AAED,wBAAsB,aAAa,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA4CpC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErE;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA8DrD,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACzD,SAAS,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAWvF;AAYD,MAAM,WAAW,wBAAwB;IACxC,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC5B,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AA4BD,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAYhC;AAED,wBAAsB,6BAA6B,CAClD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC1B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAKpC;AA6BD,wBAAsB,aAAa,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAmCpC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErE;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
@@ -66,7 +66,7 @@ function isExpiredJwtError(err) {
66
66
  'code' in err &&
67
67
  err.code === 'ERR_JWT_EXPIRED'));
68
68
  }
69
- export async function verifyAccessToken(issuer, token, options = {}) {
69
+ async function verifyAccessTokenPayload(issuer, token, options = {}) {
70
70
  try {
71
71
  const header = decodeJwtHeader(token);
72
72
  const kid = typeof header.kid === 'string' ? header.kid : undefined;
@@ -79,15 +79,7 @@ export async function verifyAccessToken(issuer, token, options = {}) {
79
79
  if (payload.mode !== 'access') {
80
80
  throw new Error(`unexpected token mode: ${String(payload.mode)}`);
81
81
  }
82
- const claims = extractTokenClaims(payload);
83
- return {
84
- sub: claims.sub,
85
- email: claims.email,
86
- name: claims.name,
87
- image: claims.image,
88
- synthetic: payload.synthetic === true ? true : undefined,
89
- purpose: typeof payload.purpose === 'string' ? payload.purpose : undefined,
90
- };
82
+ return payload;
91
83
  }
92
84
  catch (err) {
93
85
  if (!isExpiredJwtError(err)) {
@@ -96,7 +88,49 @@ export async function verifyAccessToken(issuer, token, options = {}) {
96
88
  return null;
97
89
  }
98
90
  }
91
+ export async function verifyAccessToken(issuer, token, options = {}) {
92
+ const payload = await verifyAccessTokenPayload(issuer, token, options);
93
+ if (!payload)
94
+ return null;
95
+ const claims = extractTokenClaims(payload);
96
+ return {
97
+ sub: claims.sub,
98
+ email: claims.email,
99
+ name: claims.name,
100
+ image: claims.image,
101
+ synthetic: payload.synthetic === true ? true : undefined,
102
+ purpose: typeof payload.purpose === 'string' ? payload.purpose : undefined,
103
+ };
104
+ }
105
+ export async function verifyIssuedAccessTokenClaims(issuer, token, audience) {
106
+ if (!audience)
107
+ return null;
108
+ const payload = await verifyAccessTokenPayload(issuer, token, { audience });
109
+ if (!payload)
110
+ return null;
111
+ return extractTokenClaims(payload);
112
+ }
113
+ function tokenResponseHasTokens(data) {
114
+ if (typeof data !== 'object' || data === null)
115
+ return false;
116
+ return (typeof data.access_token === 'string' &&
117
+ typeof data.refresh_token === 'string');
118
+ }
119
+ function tokenResultFromClaims(accessToken, refreshToken, claims) {
120
+ return {
121
+ accessToken,
122
+ refreshToken,
123
+ claims: {
124
+ sub: claims.sub,
125
+ email: claims.email,
126
+ name: claims.name,
127
+ image: claims.image,
128
+ },
129
+ };
130
+ }
99
131
  export async function refreshTokens(issuer, refreshToken, clientId, clientSecret, resource) {
132
+ if (!resource)
133
+ return null;
100
134
  try {
101
135
  const params = new URLSearchParams({
102
136
  grant_type: 'refresh_token',
@@ -104,8 +138,7 @@ export async function refreshTokens(issuer, refreshToken, clientId, clientSecret
104
138
  });
105
139
  if (clientId)
106
140
  params.set('client_id', clientId);
107
- if (resource)
108
- params.set('resource', resource);
141
+ params.set('resource', resource);
109
142
  const headers = {
110
143
  'Content-Type': 'application/x-www-form-urlencoded',
111
144
  };
@@ -119,18 +152,13 @@ export async function refreshTokens(issuer, refreshToken, clientId, clientSecret
119
152
  });
120
153
  if (!response.ok)
121
154
  return null;
122
- const data = (await response.json());
123
- const claims = extractTokenClaims(decodeJwtPayload(data.access_token));
124
- return {
125
- accessToken: data.access_token,
126
- refreshToken: data.refresh_token,
127
- claims: {
128
- sub: claims.sub,
129
- email: claims.email,
130
- name: claims.name,
131
- image: claims.image,
132
- },
133
- };
155
+ const data = await response.json();
156
+ if (!tokenResponseHasTokens(data))
157
+ return null;
158
+ const claims = await verifyIssuedAccessTokenClaims(issuer, data.access_token, resource);
159
+ if (!claims)
160
+ return null;
161
+ return tokenResultFromClaims(data.access_token, data.refresh_token, claims);
134
162
  }
135
163
  catch {
136
164
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/utils/tokens.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growth-labs/auth",
3
- "version": "0.9.1",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -57,7 +57,7 @@
57
57
  "typescript": "^5.8.0",
58
58
  "vite": "^7.3.5",
59
59
  "vitest": "^4.1.8",
60
- "@growth-labs/analytics": "0.14.0"
60
+ "@growth-labs/analytics": "0.14.7"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsc",
@@ -1,4 +1,5 @@
1
1
  import type { ResolvedAuthOptions } from './options.js'
2
+ import { rehydrateClientSecretLiteral } from './utils/client-secret-literal.js'
2
3
  import { isEnvMarker, resolveEnvMarker } from './utils/env-marker.js'
3
4
 
4
5
  let _config: ResolvedAuthOptions | null = null
@@ -13,7 +14,7 @@ export function getConfig(): ResolvedAuthOptions {
13
14
  __GL_AUTH_CONFIG__?: ResolvedAuthOptions
14
15
  }
15
16
  ).__GL_AUTH_CONFIG__
16
- const base = runtimeConfig ?? _config
17
+ const base = runtimeConfig ? rehydrateRuntimeConfig(runtimeConfig) : _config
17
18
  if (!base) {
18
19
  throw new Error(
19
20
  '@growth-labs/auth: integration not configured. Add auth() to your astro.config.mjs integrations array.',
@@ -22,6 +23,17 @@ export function getConfig(): ResolvedAuthOptions {
22
23
  return resolveEnvMarkers(base)
23
24
  }
24
25
 
26
+ function rehydrateRuntimeConfig(base: ResolvedAuthOptions): ResolvedAuthOptions {
27
+ // The Vite global is emitted only after authOptionsSchema accepts a
28
+ // WeakSet-issued literal at build time. Treat the structural wire shape as
29
+ // transport encoding here, then re-issue the runtime object; the resolver
30
+ // itself never trusts the structural marker.
31
+ const clientSecret = rehydrateClientSecretLiteral(base.clientSecret)
32
+ return clientSecret === base.clientSecret
33
+ ? base
34
+ : ({ ...base, clientSecret } as ResolvedAuthOptions)
35
+ }
36
+
25
37
  /**
26
38
  * Resolves any `"env:VAR_NAME"` markers on auth-tuple fields against the
27
39
  * runtime env stashed by `src/runtime-bridge.ts`. The bridge is loaded
@@ -51,12 +63,16 @@ function resolveEnvMarkers(base: ResolvedAuthOptions): ResolvedAuthOptions {
51
63
  if (typeof issuer !== 'string') {
52
64
  throw new Error('@growth-labs/auth: issuer must resolve to a string')
53
65
  }
66
+ const resource = resolveEnvMarker(base.resource, env)
67
+ if (typeof resource !== 'string') {
68
+ throw new Error('@growth-labs/auth: resource must resolve to a string')
69
+ }
54
70
  return {
55
71
  ...base,
56
72
  issuer,
57
73
  issuerInternal: resolveEnvMarker(base.issuerInternal, env),
58
74
  clientId: resolveEnvMarker(base.clientId, env),
59
- resource: resolveEnvMarker(base.resource, env),
75
+ resource,
60
76
  }
61
77
  }
62
78
 
package/src/index.ts CHANGED
@@ -2,6 +2,10 @@ import { _setConfig } from './config-store.js'
2
2
  import type { AuthOptions } from './options.js'
3
3
  import { authOptionsSchema } from './options.js'
4
4
  import type { AuthRenderers } from './types.js'
5
+ import {
6
+ isClientSecretLiteral,
7
+ serializeClientSecretLiteral,
8
+ } from './utils/client-secret-literal.js'
5
9
 
6
10
  interface AstroIntegration {
7
11
  name: string
@@ -72,7 +76,13 @@ function isValidRendererSpec(value: unknown): value is { module: string; export:
72
76
  export default function auth(userOptions: AuthOptions): AstroIntegration {
73
77
  const options = authOptionsSchema.parse(userOptions)
74
78
  _setConfig(options)
75
- const serializableOptions = { ...options, renderers: {} }
79
+ const serializableOptions = {
80
+ ...options,
81
+ clientSecret: isClientSecretLiteral(options.clientSecret)
82
+ ? serializeClientSecretLiteral(options.clientSecret)
83
+ : options.clientSecret,
84
+ renderers: {},
85
+ }
76
86
 
77
87
  return {
78
88
  name: '@growth-labs/auth',
@@ -3,6 +3,7 @@ import '../runtime-bridge.js'
3
3
  import type { User } from '../types.js'
4
4
  import { resolveClientSecret } from '../utils/client-secret.js'
5
5
  import { getCookie, setTokenCookies } from '../utils/cookies.js'
6
+ import { secretResolutionFailureResponse } from '../utils/fail-closed.js'
6
7
  import { resolveInternalIssuer } from '../utils/issuer.js'
7
8
  import { refreshTokens, verifyAccessToken } from '../utils/tokens.js'
8
9
 
@@ -21,9 +22,30 @@ export async function onRequest(
21
22
  // 2. Attempt to authenticate
22
23
  let user: User | null = null
23
24
  let refreshed: Awaited<ReturnType<typeof refreshTokens>> = null
25
+ let resolvedClientSecret: string | undefined
26
+
27
+ // A request carrying a refresh token can enter the confidential-client
28
+ // refresh path if access-token verification fails. Resolve that request's
29
+ // configured secret before verification so a cold JWKS lookup can never
30
+ // precede discovery of a broken binding. The module-private Request-keyed
31
+ // resolver cache makes this the single read, and the exact bytes are reused
32
+ // below. A refresh token explicitly opts the request into this fail-closed
33
+ // confidential-session invariant even when its access token would verify;
34
+ // an access-token-only request never needs or reads the client secret.
35
+ if (refreshToken && options.clientSecret !== undefined) {
36
+ const secretResult = await resolveClientSecret(options.clientSecret, request)
37
+ if (!secretResult.ok) {
38
+ context.locals.user = null
39
+ return secretResolutionFailureResponse(request, {
40
+ surface: 'middleware_refresh',
41
+ code: secretResult.code,
42
+ })
43
+ }
44
+ resolvedClientSecret = secretResult.secret ?? undefined
45
+ }
24
46
 
25
47
  if (accessToken) {
26
- const verified = await verifyAccessToken(issuer, accessToken)
48
+ const verified = await verifyAccessToken(issuer, accessToken, { audience: options.resource })
27
49
  if (verified) {
28
50
  user = {
29
51
  id: verified.sub,
@@ -39,8 +61,13 @@ export async function onRequest(
39
61
 
40
62
  if (!user && refreshToken) {
41
63
  const clientId = options.clientId || context.url.hostname
42
- const clientSecret = await resolveClientSecret(options.clientSecret)
43
- refreshed = await refreshTokens(issuer, refreshToken, clientId, clientSecret, options.resource)
64
+ refreshed = await refreshTokens(
65
+ issuer,
66
+ refreshToken,
67
+ clientId,
68
+ resolvedClientSecret,
69
+ options.resource,
70
+ )
44
71
  if (refreshed) {
45
72
  user = {
46
73
  id: refreshed.claims.sub,
@@ -66,7 +93,7 @@ export async function onRequest(
66
93
  // 5. Continue to the page
67
94
  const response = await next()
68
95
 
69
- // 6. If tokens were refreshed or response must be modified, clone and apply changes
96
+ // 6. If tokens were refreshed or the response must carry no-store, clone and apply.
70
97
  if (refreshed || context.locals.synthetic === true) {
71
98
  const modifiedResponse = new Response(response.body, {
72
99
  status: response.status,
package/src/options.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import type { AuthRenderers } from './types.js'
3
+ import type { ClientSecretLiteral } from './utils/client-secret-literal.js'
4
+ import { isClientSecretLiteral } from './utils/client-secret-literal.js'
3
5
 
4
6
  // String accepted as either a literal URL or an "env:VAR_NAME" marker that
5
7
  // gets resolved against the Worker runtime env at request time. See
@@ -27,8 +29,21 @@ export const authOptionsSchema = z.object({
27
29
 
28
30
  // ─── Client identity ───
29
31
  clientId: stringOrEnvMarker.optional(),
30
- clientSecret: z.string().optional(),
31
- resource: urlOrEnvMarker.optional(),
32
+ // clientSecret accepts either a binding name (string resolved at request time
33
+ // against the Worker runtime env) or an explicit literalClientSecret() value
34
+ // (local-dev only; embedded in the serialized config). A bare string is
35
+ // always treated as a binding name — fail closed when the binding is absent.
36
+ // An empty or whitespace-only binding name is schema-rejected; it can never
37
+ // resolve to a usable secret and must not silently become a public client.
38
+ clientSecret: z
39
+ .union([
40
+ z.string().refine((v) => v.trim().length > 0, {
41
+ message: 'clientSecret binding name must not be empty or whitespace-only',
42
+ }),
43
+ z.custom<ClientSecretLiteral>(isClientSecretLiteral),
44
+ ])
45
+ .optional(),
46
+ resource: urlOrEnvMarker,
32
47
 
33
48
  // ─── Cookie configuration ───
34
49
  cookiePrefix: z.string(),
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  authClient,
3
+ failClosedOnSecretResolutionFailure,
3
4
  getConfig,
4
5
  type RouteContext,
5
6
  readForm,
@@ -9,6 +10,7 @@ import {
9
10
  } from './shared.js'
10
11
 
11
12
  export async function POST(context: RouteContext): Promise<Response> {
13
+ // Trust boundary: Origin rejection must precede config, secret, form, and issuer I/O.
12
14
  const crossSitePost = rejectCrossSitePost(context)
13
15
  if (crossSitePost) return crossSitePost
14
16
 
@@ -17,6 +19,9 @@ export async function POST(context: RouteContext): Promise<Response> {
17
19
  return new Response('Not found', { status: 404 })
18
20
  }
19
21
 
22
+ const secretError = await failClosedOnSecretResolutionFailure(context)
23
+ if (secretError) return secretError
24
+
20
25
  const form = await readForm(context)
21
26
  const email = await readFormString(form, 'email')
22
27
  const redirect = form.get('redirect')?.toString()
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  authClient,
3
3
  callbackRedirect,
4
+ failClosedOnSecretResolutionFailure,
4
5
  getConfig,
5
6
  type RouteContext,
6
7
  readForm,
@@ -9,6 +10,7 @@ import {
9
10
  } from './shared.js'
10
11
 
11
12
  export async function POST(context: RouteContext): Promise<Response> {
13
+ // Trust boundary: Origin rejection must precede config, secret, form, and issuer I/O.
12
14
  const crossSitePost = rejectCrossSitePost(context)
13
15
  if (crossSitePost) return crossSitePost
14
16
 
@@ -17,6 +19,9 @@ export async function POST(context: RouteContext): Promise<Response> {
17
19
  return new Response('Not found', { status: 404 })
18
20
  }
19
21
 
22
+ const secretError = await failClosedOnSecretResolutionFailure(context)
23
+ if (secretError) return secretError
24
+
20
25
  const form = await readForm(context)
21
26
  const email = await readFormString(form, 'email')
22
27
  const code = await readFormString(form, 'code')