@iqauth/sdk 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/browser-session.d.mts +1 -2
- package/dist/browser-session.d.ts +1 -2
- package/dist/browser-session.js +89 -68
- package/dist/browser-session.mjs +2 -1
- package/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +13 -2
- package/dist/browser.mjs +2 -2
- package/dist/{chunk-D72UL5HL.mjs → chunk-EKTNEZIH.mjs} +4 -4
- package/dist/{chunk-M4J6BPK7.mjs → chunk-KGEPDXHU.mjs} +10 -1
- package/dist/{chunk-QZB745C2.mjs → chunk-RACIPVLD.mjs} +13 -2
- package/dist/chunk-UNYDG2L4.mjs +209 -0
- package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
- package/dist/{chunk-QEJB7WEQ.mjs → chunk-WQWBJSSS.mjs} +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/{client-DXbHb2ul.d.ts → client-DTX4hNdS.d.ts} +16 -21
- package/dist/{client-Dv4v92Mj.d.mts → client-vdh2a9fJ.d.mts} +16 -21
- package/dist/{doctor-XCI77BQS.mjs → doctor-A5E7LSFW.mjs} +1 -1
- package/dist/{express-BZmF1llh.d.mts → express-A0-dWEMy.d.mts} +1 -1
- package/dist/{express-B4o3P8vK.d.ts → express-Bo_pJKHN.d.ts} +1 -1
- package/dist/express.d.mts +75 -5
- package/dist/express.d.ts +75 -5
- package/dist/express.js +300 -70
- package/dist/express.mjs +208 -7
- package/dist/fastify.js +101 -70
- package/dist/fastify.mjs +8 -6
- package/dist/hono.js +100 -70
- package/dist/hono.mjs +7 -6
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +90 -69
- package/dist/index.mjs +15 -13
- package/dist/mobile.d.mts +1 -2
- package/dist/mobile.d.ts +1 -2
- package/dist/mobile.js +89 -68
- package/dist/mobile.mjs +2 -1
- package/dist/next.d.mts +9 -0
- package/dist/next.d.ts +9 -0
- package/dist/next.js +99 -1616
- package/dist/next.mjs +9 -9
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +13 -2
- package/dist/react.mjs +2 -2
- package/dist/server/handlers.d.mts +2 -0
- package/dist/server/handlers.d.ts +2 -0
- package/dist/server/handlers.js +10 -1
- package/dist/server/handlers.mjs +2 -2
- package/dist/server.d.mts +2 -3
- package/dist/server.d.ts +2 -3
- package/dist/server.js +99 -69
- package/dist/server.mjs +7 -6
- package/dist/service.d.mts +1 -2
- package/dist/service.d.ts +1 -2
- package/dist/service.js +89 -68
- package/dist/service.mjs +2 -1
- package/dist/{signIn-D_kP3v-c.d.mts → signIn-Cd0P4y9d.d.mts} +8 -0
- package/dist/{signIn-BVDTIA_t.d.ts → signIn-DKakyzeu.d.ts} +8 -0
- package/package.json +3 -2
package/dist/next.mjs
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
handleRefresh,
|
|
4
4
|
handleSignout,
|
|
5
5
|
serializeCookie
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KGEPDXHU.mjs";
|
|
7
7
|
import {
|
|
8
8
|
assertPublishableKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-WQWBJSSS.mjs";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
TokensModule
|
|
12
|
+
} from "./chunk-UNYDG2L4.mjs";
|
|
13
13
|
import "./chunk-6I6RM4MN.mjs";
|
|
14
14
|
import "./chunk-Y6FXYEAI.mjs";
|
|
15
15
|
|
|
@@ -59,7 +59,7 @@ function handler(options) {
|
|
|
59
59
|
if (action === "signout") {
|
|
60
60
|
const auth = req.headers.get("authorization");
|
|
61
61
|
const accessToken = auth && auth.replace(/^Bearer /i, "") || readCookieFromHeader(cookieHeader, accessCookie);
|
|
62
|
-
return toResponse(await handleSignout(helperConfig, { accessToken }));
|
|
62
|
+
return toResponse(await handleSignout(helperConfig, { accessToken, ssoCookieHeader: cookieHeader ?? void 0 }));
|
|
63
63
|
}
|
|
64
64
|
return new Response(JSON.stringify({ success: false, error: { code: "NOT_FOUND", message: `Unknown action: ${action}` } }), {
|
|
65
65
|
status: 404,
|
|
@@ -71,7 +71,7 @@ function createMiddleware(options) {
|
|
|
71
71
|
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/next createMiddleware" });
|
|
72
72
|
const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
73
73
|
const accessCookie = options.accessCookieName ?? "iqauth_at";
|
|
74
|
-
const
|
|
74
|
+
const tokens = new TokensModule(issuer);
|
|
75
75
|
return async (req) => {
|
|
76
76
|
const auth = req.headers.get("authorization");
|
|
77
77
|
let token;
|
|
@@ -84,7 +84,7 @@ function createMiddleware(options) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
try {
|
|
87
|
-
await
|
|
87
|
+
await tokens.verify(token);
|
|
88
88
|
return void 0;
|
|
89
89
|
} catch (err) {
|
|
90
90
|
const code = err.code || "TOKEN_INVALID";
|
|
@@ -113,9 +113,9 @@ async function getAuth(options) {
|
|
|
113
113
|
}
|
|
114
114
|
const token = cookieJar?.get(accessCookie)?.value;
|
|
115
115
|
if (!token) return null;
|
|
116
|
-
const
|
|
116
|
+
const tokens = new TokensModule(issuer);
|
|
117
117
|
try {
|
|
118
|
-
return await
|
|
118
|
+
return await tokens.verify(token);
|
|
119
119
|
} catch {
|
|
120
120
|
return null;
|
|
121
121
|
}
|
package/dist/react.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { S as SessionManager, a as SessionSnapshot, b as SignInOptions, c as SignOutOptions, C as CallbackResult } from './signIn-
|
|
4
|
+
import { S as SessionManager, a as SessionSnapshot, b as SignInOptions, c as SignOutOptions, C as CallbackResult } from './signIn-Cd0P4y9d.mjs';
|
|
5
5
|
import { d as SessionUser, J as JwtClaims } from './types-Cxl3bQHt.mjs';
|
|
6
6
|
import './publishableKey-BaR0HoAH.mjs';
|
|
7
7
|
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { S as SessionManager, a as SessionSnapshot, b as SignInOptions, c as SignOutOptions, C as CallbackResult } from './signIn-
|
|
4
|
+
import { S as SessionManager, a as SessionSnapshot, b as SignInOptions, c as SignOutOptions, C as CallbackResult } from './signIn-DKakyzeu.js';
|
|
5
5
|
import { d as SessionUser, J as JwtClaims } from './types-Cxl3bQHt.js';
|
|
6
6
|
import './publishableKey-BaR0HoAH.js';
|
|
7
7
|
|
package/dist/react.js
CHANGED
|
@@ -117,7 +117,7 @@ function assertPublishableKey(raw, opts) {
|
|
|
117
117
|
if (!isValidIssuerUrl(decoded.iss)) {
|
|
118
118
|
throw new IQAuthError(
|
|
119
119
|
"CONFIG_INVALID",
|
|
120
|
-
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console
|
|
120
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console \u2014 the new key will encode a valid issuer URL.`
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
123
|
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
@@ -649,6 +649,7 @@ async function createPkcePair() {
|
|
|
649
649
|
// src/browser/signIn.ts
|
|
650
650
|
var DEFAULT_SIGN_IN_PATH = "/sign-in";
|
|
651
651
|
var DEFAULT_LOGOUT_PATH = "/api/v1/auth/logout";
|
|
652
|
+
var DEFAULT_SSO_LOGOUT_PATH = "/oidc/sso-logout";
|
|
652
653
|
var DEFAULT_TOKEN_PATH = "/oidc/token";
|
|
653
654
|
var DEFAULT_CALLBACK_PATH = "/auth/callback";
|
|
654
655
|
function defaultRedirectUri() {
|
|
@@ -747,11 +748,21 @@ async function handleAuthCallback(manager, options = {}) {
|
|
|
747
748
|
}
|
|
748
749
|
async function signOut(manager, opts = {}) {
|
|
749
750
|
if (!opts.localOnly) {
|
|
751
|
+
const issuer = manager.issuerUrl.replace(/\/$/, "");
|
|
750
752
|
try {
|
|
751
|
-
const url = `${
|
|
753
|
+
const url = `${issuer}${opts.logoutPath ?? DEFAULT_LOGOUT_PATH}`;
|
|
752
754
|
await manager.fetch(url, { method: "POST" }).catch(() => void 0);
|
|
753
755
|
} catch {
|
|
754
756
|
}
|
|
757
|
+
if (opts.endSsoSession !== false) {
|
|
758
|
+
try {
|
|
759
|
+
await fetch(`${issuer}${DEFAULT_SSO_LOGOUT_PATH}`, {
|
|
760
|
+
method: "POST",
|
|
761
|
+
credentials: "include"
|
|
762
|
+
}).catch(() => void 0);
|
|
763
|
+
} catch {
|
|
764
|
+
}
|
|
765
|
+
}
|
|
755
766
|
}
|
|
756
767
|
clearCookie(REFRESH_COOKIE);
|
|
757
768
|
manager.signOutLocal();
|
package/dist/react.mjs
CHANGED
|
@@ -118,6 +118,8 @@ declare function handleRefresh(config: IQAuthHelperConfig, input: {
|
|
|
118
118
|
/** POST /api/iqauth/signout — clear cookies and best-effort revoke at issuer. */
|
|
119
119
|
declare function handleSignout(config: IQAuthHelperConfig, input: {
|
|
120
120
|
accessToken?: string;
|
|
121
|
+
ssoCookieHeader?: string;
|
|
122
|
+
endSsoSession?: boolean;
|
|
121
123
|
}): Promise<HandlerResponse>;
|
|
122
124
|
|
|
123
125
|
export { type HandlerResponse, type IQAuthHelperConfig, type ResolvedConfig as ResolvedIQAuthHelperConfig, type SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie };
|
|
@@ -118,6 +118,8 @@ declare function handleRefresh(config: IQAuthHelperConfig, input: {
|
|
|
118
118
|
/** POST /api/iqauth/signout — clear cookies and best-effort revoke at issuer. */
|
|
119
119
|
declare function handleSignout(config: IQAuthHelperConfig, input: {
|
|
120
120
|
accessToken?: string;
|
|
121
|
+
ssoCookieHeader?: string;
|
|
122
|
+
endSsoSession?: boolean;
|
|
121
123
|
}): Promise<HandlerResponse>;
|
|
122
124
|
|
|
123
125
|
export { type HandlerResponse, type IQAuthHelperConfig, type ResolvedConfig as ResolvedIQAuthHelperConfig, type SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie };
|
package/dist/server/handlers.js
CHANGED
|
@@ -96,7 +96,7 @@ function assertPublishableKey(raw, opts) {
|
|
|
96
96
|
if (!isValidIssuerUrl(decoded.iss)) {
|
|
97
97
|
throw new IQAuthError(
|
|
98
98
|
"CONFIG_INVALID",
|
|
99
|
-
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console
|
|
99
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console \u2014 the new key will encode a valid issuer URL.`
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
@@ -298,6 +298,15 @@ async function handleSignout(config, input) {
|
|
|
298
298
|
} catch {
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
+
if (input.endSsoSession !== false && input.ssoCookieHeader) {
|
|
302
|
+
try {
|
|
303
|
+
await cfg.fetchImpl(`${cfg.issuer}/oidc/sso-logout`, {
|
|
304
|
+
method: "POST",
|
|
305
|
+
headers: { Cookie: input.ssoCookieHeader }
|
|
306
|
+
});
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
301
310
|
return {
|
|
302
311
|
status: 200,
|
|
303
312
|
body: { success: true, data: { signedOut: true } },
|
package/dist/server/handlers.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
handleRefresh,
|
|
4
4
|
handleSignout,
|
|
5
5
|
serializeCookie
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-KGEPDXHU.mjs";
|
|
7
|
+
import "../chunk-WQWBJSSS.mjs";
|
|
8
8
|
import "../chunk-6I6RM4MN.mjs";
|
|
9
9
|
import "../chunk-Y6FXYEAI.mjs";
|
|
10
10
|
export {
|
package/dist/server.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.mjs';
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
2
|
+
import { I as IQAuthClient } from './client-vdh2a9fJ.mjs';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
|
|
4
|
-
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-
|
|
4
|
+
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-A0-dWEMy.mjs';
|
|
5
5
|
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.mjs';
|
|
6
|
-
import 'jsonwebtoken';
|
|
7
6
|
|
|
8
7
|
declare class ServerIQAuthClient extends IQAuthClient {
|
|
9
8
|
constructor(config: IQAuthTokenClientConfig);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.js';
|
|
2
|
-
import { I as IQAuthClient } from './client-
|
|
2
|
+
import { I as IQAuthClient } from './client-DTX4hNdS.js';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
|
|
4
|
-
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-
|
|
4
|
+
export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-Bo_pJKHN.js';
|
|
5
5
|
export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.js';
|
|
6
|
-
import 'jsonwebtoken';
|
|
7
6
|
|
|
8
7
|
declare class ServerIQAuthClient extends IQAuthClient {
|
|
9
8
|
constructor(config: IQAuthTokenClientConfig);
|
package/dist/server.js
CHANGED
|
@@ -453,8 +453,7 @@ function parseMfaResponse(data, browserSessionMode) {
|
|
|
453
453
|
}
|
|
454
454
|
|
|
455
455
|
// src/modules/tokens.ts
|
|
456
|
-
var
|
|
457
|
-
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
456
|
+
var import_jose = require("jose");
|
|
458
457
|
var JWKS_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
459
458
|
var DEFAULT_TOKEN_ISSUER = [
|
|
460
459
|
"https://auth.dispositioniq.com",
|
|
@@ -467,6 +466,24 @@ var DEFAULT_TOKEN_AUDIENCE = [
|
|
|
467
466
|
"iqvalidate"
|
|
468
467
|
];
|
|
469
468
|
var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
469
|
+
function decodeProtectedHeader(token) {
|
|
470
|
+
const parts = token.split(".");
|
|
471
|
+
if (parts.length < 2) return null;
|
|
472
|
+
try {
|
|
473
|
+
const padded = parts[0] + "=".repeat((4 - parts[0].length % 4) % 4);
|
|
474
|
+
const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
475
|
+
let json;
|
|
476
|
+
if (typeof atob === "function") {
|
|
477
|
+
json = atob(b64);
|
|
478
|
+
} else {
|
|
479
|
+
const { Buffer: Buffer2 } = require("buffer");
|
|
480
|
+
json = Buffer2.from(b64, "base64").toString("utf8");
|
|
481
|
+
}
|
|
482
|
+
return JSON.parse(json);
|
|
483
|
+
} catch {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
470
487
|
var TokensModule = class {
|
|
471
488
|
constructor(baseUrl, options = {}) {
|
|
472
489
|
this.jwksCache = null;
|
|
@@ -477,49 +494,49 @@ var TokensModule = class {
|
|
|
477
494
|
this.defaultClockTolerance = options.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
|
|
478
495
|
}
|
|
479
496
|
/**
|
|
480
|
-
* Verify a JWT access token using RS256 via JWKS from
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
* clock tolerance default to client config but can be overridden per call.
|
|
497
|
+
* Verify a JWT access token using RS256/ES256 via JWKS from
|
|
498
|
+
* `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
|
|
499
|
+
* Node, browser, and edge runtimes alike — no `node:crypto` dependency.
|
|
500
|
+
* Caches JWKS for 1 hour and refetches once on unknown `kid`.
|
|
485
501
|
*/
|
|
486
502
|
async verify(token, options = {}) {
|
|
487
|
-
const
|
|
488
|
-
if (!
|
|
503
|
+
const header = decodeProtectedHeader(token);
|
|
504
|
+
if (!header) {
|
|
489
505
|
throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
|
|
490
506
|
}
|
|
491
|
-
const kid =
|
|
507
|
+
const kid = header.kid;
|
|
492
508
|
if (!kid) {
|
|
493
509
|
throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
|
|
494
510
|
}
|
|
495
|
-
let
|
|
496
|
-
if (!
|
|
497
|
-
|
|
498
|
-
|
|
511
|
+
let cache = await this.ensureCache();
|
|
512
|
+
if (!cache.byKid.has(kid)) {
|
|
513
|
+
this.jwksCache = null;
|
|
514
|
+
cache = await this.ensureCache();
|
|
499
515
|
}
|
|
500
|
-
if (!
|
|
516
|
+
if (!cache.byKid.has(kid)) {
|
|
501
517
|
throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
|
|
502
518
|
}
|
|
503
519
|
const issuer = options.issuer ?? this.defaultIssuer;
|
|
504
520
|
const audience = options.audience ?? this.defaultAudience;
|
|
505
521
|
const clockTolerance = options.clockTolerance ?? this.defaultClockTolerance;
|
|
506
|
-
const algorithms = options.algorithms ?? ["RS256"];
|
|
522
|
+
const algorithms = options.algorithms ?? ["RS256", "ES256"];
|
|
523
|
+
const verifyOptions = {
|
|
524
|
+
algorithms,
|
|
525
|
+
clockTolerance,
|
|
526
|
+
issuer,
|
|
527
|
+
audience
|
|
528
|
+
};
|
|
507
529
|
try {
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
clockTolerance,
|
|
511
|
-
// The jsonwebtoken types insist on tuple types for arrays; runtime
|
|
512
|
-
// accepts plain string[] so we cast to satisfy the compiler.
|
|
513
|
-
issuer,
|
|
514
|
-
audience
|
|
515
|
-
};
|
|
516
|
-
const verified = import_jsonwebtoken.default.verify(token, publicKey, verifyOptions);
|
|
517
|
-
return verified;
|
|
530
|
+
const { payload } = await (0, import_jose.jwtVerify)(token, cache.verifier, verifyOptions);
|
|
531
|
+
return payload;
|
|
518
532
|
} catch (err) {
|
|
533
|
+
if (err instanceof import_jose.errors.JWTExpired) {
|
|
534
|
+
throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
|
|
535
|
+
}
|
|
536
|
+
if (err instanceof import_jose.errors.JOSEError) {
|
|
537
|
+
throw new IQAuthError("TOKEN_INVALID", err.message);
|
|
538
|
+
}
|
|
519
539
|
if (err instanceof Error) {
|
|
520
|
-
if (err.name === "TokenExpiredError") {
|
|
521
|
-
throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
|
|
522
|
-
}
|
|
523
540
|
throw new IQAuthError("TOKEN_INVALID", err.message);
|
|
524
541
|
}
|
|
525
542
|
throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
|
|
@@ -527,29 +544,40 @@ var TokensModule = class {
|
|
|
527
544
|
}
|
|
528
545
|
/**
|
|
529
546
|
* Decode a JWT without verification. Returns null if malformed.
|
|
530
|
-
*
|
|
531
|
-
* @remarks Local decode only — no network call
|
|
532
547
|
*/
|
|
533
548
|
decode(token) {
|
|
534
|
-
|
|
535
|
-
|
|
549
|
+
try {
|
|
550
|
+
const parts = token.split(".");
|
|
551
|
+
if (parts.length < 2) return null;
|
|
552
|
+
const payload = parts[1];
|
|
553
|
+
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
554
|
+
const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
555
|
+
let json;
|
|
556
|
+
if (typeof atob === "function") {
|
|
557
|
+
json = atob(b64);
|
|
558
|
+
} else {
|
|
559
|
+
const { Buffer: Buffer2 } = require("buffer");
|
|
560
|
+
json = Buffer2.from(b64, "base64").toString("utf8");
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
json = decodeURIComponent(escape(json));
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
const claims = JSON.parse(json);
|
|
567
|
+
if (!claims || typeof claims !== "object") return null;
|
|
568
|
+
return claims;
|
|
569
|
+
} catch {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
536
572
|
}
|
|
537
|
-
/**
|
|
538
|
-
* Check if a token is expired based on the `exp` claim.
|
|
539
|
-
*
|
|
540
|
-
* @remarks Local check only — no network call
|
|
541
|
-
*/
|
|
573
|
+
/** Check if a token is expired based on the `exp` claim. */
|
|
542
574
|
isExpired(token) {
|
|
543
575
|
const claims = this.decode(token);
|
|
544
576
|
if (!claims?.exp) return true;
|
|
545
577
|
const now = Math.floor(Date.now() / 1e3);
|
|
546
578
|
return claims.exp <= now;
|
|
547
579
|
}
|
|
548
|
-
/**
|
|
549
|
-
* Get the claims from a token without verification.
|
|
550
|
-
*
|
|
551
|
-
* @remarks Local decode only — no network call
|
|
552
|
-
*/
|
|
580
|
+
/** Get the claims from a token without verification. */
|
|
553
581
|
getClaims(token) {
|
|
554
582
|
const claims = this.decode(token);
|
|
555
583
|
if (!claims) {
|
|
@@ -557,11 +585,15 @@ var TokensModule = class {
|
|
|
557
585
|
}
|
|
558
586
|
return claims;
|
|
559
587
|
}
|
|
560
|
-
async
|
|
561
|
-
if (
|
|
562
|
-
|
|
588
|
+
async ensureCache() {
|
|
589
|
+
if (this.jwksCache && Date.now() - this.jwksCache.fetchedAt <= JWKS_CACHE_TTL_MS) {
|
|
590
|
+
return this.jwksCache;
|
|
591
|
+
}
|
|
592
|
+
await this.refreshJwks();
|
|
593
|
+
if (!this.jwksCache) {
|
|
594
|
+
throw new IQAuthError("INTERNAL_ERROR", "JWKS cache unavailable after refresh");
|
|
563
595
|
}
|
|
564
|
-
return this.jwksCache
|
|
596
|
+
return this.jwksCache;
|
|
565
597
|
}
|
|
566
598
|
async refreshJwks() {
|
|
567
599
|
if (this.inFlightRefresh) {
|
|
@@ -588,35 +620,24 @@ var TokensModule = class {
|
|
|
588
620
|
"Malformed JWKS response: expected { keys: [...] }"
|
|
589
621
|
);
|
|
590
622
|
}
|
|
591
|
-
const
|
|
623
|
+
const byKid = /* @__PURE__ */ new Set();
|
|
592
624
|
for (const key of jwks.keys) {
|
|
593
|
-
if (!key || typeof key.kid !== "string" || typeof key.n !== "string" || typeof key.e !== "string") {
|
|
625
|
+
if (!key || typeof key.kid !== "string" || typeof key.n !== "string" && typeof key.x !== "string" || key.kty === "RSA" && (typeof key.n !== "string" || typeof key.e !== "string")) {
|
|
594
626
|
throw new IQAuthError(
|
|
595
627
|
"INTERNAL_ERROR",
|
|
596
628
|
"Malformed JWKS response: key missing required fields"
|
|
597
629
|
);
|
|
598
630
|
}
|
|
599
|
-
|
|
600
|
-
keys.set(key.kid, pem);
|
|
631
|
+
byKid.add(key.kid);
|
|
601
632
|
}
|
|
602
|
-
|
|
633
|
+
const verifier = (0, import_jose.createLocalJWKSet)({ keys: jwks.keys });
|
|
634
|
+
this.jwksCache = { raw: jwks.keys, byKid, verifier, fetchedAt: Date.now() };
|
|
603
635
|
} finally {
|
|
604
636
|
this.inFlightRefresh = null;
|
|
605
637
|
}
|
|
606
638
|
})();
|
|
607
639
|
return this.inFlightRefresh;
|
|
608
640
|
}
|
|
609
|
-
jwkToPem(jwk) {
|
|
610
|
-
const keyObject = import_crypto.default.createPublicKey({
|
|
611
|
-
key: {
|
|
612
|
-
kty: jwk.kty,
|
|
613
|
-
n: jwk.n,
|
|
614
|
-
e: jwk.e
|
|
615
|
-
},
|
|
616
|
-
format: "jwk"
|
|
617
|
-
});
|
|
618
|
-
return keyObject.export({ type: "spki", format: "pem" });
|
|
619
|
-
}
|
|
620
641
|
/** @internal Exposed for testing — clears JWKS cache */
|
|
621
642
|
clearCache() {
|
|
622
643
|
this.jwksCache = null;
|
|
@@ -824,7 +845,7 @@ var PermissionsModule = class {
|
|
|
824
845
|
};
|
|
825
846
|
|
|
826
847
|
// src/modules/oidc.ts
|
|
827
|
-
var
|
|
848
|
+
var import_crypto = __toESM(require("crypto"));
|
|
828
849
|
var InMemoryOidcStateStore = class {
|
|
829
850
|
constructor() {
|
|
830
851
|
this.map = /* @__PURE__ */ new Map();
|
|
@@ -905,12 +926,12 @@ var OidcModule = class {
|
|
|
905
926
|
* ready to redirect the user to.
|
|
906
927
|
*/
|
|
907
928
|
async createAuthRequest(params) {
|
|
908
|
-
const codeVerifier = base64UrlEncode(
|
|
929
|
+
const codeVerifier = base64UrlEncode(import_crypto.default.randomBytes(32));
|
|
909
930
|
const codeChallenge = base64UrlEncode(
|
|
910
|
-
|
|
931
|
+
import_crypto.default.createHash("sha256").update(codeVerifier).digest()
|
|
911
932
|
);
|
|
912
|
-
const state = base64UrlEncode(
|
|
913
|
-
const nonce = base64UrlEncode(
|
|
933
|
+
const state = base64UrlEncode(import_crypto.default.randomBytes(16));
|
|
934
|
+
const nonce = base64UrlEncode(import_crypto.default.randomBytes(16));
|
|
914
935
|
await this.stateStore.set(state, {
|
|
915
936
|
codeVerifier,
|
|
916
937
|
state,
|
|
@@ -1858,7 +1879,7 @@ function assertPublishableKey(raw, opts) {
|
|
|
1858
1879
|
if (!isValidIssuerUrl(decoded.iss)) {
|
|
1859
1880
|
throw new IQAuthError(
|
|
1860
1881
|
"CONFIG_INVALID",
|
|
1861
|
-
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console
|
|
1882
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console \u2014 the new key will encode a valid issuer URL.`
|
|
1862
1883
|
);
|
|
1863
1884
|
}
|
|
1864
1885
|
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
@@ -2218,6 +2239,15 @@ async function handleSignout(config, input) {
|
|
|
2218
2239
|
} catch {
|
|
2219
2240
|
}
|
|
2220
2241
|
}
|
|
2242
|
+
if (input.endSsoSession !== false && input.ssoCookieHeader) {
|
|
2243
|
+
try {
|
|
2244
|
+
await cfg.fetchImpl(`${cfg.issuer}/oidc/sso-logout`, {
|
|
2245
|
+
method: "POST",
|
|
2246
|
+
headers: { Cookie: input.ssoCookieHeader }
|
|
2247
|
+
});
|
|
2248
|
+
} catch {
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2221
2251
|
return {
|
|
2222
2252
|
status: 200,
|
|
2223
2253
|
body: { success: true, data: { signedOut: true } },
|
package/dist/server.mjs
CHANGED
|
@@ -2,17 +2,18 @@ import {
|
|
|
2
2
|
DEFAULT_ACCESS_COOKIE,
|
|
3
3
|
DEFAULT_REFRESH_COOKIE,
|
|
4
4
|
iqAuthMiddleware
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EKTNEZIH.mjs";
|
|
6
|
+
import {
|
|
7
|
+
IQAuthClient
|
|
8
|
+
} from "./chunk-W3F4JYGP.mjs";
|
|
6
9
|
import {
|
|
7
10
|
handleCallback,
|
|
8
11
|
handleRefresh,
|
|
9
12
|
handleSignout,
|
|
10
13
|
serializeCookie
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import
|
|
14
|
-
IQAuthClient
|
|
15
|
-
} from "./chunk-MDUHPQMM.mjs";
|
|
14
|
+
} from "./chunk-KGEPDXHU.mjs";
|
|
15
|
+
import "./chunk-WQWBJSSS.mjs";
|
|
16
|
+
import "./chunk-UNYDG2L4.mjs";
|
|
16
17
|
import {
|
|
17
18
|
ErrorCodes,
|
|
18
19
|
IQAuthError
|
package/dist/service.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { I as IQAuthClient } from './client-
|
|
1
|
+
import { I as IQAuthClient } from './client-vdh2a9fJ.mjs';
|
|
2
2
|
import { b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.mjs';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
|
|
4
|
-
import 'jsonwebtoken';
|
|
5
4
|
|
|
6
5
|
declare class ServiceIQAuthClient extends IQAuthClient {
|
|
7
6
|
constructor(config: IQAuthTokenClientConfig);
|
package/dist/service.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { I as IQAuthClient } from './client-
|
|
1
|
+
import { I as IQAuthClient } from './client-DTX4hNdS.js';
|
|
2
2
|
import { b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.js';
|
|
3
3
|
export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
|
|
4
|
-
import 'jsonwebtoken';
|
|
5
4
|
|
|
6
5
|
declare class ServiceIQAuthClient extends IQAuthClient {
|
|
7
6
|
constructor(config: IQAuthTokenClientConfig);
|