@venturekit/auth 0.0.0-dev.20260429204013 → 0.0.0-dev.20260430161503

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.
@@ -3,16 +3,17 @@
3
3
  *
4
4
  * The server flows in this directory all hit the same Cognito User Pool
5
5
  * within a Lambda invocation, so we cache one
6
- * `CognitoIdentityProviderClient` per region. The cache is keyed by region
7
- * because a single deployment could (in theory) host pools in multiple
8
- * regions in practice every flow uses the region from
9
- * {@link AuthServerConfig.region}, so the cache stays at size 1.
6
+ * `CognitoIdentityProviderClient` per `(region, endpoint)` tuple. In
7
+ * production both stay constant per Lambda, so the cache stays at size 1.
8
+ * In `vk dev` the `endpoint` differs (it points at cognito-local), so we
9
+ * key on it as well to avoid serving prod-config requests through a
10
+ * dev-config client when both happen to live in the same process.
10
11
  *
11
12
  * This module is **not** part of the public API surface; consumers go
12
13
  * through `sign-in.ts`, `refresh.ts`, `revoke.ts`.
13
14
  */
14
15
  import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider';
15
- export declare function getCognitoClient(region: string): CognitoIdentityProviderClient;
16
+ export declare function getCognitoClient(region: string, endpoint?: string): CognitoIdentityProviderClient;
16
17
  /** Test-only — clear the cached clients between vitest runs. */
17
18
  export declare function _resetCognitoClientCacheForTesting(): void;
18
19
  //# sourceMappingURL=cognito-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cognito-client.d.ts","sourceRoot":"","sources":["../../src/server/cognito-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAI1F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,6BAA6B,CAM9E;AAED,gEAAgE;AAChE,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD"}
1
+ {"version":3,"file":"cognito-client.d.ts","sourceRoot":"","sources":["../../src/server/cognito-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAQ1F,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,6BAA6B,CAa/B;AAED,gEAAgE;AAChE,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD"}
@@ -3,22 +3,33 @@
3
3
  *
4
4
  * The server flows in this directory all hit the same Cognito User Pool
5
5
  * within a Lambda invocation, so we cache one
6
- * `CognitoIdentityProviderClient` per region. The cache is keyed by region
7
- * because a single deployment could (in theory) host pools in multiple
8
- * regions in practice every flow uses the region from
9
- * {@link AuthServerConfig.region}, so the cache stays at size 1.
6
+ * `CognitoIdentityProviderClient` per `(region, endpoint)` tuple. In
7
+ * production both stay constant per Lambda, so the cache stays at size 1.
8
+ * In `vk dev` the `endpoint` differs (it points at cognito-local), so we
9
+ * key on it as well to avoid serving prod-config requests through a
10
+ * dev-config client when both happen to live in the same process.
10
11
  *
11
12
  * This module is **not** part of the public API surface; consumers go
12
13
  * through `sign-in.ts`, `refresh.ts`, `revoke.ts`.
13
14
  */
14
15
  import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider';
15
16
  const cache = new Map();
16
- export function getCognitoClient(region) {
17
- const existing = cache.get(region);
17
+ function key(region, endpoint) {
18
+ return `${region}|${endpoint ?? ''}`;
19
+ }
20
+ export function getCognitoClient(region, endpoint) {
21
+ const k = key(region, endpoint);
22
+ const existing = cache.get(k);
18
23
  if (existing)
19
24
  return existing;
20
- const client = new CognitoIdentityProviderClient({ region });
21
- cache.set(region, client);
25
+ const client = new CognitoIdentityProviderClient({
26
+ region,
27
+ // When `endpoint` is set the SDK skips its usual host construction
28
+ // (`https://cognito-idp.<region>.amazonaws.com`) and POSTs to this URL
29
+ // instead — exactly what we want for cognito-local in dev.
30
+ ...(endpoint ? { endpoint } : {}),
31
+ });
32
+ cache.set(k, client);
22
33
  return client;
23
34
  }
24
35
  /** Test-only — clear the cached clients between vitest runs. */
@@ -1 +1 @@
1
- {"version":3,"file":"cognito-client.js","sourceRoot":"","sources":["../../src/server/cognito-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAE1F,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyC,CAAC;AAE/D,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,IAAI,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7D,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kCAAkC;IAChD,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"cognito-client.js","sourceRoot":"","sources":["../../src/server/cognito-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAE1F,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyC,CAAC;AAE/D,SAAS,GAAG,CAAC,MAAc,EAAE,QAA4B;IACvD,OAAO,GAAG,MAAM,IAAI,QAAQ,IAAI,EAAE,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,QAAiB;IAEjB,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,MAAM,GAAG,IAAI,6BAA6B,CAAC;QAC/C,MAAM;QACN,mEAAmE;QACnE,uEAAuE;QACvE,2DAA2D;QAC3D,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClC,CAAC,CAAC;IACH,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACrB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,kCAAkC;IAChD,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -7,12 +7,17 @@
7
7
  * - `region` — AWS region the user pool lives in
8
8
  * - `userPoolId` — Cognito User Pool id
9
9
  * - `appClientId` — Cognito User Pool app client id
10
+ * - `endpoint` — optional override for the Cognito API endpoint
11
+ * (used by `vk dev` to point at the in-docker
12
+ * cognito-local service; never set in deployed
13
+ * Lambdas)
10
14
  *
11
15
  * Most consumers will call {@link loadAuthServerConfig} which reads these
12
16
  * from environment variables (`COGNITO_REGION`/`AWS_REGION`,
13
- * `COGNITO_USER_POOL_ID`, `COGNITO_APP_CLIENT_ID`). The VentureKit
14
- * deploy pipeline injects these into the Lambda environment when an
15
- * `auth` intent is declared in `vk.config.ts`.
17
+ * `COGNITO_USER_POOL_ID`, `COGNITO_APP_CLIENT_ID`, optional
18
+ * `COGNITO_ENDPOINT`). The VentureKit deploy pipeline injects the first
19
+ * three into the Lambda environment automatically when an `auth` intent
20
+ * is declared in `vk.config.ts`.
16
21
  *
17
22
  * Tests / scripts can pass a `config` argument explicitly to every flow
18
23
  * and skip env-var loading entirely.
@@ -21,6 +26,15 @@ export interface AuthServerConfig {
21
26
  region: string;
22
27
  userPoolId: string;
23
28
  appClientId: string;
29
+ /**
30
+ * Cognito API endpoint override. When set, all SDK calls
31
+ * (`signInWithPassword`, `refreshSession`, `revokeRefreshToken`) target
32
+ * this URL instead of the public AWS endpoint, and JWT verification
33
+ * uses the generic OIDC verifier with `<endpoint>/<userPoolId>` as the
34
+ * issuer. `vk dev` populates this with `http://localhost:9229` (the
35
+ * cognito-local Docker service); deployed Lambdas leave it unset.
36
+ */
37
+ endpoint?: string;
24
38
  }
25
39
  /**
26
40
  * Read server-side auth configuration from environment variables.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACxE,gBAAgB,CAqBlB"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACxE,gBAAgB,CAwBlB"}
@@ -7,12 +7,17 @@
7
7
  * - `region` — AWS region the user pool lives in
8
8
  * - `userPoolId` — Cognito User Pool id
9
9
  * - `appClientId` — Cognito User Pool app client id
10
+ * - `endpoint` — optional override for the Cognito API endpoint
11
+ * (used by `vk dev` to point at the in-docker
12
+ * cognito-local service; never set in deployed
13
+ * Lambdas)
10
14
  *
11
15
  * Most consumers will call {@link loadAuthServerConfig} which reads these
12
16
  * from environment variables (`COGNITO_REGION`/`AWS_REGION`,
13
- * `COGNITO_USER_POOL_ID`, `COGNITO_APP_CLIENT_ID`). The VentureKit
14
- * deploy pipeline injects these into the Lambda environment when an
15
- * `auth` intent is declared in `vk.config.ts`.
17
+ * `COGNITO_USER_POOL_ID`, `COGNITO_APP_CLIENT_ID`, optional
18
+ * `COGNITO_ENDPOINT`). The VentureKit deploy pipeline injects the first
19
+ * three into the Lambda environment automatically when an `auth` intent
20
+ * is declared in `vk.config.ts`.
16
21
  *
17
22
  * Tests / scripts can pass a `config` argument explicitly to every flow
18
23
  * and skip env-var loading entirely.
@@ -30,6 +35,7 @@ export function loadAuthServerConfig(env = process.env) {
30
35
  const region = env['COGNITO_REGION'] ?? env['AWS_REGION'];
31
36
  const userPoolId = env['COGNITO_USER_POOL_ID'];
32
37
  const appClientId = env['COGNITO_APP_CLIENT_ID'];
38
+ const endpoint = env['COGNITO_ENDPOINT'];
33
39
  const missing = [];
34
40
  if (!region)
35
41
  missing.push('COGNITO_REGION (or AWS_REGION)');
@@ -40,13 +46,15 @@ export function loadAuthServerConfig(env = process.env) {
40
46
  if (missing.length > 0) {
41
47
  throw new Error(`[@venturekit/auth/server] missing env var(s): ${missing.join(', ')} — ` +
42
48
  `VentureKit's auth intent in vk.config.ts injects these into the Lambda ` +
43
- `environment from the deployed user pool. Set them manually in .env.local ` +
44
- `for local dev.`);
49
+ `environment from the deployed user pool. For local dev, run \`vk dev\` ` +
50
+ `which auto-provisions a cognito-local user pool and writes the IDs ` +
51
+ `into the dev server's env (or set them manually in .env.local).`);
45
52
  }
46
53
  return {
47
54
  region: region,
48
55
  userPoolId: userPoolId,
49
56
  appClientId: appClientId,
57
+ ...(endpoint ? { endpoint } : {}),
50
58
  };
51
59
  }
52
60
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAA8D,OAAO,CAAC,GAAG;IAEzE,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACjD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW;QAAE,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,iDAAiD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK;YACtE,yEAAyE;YACzE,2EAA2E;YAC3E,gBAAgB,CACnB,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAO;QACf,UAAU,EAAE,UAAW;QACvB,WAAW,EAAE,WAAY;KAC1B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAiBH;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAA8D,OAAO,CAAC,GAAG;IAEzE,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACzC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW;QAAE,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,iDAAiD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK;YACtE,yEAAyE;YACzE,yEAAyE;YACzE,qEAAqE;YACrE,iEAAiE,CACpE,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAO;QACf,UAAU,EAAE,UAAW;QACvB,WAAW,EAAE,WAAY;QACzB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAKzD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,UAAU,CAAC,cAAc,CAAC,CAgC5B;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,sBAAsB,EAC7B,UAAU,GAAE,MAAwB,GACnC,MAAM,GAAG,IAAI,CAaf"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAKzD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC,GACxC,UAAU,CAAC,cAAc,CAAC,CAiC5B;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,sBAAsB,EAC7B,UAAU,GAAE,MAAwB,GACnC,MAAM,GAAG,IAAI,CAaf"}
@@ -67,6 +67,7 @@ export function cookieAuthMiddleware(options = {}) {
67
67
  userPoolId: cfg.userPoolId,
68
68
  clientId: cfg.appClientId,
69
69
  tokenUse,
70
+ ...(cfg.endpoint ? { endpoint: cfg.endpoint } : {}),
70
71
  });
71
72
  if (claims) {
72
73
  ctx.user = claimsToUserContext(claims);
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/server/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAKpD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,gBAAyC,GAChD,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/server/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,gBAAyC,GAChD,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
@@ -11,6 +11,7 @@ import { InitiateAuthCommand } from '@aws-sdk/client-cognito-identity-provider';
11
11
  import { loadAuthServerConfig } from './config.js';
12
12
  import { getCognitoClient } from './cognito-client.js';
13
13
  import { AuthError, mapProviderError } from './errors.js';
14
+ import { deriveExpiresInFromJwt } from './token-utils.js';
14
15
  /**
15
16
  * Exchange a refresh token for fresh id + access tokens.
16
17
  *
@@ -19,7 +20,7 @@ import { AuthError, mapProviderError } from './errors.js';
19
20
  * failures so the user is redirected straight to sign-in.
20
21
  */
21
22
  export async function refreshSession(refreshToken, config = loadAuthServerConfig()) {
22
- const client = getCognitoClient(config.region);
23
+ const client = getCognitoClient(config.region, config.endpoint);
23
24
  let res;
24
25
  try {
25
26
  res = await client.send(new InitiateAuthCommand({
@@ -32,14 +33,16 @@ export async function refreshSession(refreshToken, config = loadAuthServerConfig
32
33
  throw mapProviderError(err, 'refresh_failed');
33
34
  }
34
35
  const result = res.AuthenticationResult;
35
- if (!result?.IdToken || !result.AccessToken || !result.ExpiresIn) {
36
+ // Tokens MUST be present; `ExpiresIn` is derived from the access
37
+ // token's `exp` claim when the provider omits it (see `sign-in.ts`).
38
+ if (!result?.IdToken || !result.AccessToken) {
36
39
  throw new AuthError('incomplete_auth_result', 'Identity provider returned an incomplete refresh result', 500);
37
40
  }
38
41
  return {
39
42
  idToken: result.IdToken,
40
43
  accessToken: result.AccessToken,
41
44
  refreshToken: '',
42
- expiresIn: result.ExpiresIn,
45
+ expiresIn: result.ExpiresIn ?? deriveExpiresInFromJwt(result.AccessToken),
43
46
  };
44
47
  }
45
48
  //# sourceMappingURL=refresh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/server/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAe1D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CACrB,IAAI,mBAAmB,CAAC;YACtB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,QAAQ,EAAE,oBAAoB;YAC9B,cAAc,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE;SAChD,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,oBAAoB,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACjE,MAAM,IAAI,SAAS,CACjB,wBAAwB,EACxB,yDAAyD,EACzD,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,EAAE;QAChB,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/server/refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAe1D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,YAAoB,EACpB,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CACrB,IAAI,mBAAmB,CAAC;YACtB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,QAAQ,EAAE,oBAAoB;YAC9B,cAAc,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE;SAChD,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,oBAAoB,CAAC;IACxC,iEAAiE;IACjE,qEAAqE;IACrE,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,IAAI,SAAS,CACjB,wBAAwB,EACxB,yDAAyD,EACzD,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,EAAE;QAChB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,sBAAsB,CAAC,MAAM,CAAC,WAAW,CAAC;KAC1E,CAAC;AACJ,CAAC"}
@@ -17,7 +17,7 @@ import { getCognitoClient } from './cognito-client.js';
17
17
  * the function never throws.
18
18
  */
19
19
  export async function revokeRefreshToken(refreshToken, config = loadAuthServerConfig()) {
20
- const client = getCognitoClient(config.region);
20
+ const client = getCognitoClient(config.region, config.endpoint);
21
21
  try {
22
22
  await client.send(new RevokeTokenCommand({
23
23
  ClientId: config.appClientId,
@@ -1 +1 @@
1
- {"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../src/server/revoke.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,YAAoB,EACpB,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,kBAAkB,CAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,KAAK,EAAE,YAAY;SACpB,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,kEAAkE,EAAE,GAAG,CAAC,CAAC;IACxF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../src/server/revoke.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAE/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,YAAoB,EACpB,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,kBAAkB,CAAC;YACrB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,KAAK,EAAE,YAAY;SACpB,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,kEAAkE,EAAE,GAAG,CAAC,CAAC;IACxF,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../src/server/sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAKpD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,iBAAiB,EAC9B,MAAM,GAAE,gBAAyC,GAChD,OAAO,CAAC,YAAY,CAAC,CAkBvB"}
1
+ {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../src/server/sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,iBAAiB,EAC9B,MAAM,GAAE,gBAAyC,GAChD,OAAO,CAAC,YAAY,CAAC,CAsCvB"}
@@ -20,6 +20,7 @@ import { InitiateAuthCommand, } from '@aws-sdk/client-cognito-identity-provider'
20
20
  import { loadAuthServerConfig } from './config.js';
21
21
  import { getCognitoClient } from './cognito-client.js';
22
22
  import { AuthError, mapProviderError } from './errors.js';
23
+ import { deriveExpiresInFromJwt } from './token-utils.js';
23
24
  /**
24
25
  * Authenticate a user with email + password and return freshly-minted
25
26
  * id, access, and refresh tokens.
@@ -29,7 +30,7 @@ import { AuthError, mapProviderError } from './errors.js';
29
30
  * {@link loadAuthServerConfig}.
30
31
  */
31
32
  export async function signInWithPassword(credentials, config = loadAuthServerConfig()) {
32
- const client = getCognitoClient(config.region);
33
+ const client = getCognitoClient(config.region, config.endpoint);
33
34
  let res;
34
35
  try {
35
36
  res = await client.send(new InitiateAuthCommand({
@@ -44,20 +45,47 @@ export async function signInWithPassword(credentials, config = loadAuthServerCon
44
45
  catch (err) {
45
46
  throw mapProviderError(err, 'auth_failed');
46
47
  }
48
+ // Cognito returns one of three shapes:
49
+ // 1. `AuthenticationResult` — success, tokens are present
50
+ // 2. `ChallengeName` + `Session` — credentials OK but additional step
51
+ // needed (NEW_PASSWORD_REQUIRED, MFA_SETUP, SMS_MFA, …)
52
+ // 3. neither — only happens with broken/misconfigured providers
53
+ //
54
+ // We don't auto-resolve challenges — surfacing the challenge name lets
55
+ // callers implement the right flow. The most common one in dev is
56
+ // NEW_PASSWORD_REQUIRED, which fires when a user was created with
57
+ // `admin-create-user --temporary-password` but never had
58
+ // `admin-set-user-password --permanent` run against them.
59
+ if (res.ChallengeName) {
60
+ throw new AuthError('auth_challenge_required', challengeMessage(res.ChallengeName), 400);
61
+ }
47
62
  return extractSignInTokens(res.AuthenticationResult);
48
63
  }
64
+ function challengeMessage(challengeName) {
65
+ const base = `Identity provider requires challenge: ${challengeName}.`;
66
+ if (challengeName === 'NEW_PASSWORD_REQUIRED') {
67
+ return (base +
68
+ ' User is in FORCE_CHANGE_PASSWORD state — set a permanent password' +
69
+ ' (local dev: `aws cognito-idp admin-set-user-password --permanent`;' +
70
+ ' prod: complete the new-password flow client-side).');
71
+ }
72
+ return base;
73
+ }
49
74
  function extractSignInTokens(result) {
75
+ // Tokens themselves MUST be present. `ExpiresIn` is derived from the
76
+ // access-token's `exp` claim when the response omits it — real AWS
77
+ // always sends it, but some OSS compatibles (cognito-local) don't,
78
+ // and the tokens are usable regardless.
50
79
  if (!result?.IdToken ||
51
80
  !result.AccessToken ||
52
- !result.RefreshToken ||
53
- !result.ExpiresIn) {
81
+ !result.RefreshToken) {
54
82
  throw new AuthError('incomplete_auth_result', 'Identity provider returned an incomplete authentication result', 500);
55
83
  }
56
84
  return {
57
85
  idToken: result.IdToken,
58
86
  accessToken: result.AccessToken,
59
87
  refreshToken: result.RefreshToken,
60
- expiresIn: result.ExpiresIn,
88
+ expiresIn: result.ExpiresIn ?? deriveExpiresInFromJwt(result.AccessToken),
61
89
  };
62
90
  }
63
91
  //# sourceMappingURL=sign-in.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sign-in.js","sourceRoot":"","sources":["../../src/server/sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,GAEpB,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAe1D;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAA8B,EAC9B,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,GAAG,CAAC;IACR,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CACrB,IAAI,mBAAmB,CAAC;YACtB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,QAAQ,EAAE,oBAAoB;YAC9B,cAAc,EAAE;gBACd,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE;gBACzC,QAAQ,EAAE,WAAW,CAAC,QAAQ;aAC/B;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,mBAAmB,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,mBAAmB,CAC1B,MAA4C;IAE5C,IACE,CAAC,MAAM,EAAE,OAAO;QAChB,CAAC,MAAM,CAAC,WAAW;QACnB,CAAC,MAAM,CAAC,YAAY;QACpB,CAAC,MAAM,CAAC,SAAS,EACjB,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,wBAAwB,EACxB,gEAAgE,EAChE,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"sign-in.js","sourceRoot":"","sources":["../../src/server/sign-in.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,GAGpB,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAe1D;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAA8B,EAC9B,SAA2B,oBAAoB,EAAE;IAEjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,GAA8B,CAAC;IACnC,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CACrB,IAAI,mBAAmB,CAAC;YACtB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,QAAQ,EAAE,oBAAoB;YAC9B,cAAc,EAAE;gBACd,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE;gBACzC,QAAQ,EAAE,WAAW,CAAC,QAAQ;aAC/B;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC;IAED,uCAAuC;IACvC,4DAA4D;IAC5D,wEAAwE;IACxE,6DAA6D;IAC7D,kEAAkE;IAClE,EAAE;IACF,uEAAuE;IACvE,kEAAkE;IAClE,kEAAkE;IAClE,yDAAyD;IACzD,0DAA0D;IAC1D,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;QACtB,MAAM,IAAI,SAAS,CACjB,yBAAyB,EACzB,gBAAgB,CAAC,GAAG,CAAC,aAAa,CAAC,EACnC,GAAG,CACJ,CAAC;IACJ,CAAC;IAED,OAAO,mBAAmB,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,gBAAgB,CAAC,aAAqB;IAC7C,MAAM,IAAI,GAAG,yCAAyC,aAAa,GAAG,CAAC;IACvE,IAAI,aAAa,KAAK,uBAAuB,EAAE,CAAC;QAC9C,OAAO,CACL,IAAI;YACJ,oEAAoE;YACpE,qEAAqE;YACrE,qDAAqD,CACtD,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAC1B,MAA4C;IAE5C,qEAAqE;IACrE,mEAAmE;IACnE,mEAAmE;IACnE,wCAAwC;IACxC,IACE,CAAC,MAAM,EAAE,OAAO;QAChB,CAAC,MAAM,CAAC,WAAW;QACnB,CAAC,MAAM,CAAC,YAAY,EACpB,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,wBAAwB,EACxB,gEAAgE,EAChE,GAAG,CACJ,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,sBAAsB,CAAC,MAAM,CAAC,WAAW,CAAC;KAC1E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Internal token-introspection helpers.
3
+ *
4
+ * The Cognito SDK returns `ExpiresIn` alongside every successful
5
+ * `InitiateAuthCommand`, but some OSS Cognito compatibles
6
+ * (notably `cognito-local`) omit that top-level field even when the
7
+ * tokens themselves carry a perfectly valid `exp` claim. Rather than
8
+ * treat those responses as fatal, we derive the remaining lifetime
9
+ * from the access token itself.
10
+ *
11
+ * This file intentionally has no `@venturekit/auth/server` surface
12
+ * exposure — it's consumed by `sign-in.ts` and `refresh.ts` only.
13
+ */
14
+ /** Cognito's default access-token lifetime (1 hour). */
15
+ export declare const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
16
+ /**
17
+ * Compute seconds-until-expiry from a JWT's `exp` claim, preferring
18
+ * `exp - now` (the actual remaining validity) and falling back to
19
+ * `exp - iat` if the system clock is off. Returns the Cognito default
20
+ * when the token is malformed or lacks a usable `exp`.
21
+ *
22
+ * This function does NOT verify the token's signature — the caller is
23
+ * expected to trust the minting provider (a fresh response from
24
+ * `InitiateAuthCommand`). Use `verifyAndDecode` for untrusted input.
25
+ */
26
+ export declare function deriveExpiresInFromJwt(jwt: string, nowSeconds?: number): number;
27
+ //# sourceMappingURL=token-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wDAAwD;AACxD,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAsC,GACjD,MAAM,CAgBR"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Internal token-introspection helpers.
3
+ *
4
+ * The Cognito SDK returns `ExpiresIn` alongside every successful
5
+ * `InitiateAuthCommand`, but some OSS Cognito compatibles
6
+ * (notably `cognito-local`) omit that top-level field even when the
7
+ * tokens themselves carry a perfectly valid `exp` claim. Rather than
8
+ * treat those responses as fatal, we derive the remaining lifetime
9
+ * from the access token itself.
10
+ *
11
+ * This file intentionally has no `@venturekit/auth/server` surface
12
+ * exposure — it's consumed by `sign-in.ts` and `refresh.ts` only.
13
+ */
14
+ /** Cognito's default access-token lifetime (1 hour). */
15
+ export const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 3600;
16
+ /**
17
+ * Compute seconds-until-expiry from a JWT's `exp` claim, preferring
18
+ * `exp - now` (the actual remaining validity) and falling back to
19
+ * `exp - iat` if the system clock is off. Returns the Cognito default
20
+ * when the token is malformed or lacks a usable `exp`.
21
+ *
22
+ * This function does NOT verify the token's signature — the caller is
23
+ * expected to trust the minting provider (a fresh response from
24
+ * `InitiateAuthCommand`). Use `verifyAndDecode` for untrusted input.
25
+ */
26
+ export function deriveExpiresInFromJwt(jwt, nowSeconds = Math.floor(Date.now() / 1000)) {
27
+ const payload = decodeJwtPayload(jwt);
28
+ if (!payload)
29
+ return DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
30
+ const exp = typeof payload.exp === 'number' ? payload.exp : undefined;
31
+ const iat = typeof payload.iat === 'number' ? payload.iat : undefined;
32
+ if (exp === undefined)
33
+ return DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
34
+ const remaining = exp - nowSeconds;
35
+ if (remaining > 0)
36
+ return remaining;
37
+ // Clock skew fallback — prefer minted lifetime over a negative remaining.
38
+ if (iat !== undefined && exp > iat)
39
+ return exp - iat;
40
+ return DEFAULT_ACCESS_TOKEN_TTL_SECONDS;
41
+ }
42
+ /**
43
+ * Base64url-decode and JSON.parse a JWT payload. Returns `null` on any
44
+ * failure — callers fall back to a default.
45
+ */
46
+ function decodeJwtPayload(jwt) {
47
+ if (typeof jwt !== 'string' || jwt.length === 0)
48
+ return null;
49
+ const parts = jwt.split('.');
50
+ if (parts.length < 2)
51
+ return null;
52
+ try {
53
+ const decoded = Buffer.from(parts[1], 'base64url').toString('utf8');
54
+ const obj = JSON.parse(decoded);
55
+ if (obj && typeof obj === 'object')
56
+ return obj;
57
+ return null;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ //# sourceMappingURL=token-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-utils.js","sourceRoot":"","sources":["../../src/server/token-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAErD;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAW,EACX,aAAqB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAElD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO;QAAE,OAAO,gCAAgC,CAAC;IAEtD,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,gCAAgC,CAAC;IAE/D,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;IACnC,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAEpC,0EAA0E;IAC1E,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,GAAG;QAAE,OAAO,GAAG,GAAG,GAAG,CAAC;IAErD,OAAO,gCAAgC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,GAAW;IACnC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;QAC3C,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,GAA8B,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,21 +1,58 @@
1
1
  /**
2
2
  * JWT verification against a Cognito User Pool's JWKS.
3
3
  *
4
- * Wraps `aws-jwt-verify`'s `CognitoJwtVerifier` so consumers don't take
5
- * a direct dependency on it. The verifier is cached per
6
- * `(userPoolId, tokenUse, clientId)` tuple so the JWKS is fetched once
7
- * per cold start.
4
+ * Wraps `aws-jwt-verify` so consumers don't take a direct dependency on
5
+ * it. Two code paths:
6
+ *
7
+ * 1. **Production** `CognitoJwtVerifier` builds the issuer + JWKS URL
8
+ * from the user-pool id (`https://cognito-idp.<region>.amazonaws.com/<userPoolId>`).
9
+ * 2. **`vk dev` (cognito-local)** — when `endpoint` is set we fall
10
+ * through to the generic `JwtVerifier` with an explicit issuer
11
+ * (`<endpoint>/<userPoolId>`) and `jwksUri`
12
+ * (`<endpoint>/<userPoolId>/.well-known/jwks.json`). The
13
+ * Cognito-specific `token_use` + `client_id` checks are reapplied
14
+ * via `validateCognitoJwtFields`.
15
+ *
16
+ * The verifier is cached per `(userPoolId, tokenUse, clientId, endpoint)`
17
+ * tuple so the JWKS is fetched once per cold start.
8
18
  *
9
19
  * Use this when API Gateway is NOT fronting your Lambda with a Cognito
10
20
  * Authorizer — for example, cookie-based sessions where the Lambda
11
21
  * itself reads the cookie and verifies the token.
12
22
  */
23
+ import { type JsonFetcher } from 'aws-jwt-verify/https';
24
+ import type { Json } from 'aws-jwt-verify/safe-json-parse';
13
25
  export interface VerifyOptions {
14
26
  userPoolId: string;
15
27
  /** App client id(s) the token must have been issued to. */
16
28
  clientId?: string | string[];
17
29
  /** Defaults to `'access'`. Use `'id'` for id tokens. */
18
30
  tokenUse?: 'access' | 'id';
31
+ /**
32
+ * Cognito API endpoint override. When set, JWTs are validated as
33
+ * generic OIDC tokens with `<endpoint>/<userPoolId>` as the expected
34
+ * issuer (Cognito-specific fields are still checked via
35
+ * `validateCognitoJwtFields`). Used by `vk dev` to verify tokens
36
+ * minted by cognito-local; deployed Lambdas leave it undefined.
37
+ */
38
+ endpoint?: string;
39
+ }
40
+ /**
41
+ * `JsonFetcher` that transparently handles `http://` URIs by routing
42
+ * them through Node's `http` module. The library's stock
43
+ * `SimpleJsonFetcher` is hardcoded to `https`-only — see
44
+ * `aws-jwt-verify/dist/esm/https-node.js`'s `https.request` call —
45
+ * which is what we want in production but breaks against
46
+ * cognito-local's plain-HTTP JWKS endpoint and surfaces as
47
+ * `ERR_INVALID_PROTOCOL: Protocol "http:" not supported. Expected
48
+ * "https:"` from inside the verify error path.
49
+ *
50
+ * Exported for direct unit testing.
51
+ */
52
+ export declare class HttpAwareJsonFetcher implements JsonFetcher {
53
+ private readonly httpsFallback;
54
+ constructor(httpsFallback?: JsonFetcher);
55
+ fetch: (uri: string, requestOptions?: Record<string, unknown>, data?: Buffer) => Promise<Json>;
19
56
  }
20
57
  /**
21
58
  * Verify a Cognito-signed JWT and return its claims, or `null` when the
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/server/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AAcD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAiBzC;AAED,iEAAiE;AACjE,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/server/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AAG3D,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgCD;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAqB,YAAW,WAAW;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;gBAEhC,aAAa,GAAE,WAAqC;IAIhE,KAAK,GACH,KAAK,MAAM,EACX,iBAAiB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,OAAO,MAAM,KACZ,OAAO,CAAC,IAAI,CAAC,CA0Bd;CACH;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAyDzC;AAED,iEAAiE;AACjE,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
@@ -1,23 +1,92 @@
1
1
  /**
2
2
  * JWT verification against a Cognito User Pool's JWKS.
3
3
  *
4
- * Wraps `aws-jwt-verify`'s `CognitoJwtVerifier` so consumers don't take
5
- * a direct dependency on it. The verifier is cached per
6
- * `(userPoolId, tokenUse, clientId)` tuple so the JWKS is fetched once
7
- * per cold start.
4
+ * Wraps `aws-jwt-verify` so consumers don't take a direct dependency on
5
+ * it. Two code paths:
6
+ *
7
+ * 1. **Production** `CognitoJwtVerifier` builds the issuer + JWKS URL
8
+ * from the user-pool id (`https://cognito-idp.<region>.amazonaws.com/<userPoolId>`).
9
+ * 2. **`vk dev` (cognito-local)** — when `endpoint` is set we fall
10
+ * through to the generic `JwtVerifier` with an explicit issuer
11
+ * (`<endpoint>/<userPoolId>`) and `jwksUri`
12
+ * (`<endpoint>/<userPoolId>/.well-known/jwks.json`). The
13
+ * Cognito-specific `token_use` + `client_id` checks are reapplied
14
+ * via `validateCognitoJwtFields`.
15
+ *
16
+ * The verifier is cached per `(userPoolId, tokenUse, clientId, endpoint)`
17
+ * tuple so the JWKS is fetched once per cold start.
8
18
  *
9
19
  * Use this when API Gateway is NOT fronting your Lambda with a Cognito
10
20
  * Authorizer — for example, cookie-based sessions where the Lambda
11
21
  * itself reads the cookie and verifies the token.
12
22
  */
13
- import { CognitoJwtVerifier } from 'aws-jwt-verify';
23
+ import http from 'node:http';
24
+ import { CognitoJwtVerifier, JwtRsaVerifier } from 'aws-jwt-verify';
25
+ import { validateCognitoJwtFields } from 'aws-jwt-verify/cognito-verifier';
26
+ import { SimpleJsonFetcher } from 'aws-jwt-verify/https';
27
+ import { SimpleJwksCache } from 'aws-jwt-verify/jwk';
14
28
  const verifierCache = new Map();
15
29
  function cacheKey(opts) {
16
30
  const tokenUse = opts.tokenUse ?? 'access';
17
31
  const clientId = Array.isArray(opts.clientId)
18
32
  ? opts.clientId.join(',')
19
33
  : (opts.clientId ?? 'null');
20
- return `${opts.userPoolId}|${tokenUse}|${clientId}`;
34
+ const endpoint = opts.endpoint ?? '';
35
+ return `${opts.userPoolId}|${tokenUse}|${clientId}|${endpoint}`;
36
+ }
37
+ /**
38
+ * Strip a trailing slash from a URL so subsequent path joins don't end
39
+ * up with `//` — `<endpoint>/<userPoolId>` is the issuer cognito-local
40
+ * emits, and a `//` would silently fail signature checks.
41
+ */
42
+ function trimTrailingSlash(url) {
43
+ return url.endsWith('/') ? url.slice(0, -1) : url;
44
+ }
45
+ /**
46
+ * `JsonFetcher` that transparently handles `http://` URIs by routing
47
+ * them through Node's `http` module. The library's stock
48
+ * `SimpleJsonFetcher` is hardcoded to `https`-only — see
49
+ * `aws-jwt-verify/dist/esm/https-node.js`'s `https.request` call —
50
+ * which is what we want in production but breaks against
51
+ * cognito-local's plain-HTTP JWKS endpoint and surfaces as
52
+ * `ERR_INVALID_PROTOCOL: Protocol "http:" not supported. Expected
53
+ * "https:"` from inside the verify error path.
54
+ *
55
+ * Exported for direct unit testing.
56
+ */
57
+ export class HttpAwareJsonFetcher {
58
+ httpsFallback;
59
+ constructor(httpsFallback = new SimpleJsonFetcher()) {
60
+ this.httpsFallback = httpsFallback;
61
+ }
62
+ fetch = (uri, requestOptions, data) => {
63
+ if (!uri.startsWith('http://')) {
64
+ return this.httpsFallback.fetch(uri, requestOptions, data);
65
+ }
66
+ return new Promise((resolve, reject) => {
67
+ const req = http.get(uri, (res) => {
68
+ const status = res.statusCode ?? 0;
69
+ if (status < 200 || status >= 300) {
70
+ res.resume();
71
+ reject(new Error(`HTTP ${status} fetching ${uri}`));
72
+ return;
73
+ }
74
+ const chunks = [];
75
+ res.on('data', (chunk) => chunks.push(chunk));
76
+ res.on('end', () => {
77
+ try {
78
+ const text = Buffer.concat(chunks).toString('utf-8');
79
+ resolve(JSON.parse(text));
80
+ }
81
+ catch (err) {
82
+ reject(err);
83
+ }
84
+ });
85
+ res.on('error', reject);
86
+ });
87
+ req.on('error', reject);
88
+ });
89
+ };
21
90
  }
22
91
  /**
23
92
  * Verify a Cognito-signed JWT and return its claims, or `null` when the
@@ -29,8 +98,47 @@ function cacheKey(opts) {
29
98
  */
30
99
  export async function verifyAndDecode(token, options) {
31
100
  const key = cacheKey(options);
32
- let verifier = verifierCache.get(key);
33
- if (!verifier) {
101
+ const cached = verifierCache.get(key);
102
+ let verifier;
103
+ if (cached) {
104
+ verifier = cached;
105
+ }
106
+ else if (options.endpoint) {
107
+ // Dev path: cognito-local. Use the generic OIDC verifier with the
108
+ // local issuer + JWKS URL, and reapply Cognito-specific checks
109
+ // (`token_use`, `client_id` / `aud`) via `customJwtCheck`.
110
+ //
111
+ // **HTTP fetcher.** `aws-jwt-verify`'s built-in JWKS fetcher is
112
+ // hardcoded to HTTPS (see `aws-jwt-verify/dist/esm/https-node.js`
113
+ // → `https.request`). cognito-local serves the JWKS over plain
114
+ // HTTP, so we inject `HttpAwareJsonFetcher` via a `SimpleJwksCache`
115
+ // — without it the verifier rejects every token with
116
+ // `ERR_INVALID_PROTOCOL: Protocol "http:" not supported. Expected
117
+ // "https:"` and the middleware silently treats it as "invalid
118
+ // token" → 401 on every authenticated request.
119
+ const base = `${trimTrailingSlash(options.endpoint)}/${options.userPoolId}`;
120
+ verifier = JwtRsaVerifier.create({
121
+ issuer: base,
122
+ // `audience: null` — only Cognito ID tokens carry an `aud` claim;
123
+ // access tokens have `client_id`. `validateCognitoJwtFields`
124
+ // handles both. Setting `audience` here would force-check `aud`
125
+ // and fail access tokens.
126
+ audience: null,
127
+ jwksUri: `${base}/.well-known/jwks.json`,
128
+ customJwtCheck: ({ payload }) => {
129
+ validateCognitoJwtFields(payload, {
130
+ tokenUse: options.tokenUse ?? null,
131
+ clientId: options.clientId ?? null,
132
+ });
133
+ },
134
+ }, {
135
+ jwksCache: new SimpleJwksCache({
136
+ fetcher: new HttpAwareJsonFetcher(),
137
+ }),
138
+ });
139
+ verifierCache.set(key, verifier);
140
+ }
141
+ else {
34
142
  verifier = CognitoJwtVerifier.create({
35
143
  userPoolId: options.userPoolId,
36
144
  tokenUse: options.tokenUse ?? 'access',
@@ -1 +1 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/server/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAYpD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAoB,CAAC;AAElD,SAAS,QAAQ,CAAC,IAAmB;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC3C,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QACzB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;IAC9B,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAa,EACb,OAAsB;IAEtB,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC;YACnC,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,QAAQ;YACtC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAI;SACnC,CAAC,CAAC;QACH,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,OAAO,OAAkC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,6BAA6B;IAC3C,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/server/verify.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venturekit/auth",
3
- "version": "0.0.0-dev.20260429204013",
3
+ "version": "0.0.0-dev.20260430161503",
4
4
  "description": "Authentication and authorization for VentureKit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -29,12 +29,12 @@
29
29
  }
30
30
  },
31
31
  "dependencies": {
32
- "@venturekit/core": "0.0.0-dev.20260429204013",
32
+ "@venturekit/core": "0.0.0-dev.20260430161503",
33
33
  "@aws-sdk/client-cognito-identity-provider": "^3.668.0",
34
34
  "aws-jwt-verify": "^4.0.1"
35
35
  },
36
36
  "peerDependencies": {
37
- "@venturekit/runtime": "0.0.0-dev.20260429204013"
37
+ "@venturekit/runtime": "0.0.0-dev.20260430161503"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@venturekit/runtime": {
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "devDependencies": {
45
- "@venturekit/runtime": "0.0.0-dev.20260429204013",
45
+ "@venturekit/runtime": "0.0.0-dev.20260430161503",
46
46
  "@types/aws-lambda": "^8.10.131",
47
47
  "@types/node": "^25.6.0",
48
48
  "typescript": "^5.3.0"