@schemavaults/auth-client-sdk 0.17.2 → 0.18.6
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 +35 -0
- package/dist/auth-client.d.ts +9 -1
- package/dist/auth-client.js +22 -3
- package/dist/auth-client.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/lib/acquire-access-token.js +1 -1
- package/dist/lib/acquire-access-token.js.map +1 -1
- package/dist/lib/credentials-schema/credentials-schema.d.ts +5 -26
- package/dist/lib/exchange-auth-tokens.d.ts +16 -6
- package/dist/lib/exchange-auth-tokens.js +119 -97
- package/dist/lib/exchange-auth-tokens.js.map +1 -1
- package/dist/lib/handle-successful-authentication.d.ts +20 -6
- package/dist/lib/handle-successful-authentication.js +170 -131
- package/dist/lib/handle-successful-authentication.js.map +1 -1
- package/dist/lib/handle-successful-exchange-auth-tokens-response.d.ts +11 -7
- package/dist/lib/handle-successful-exchange-auth-tokens-response.js +19 -28
- package/dist/lib/handle-successful-exchange-auth-tokens-response.js.map +1 -1
- package/dist/lib/mfa/regenerate-recovery-codes.js +1 -1
- package/dist/lib/mfa/regenerate-recovery-codes.js.map +1 -1
- package/dist/lib/oidc/index.d.ts +9 -0
- package/dist/lib/oidc/index.js +7 -0
- package/dist/lib/oidc/index.js.map +1 -0
- package/dist/lib/oidc/is-insecure-transport-allowed.d.ts +8 -0
- package/dist/lib/oidc/is-insecure-transport-allowed.js +14 -0
- package/dist/lib/oidc/is-insecure-transport-allowed.js.map +1 -0
- package/dist/lib/oidc/normalize-oidc-issuer.d.ts +8 -0
- package/dist/lib/oidc/normalize-oidc-issuer.js +19 -0
- package/dist/lib/oidc/normalize-oidc-issuer.js.map +1 -0
- package/dist/lib/oidc/oidc-client-configuration.d.ts +43 -0
- package/dist/lib/oidc/oidc-client-configuration.js +68 -0
- package/dist/lib/oidc/oidc-client-configuration.js.map +1 -0
- package/dist/lib/oidc/oidc-token-error.d.ts +16 -0
- package/dist/lib/oidc/oidc-token-error.js +54 -0
- package/dist/lib/oidc/oidc-token-error.js.map +1 -0
- package/dist/lib/oidc/token-endpoint-response-to-tokens-record.d.ts +42 -0
- package/dist/lib/oidc/token-endpoint-response-to-tokens-record.js +72 -0
- package/dist/lib/oidc/token-endpoint-response-to-tokens-record.js.map +1 -0
- package/dist/lib/oidc/uid-from-oidc-sub-claim.d.ts +13 -0
- package/dist/lib/oidc/uid-from-oidc-sub-claim.js +32 -0
- package/dist/lib/oidc/uid-from-oidc-sub-claim.js.map +1 -0
- package/dist/lib/send-authenticate-request.js +1 -1
- package/dist/types/ISchemaVaultsAuthClient.d.ts +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// is-insecure-transport-allowed.ts
|
|
2
|
+
//
|
|
3
|
+
// The single place that decides which app environments may talk to an
|
|
4
|
+
// auth server over cleartext HTTP.
|
|
5
|
+
/**
|
|
6
|
+
* Whether `environment` may talk to an auth server over cleartext HTTP.
|
|
7
|
+
* Mirrors `SchemaVaultsAuthClient.secure`: development and test are the
|
|
8
|
+
* only non-TLS environments; staging and production always require HTTPS.
|
|
9
|
+
*/
|
|
10
|
+
export function isInsecureTransportAllowed(environment) {
|
|
11
|
+
return environment === "development" || environment === "test";
|
|
12
|
+
}
|
|
13
|
+
export default isInsecureTransportAllowed;
|
|
14
|
+
//# sourceMappingURL=is-insecure-transport-allowed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-insecure-transport-allowed.js","sourceRoot":"","sources":["../../../src/lib/oidc/is-insecure-transport-allowed.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,sEAAsE;AACtE,mCAAmC;AAInC;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,WAAuC;IAEvC,OAAO,WAAW,KAAK,aAAa,IAAI,WAAW,KAAK,MAAM,CAAC;AACjE,CAAC;AAED,eAAe,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips a trailing slash so the issuer matches `getAuthServerUrl()` —
|
|
3
|
+
* and therefore the `iss` claim of the id_tokens the auth server mints
|
|
4
|
+
* and the RFC 9207 `iss` authorization-response parameter, both of which
|
|
5
|
+
* are compared byte-for-byte by openid-client.
|
|
6
|
+
*/
|
|
7
|
+
export declare function normalizeOidcIssuer(auth_server_url: string): string;
|
|
8
|
+
export default normalizeOidcIssuer;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// normalize-oidc-issuer.ts
|
|
2
|
+
//
|
|
3
|
+
// Canonicalizes an auth server URL into its OIDC issuer identifier.
|
|
4
|
+
/**
|
|
5
|
+
* Strips a trailing slash so the issuer matches `getAuthServerUrl()` —
|
|
6
|
+
* and therefore the `iss` claim of the id_tokens the auth server mints
|
|
7
|
+
* and the RFC 9207 `iss` authorization-response parameter, both of which
|
|
8
|
+
* are compared byte-for-byte by openid-client.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeOidcIssuer(auth_server_url) {
|
|
11
|
+
if (typeof auth_server_url !== "string" || auth_server_url.length === 0) {
|
|
12
|
+
throw new TypeError("Expected 'auth_server_url' to be a non-empty string!");
|
|
13
|
+
}
|
|
14
|
+
return auth_server_url.endsWith("/")
|
|
15
|
+
? auth_server_url.slice(0, -1)
|
|
16
|
+
: auth_server_url;
|
|
17
|
+
}
|
|
18
|
+
export default normalizeOidcIssuer;
|
|
19
|
+
//# sourceMappingURL=normalize-oidc-issuer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-oidc-issuer.js","sourceRoot":"","sources":["../../../src/lib/oidc/normalize-oidc-issuer.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,EAAE;AACF,oEAAoE;AAEpE;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,eAAuB;IACzD,IAAI,OAAO,eAAe,KAAK,QAAQ,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,SAAS,CAAC,sDAAsD,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC;QAClC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC,eAAe,CAAC;AACtB,CAAC;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as oidc from "openid-client";
|
|
2
|
+
import { type SchemaVaultsAppEnvironment } from "@schemavaults/app-definitions";
|
|
3
|
+
import type { ISchemaVaultsAuthClientAdapter } from "../../types/ISchemaVaultsAuthClientAdapter";
|
|
4
|
+
export interface CreateOidcClientConfigurationOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The auth server URL — its OIDC issuer identifier, byte-identical to
|
|
7
|
+
* the `iss` claim of the id_tokens it mints.
|
|
8
|
+
*/
|
|
9
|
+
auth_server_url: string;
|
|
10
|
+
/** The OAuth2 client_id: this client application's app id. */
|
|
11
|
+
client_app_id: string;
|
|
12
|
+
/**
|
|
13
|
+
* The app environment this client runs in. Decides whether a plain-HTTP
|
|
14
|
+
* auth server is tolerated: only `development` and `test` may talk to
|
|
15
|
+
* the token endpoint without TLS.
|
|
16
|
+
*/
|
|
17
|
+
environment: SchemaVaultsAppEnvironment;
|
|
18
|
+
/** Platform adapter supplying the fetch implementation. */
|
|
19
|
+
adapter: ISchemaVaultsAuthClientAdapter;
|
|
20
|
+
/** Timeout for token-endpoint requests, in seconds (default 30). */
|
|
21
|
+
timeout_seconds?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates the openid-client Configuration for this SDK instance.
|
|
25
|
+
*
|
|
26
|
+
* - Server metadata: the auth server's discovery document for
|
|
27
|
+
* `auth_server_url`, from the shared builder (see module comment).
|
|
28
|
+
* - Public client: PKCE S256 is the binding, token endpoint auth method
|
|
29
|
+
* `none` (the platform never puts client secrets in browsers).
|
|
30
|
+
* - HTTP requests go through the adapter's fetch so each platform
|
|
31
|
+
* (browser, Node, React Native) supplies its own implementation.
|
|
32
|
+
* Token-endpoint requests are sent with `credentials: "include"` so
|
|
33
|
+
* the auth server's HTTP-only refresh-token cookie travels with the
|
|
34
|
+
* refresh grant and the `Set-Cookie` on the response is honored.
|
|
35
|
+
* - Plain-HTTP issuers (local dev / the docker-compose test network)
|
|
36
|
+
* are allowed ONLY in the `development` and `test` environments;
|
|
37
|
+
* openid-client refuses non-TLS endpoints otherwise, and this function
|
|
38
|
+
* throws before building such a configuration in any other environment
|
|
39
|
+
* so a misconfigured `auth_server_url` can never leak tokens over
|
|
40
|
+
* cleartext in staging or production.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createOidcClientConfiguration({ auth_server_url, client_app_id, environment, adapter, timeout_seconds, }: CreateOidcClientConfigurationOptions): oidc.Configuration;
|
|
43
|
+
export default createOidcClientConfiguration;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// oidc-client-configuration.ts
|
|
2
|
+
//
|
|
3
|
+
// Builds the `openid-client` Configuration the SDK uses to talk to the
|
|
4
|
+
// auth server's standard OIDC surface (/api/oidc/*). The server metadata
|
|
5
|
+
// is the auth server's own discovery document, built locally from the
|
|
6
|
+
// shared `buildOidcProviderMetadata` in @schemavaults/auth-common — the
|
|
7
|
+
// very function the server serves at /.well-known/openid-configuration —
|
|
8
|
+
// so no discovery round trip is needed and the SDK cannot drift from
|
|
9
|
+
// what the server advertises.
|
|
10
|
+
import * as oidc from "openid-client";
|
|
11
|
+
import { isValidSchemaVaultsAppEnvironment, } from "@schemavaults/app-definitions";
|
|
12
|
+
import { buildOidcProviderMetadata } from "@schemavaults/auth-common";
|
|
13
|
+
import { isInsecureTransportAllowed } from "./is-insecure-transport-allowed";
|
|
14
|
+
import { normalizeOidcIssuer } from "./normalize-oidc-issuer";
|
|
15
|
+
/**
|
|
16
|
+
* Creates the openid-client Configuration for this SDK instance.
|
|
17
|
+
*
|
|
18
|
+
* - Server metadata: the auth server's discovery document for
|
|
19
|
+
* `auth_server_url`, from the shared builder (see module comment).
|
|
20
|
+
* - Public client: PKCE S256 is the binding, token endpoint auth method
|
|
21
|
+
* `none` (the platform never puts client secrets in browsers).
|
|
22
|
+
* - HTTP requests go through the adapter's fetch so each platform
|
|
23
|
+
* (browser, Node, React Native) supplies its own implementation.
|
|
24
|
+
* Token-endpoint requests are sent with `credentials: "include"` so
|
|
25
|
+
* the auth server's HTTP-only refresh-token cookie travels with the
|
|
26
|
+
* refresh grant and the `Set-Cookie` on the response is honored.
|
|
27
|
+
* - Plain-HTTP issuers (local dev / the docker-compose test network)
|
|
28
|
+
* are allowed ONLY in the `development` and `test` environments;
|
|
29
|
+
* openid-client refuses non-TLS endpoints otherwise, and this function
|
|
30
|
+
* throws before building such a configuration in any other environment
|
|
31
|
+
* so a misconfigured `auth_server_url` can never leak tokens over
|
|
32
|
+
* cleartext in staging or production.
|
|
33
|
+
*/
|
|
34
|
+
export function createOidcClientConfiguration({ auth_server_url, client_app_id, environment, adapter, timeout_seconds, }) {
|
|
35
|
+
if (!isValidSchemaVaultsAppEnvironment(environment)) {
|
|
36
|
+
throw new TypeError(`Invalid app environment '${String(environment)}' for the OIDC client configuration!`);
|
|
37
|
+
}
|
|
38
|
+
const issuer = normalizeOidcIssuer(auth_server_url);
|
|
39
|
+
const insecure_issuer = issuer.startsWith("http://");
|
|
40
|
+
if (insecure_issuer && !isInsecureTransportAllowed(environment)) {
|
|
41
|
+
throw new Error(`Refusing to use the plain-HTTP auth server '${issuer}' in the '${environment}' environment: ` +
|
|
42
|
+
"the auth server URL must use HTTPS outside of development and test.");
|
|
43
|
+
}
|
|
44
|
+
const server = buildOidcProviderMetadata(issuer);
|
|
45
|
+
const token_endpoint = server.token_endpoint;
|
|
46
|
+
const config = new oidc.Configuration(server, client_app_id, { token_endpoint_auth_method: "none" }, oidc.None());
|
|
47
|
+
if (insecure_issuer) {
|
|
48
|
+
// Reachable only in development/test (asserted above).
|
|
49
|
+
oidc.allowInsecureRequests(config);
|
|
50
|
+
}
|
|
51
|
+
if (typeof timeout_seconds === "number") {
|
|
52
|
+
config.timeout = timeout_seconds;
|
|
53
|
+
}
|
|
54
|
+
config[oidc.customFetch] = async (url, options) => {
|
|
55
|
+
const isTokenEndpoint = url === token_endpoint;
|
|
56
|
+
return await adapter.fetch(url, {
|
|
57
|
+
method: options.method,
|
|
58
|
+
headers: options.headers,
|
|
59
|
+
body: options.body,
|
|
60
|
+
redirect: options.redirect,
|
|
61
|
+
signal: options.signal,
|
|
62
|
+
credentials: isTokenEndpoint ? "include" : "same-origin",
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
return config;
|
|
66
|
+
}
|
|
67
|
+
export default createOidcClientConfiguration;
|
|
68
|
+
//# sourceMappingURL=oidc-client-configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-client-configuration.js","sourceRoot":"","sources":["../../../src/lib/oidc/oidc-client-configuration.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,sEAAsE;AACtE,wEAAwE;AACxE,yEAAyE;AACzE,qEAAqE;AACrE,8BAA8B;AAE9B,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AACtC,OAAO,EACL,iCAAiC,GAElC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAsB9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,6BAA6B,CAAC,EAC5C,eAAe,EACf,aAAa,EACb,WAAW,EACX,OAAO,EACP,eAAe,GACsB;IACrC,IAAI,CAAC,iCAAiC,CAAC,WAAW,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,SAAS,CACjB,4BAA4B,MAAM,CAAC,WAAW,CAAC,sCAAsC,CACtF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAW,mBAAmB,CAAC,eAAe,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAY,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC9D,IAAI,eAAe,IAAI,CAAC,0BAA0B,CAAC,WAAW,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,+CAA+C,MAAM,aAAa,WAAW,iBAAiB;YAC5F,qEAAqE,CACxE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAwB,yBAAyB,CAAC,MAAM,CAAC,CAAC;IACtE,MAAM,cAAc,GAAW,MAAM,CAAC,cAAwB,CAAC;IAE/D,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CACnC,MAAM,EACN,aAAa,EACb,EAAE,0BAA0B,EAAE,MAAM,EAAE,EACtC,IAAI,CAAC,IAAI,EAAE,CACZ,CAAC;IAEF,IAAI,eAAe,EAAE,CAAC;QACpB,uDAAuD;QACvD,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,OAAO,GAAG,eAAe,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,KAAK,EAC9B,GAAW,EACX,OAAgC,EACb,EAAE;QACrB,MAAM,eAAe,GAAY,GAAG,KAAK,cAAc,CAAC;QACxD,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;SACzD,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,eAAe,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ClassifiedOidcTokenError {
|
|
2
|
+
/** RFC 6749 §5.2 error code when the server returned one. */
|
|
3
|
+
error: string | null;
|
|
4
|
+
error_description: string | null;
|
|
5
|
+
status: number | null;
|
|
6
|
+
/**
|
|
7
|
+
* True when the auth server rejected the grant itself (invalid,
|
|
8
|
+
* expired, revoked, or re-used refresh token / authorization code, or
|
|
9
|
+
* the client was rejected outright): the local session cannot be
|
|
10
|
+
* recovered and should be cleared.
|
|
11
|
+
*/
|
|
12
|
+
session_lost: boolean;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function classifyOidcTokenError(e: unknown): ClassifiedOidcTokenError;
|
|
16
|
+
export default classifyOidcTokenError;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// oidc-token-error.ts
|
|
2
|
+
//
|
|
3
|
+
// Classifies failures thrown by openid-client's grant helpers so the
|
|
4
|
+
// SDK can tell "the session is gone" (log the user out) apart from
|
|
5
|
+
// transient / configuration errors.
|
|
6
|
+
import * as oidc from "openid-client";
|
|
7
|
+
const SESSION_LOST_ERROR_CODES = new Set([
|
|
8
|
+
"invalid_grant",
|
|
9
|
+
"invalid_client",
|
|
10
|
+
"unauthorized_client",
|
|
11
|
+
]);
|
|
12
|
+
export function classifyOidcTokenError(e) {
|
|
13
|
+
if (e instanceof oidc.ResponseBodyError) {
|
|
14
|
+
const error = e.error;
|
|
15
|
+
const error_description = e.error_description ?? null;
|
|
16
|
+
const status = e.status;
|
|
17
|
+
const session_lost = SESSION_LOST_ERROR_CODES.has(error) || status === 401 || status === 403;
|
|
18
|
+
return {
|
|
19
|
+
error,
|
|
20
|
+
error_description,
|
|
21
|
+
status,
|
|
22
|
+
session_lost,
|
|
23
|
+
message: `${error}${error_description ? `: ${error_description}` : ""} (HTTP ${status})`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (e instanceof oidc.WWWAuthenticateChallengeError) {
|
|
27
|
+
const status = e.status;
|
|
28
|
+
return {
|
|
29
|
+
error: null,
|
|
30
|
+
error_description: null,
|
|
31
|
+
status,
|
|
32
|
+
session_lost: status === 401 || status === 403,
|
|
33
|
+
message: `${e.message} (HTTP ${status})`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (e instanceof oidc.ResponseBodyError || e instanceof Error) {
|
|
37
|
+
return {
|
|
38
|
+
error: null,
|
|
39
|
+
error_description: null,
|
|
40
|
+
status: null,
|
|
41
|
+
session_lost: false,
|
|
42
|
+
message: e.message,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
error: null,
|
|
47
|
+
error_description: null,
|
|
48
|
+
status: null,
|
|
49
|
+
session_lost: false,
|
|
50
|
+
message: "Unknown token endpoint error",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export default classifyOidcTokenError;
|
|
54
|
+
//# sourceMappingURL=oidc-token-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-token-error.js","sourceRoot":"","sources":["../../../src/lib/oidc/oidc-token-error.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AACnE,oCAAoC;AAEpC,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAiBtC,MAAM,wBAAwB,GAAwB,IAAI,GAAG,CAAC;IAC5D,eAAe;IACf,gBAAgB;IAChB,qBAAqB;CACtB,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,CAAU;IAC/C,IAAI,CAAC,YAAY,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACxC,MAAM,KAAK,GAAW,CAAC,CAAC,KAAK,CAAC;QAC9B,MAAM,iBAAiB,GAAkB,CAAC,CAAC,iBAAiB,IAAI,IAAI,CAAC;QACrE,MAAM,MAAM,GAAW,CAAC,CAAC,MAAM,CAAC;QAChC,MAAM,YAAY,GAChB,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;QAC1E,OAAO;YACL,KAAK;YACL,iBAAiB;YACjB,MAAM;YACN,YAAY;YACZ,OAAO,EAAE,GAAG,KAAK,GAAG,iBAAiB,CAAC,CAAC,CAAC,KAAK,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,MAAM,GAAG;SACzF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,CAAC,6BAA6B,EAAE,CAAC;QACpD,MAAM,MAAM,GAAW,CAAC,CAAC,MAAM,CAAC;QAChC,OAAO;YACL,KAAK,EAAE,IAAI;YACX,iBAAiB,EAAE,IAAI;YACvB,MAAM;YACN,YAAY,EAAE,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;YAC9C,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,UAAU,MAAM,GAAG;SACzC,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,CAAC,iBAAiB,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QAC9D,OAAO;YACL,KAAK,EAAE,IAAI;YACX,iBAAiB,EAAE,IAAI;YACvB,MAAM,EAAE,IAAI;YACZ,YAAY,EAAE,KAAK;YACnB,OAAO,EAAE,CAAC,CAAC,OAAO;SACnB,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,IAAI;QACX,iBAAiB,EAAE,IAAI;QACvB,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,KAAK;QACnB,OAAO,EAAE,8BAA8B;KACxC,CAAC;AACJ,CAAC;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type SuccessfullyGeneratedTokensRecord } from "@schemavaults/auth-common";
|
|
2
|
+
/**
|
|
3
|
+
* The subset of openid-client's `TokenEndpointResponse` the SDK reads.
|
|
4
|
+
* Structurally typed so tests can build responses without the library.
|
|
5
|
+
*/
|
|
6
|
+
export interface OidcTokenEndpointResponseLike {
|
|
7
|
+
readonly access_token: string;
|
|
8
|
+
readonly token_type: string;
|
|
9
|
+
readonly expires_in?: number;
|
|
10
|
+
readonly refresh_token?: string;
|
|
11
|
+
readonly id_token?: string;
|
|
12
|
+
readonly scope?: string;
|
|
13
|
+
readonly [parameter: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface TokenEndpointResponseToTokensRecordOptions {
|
|
16
|
+
response: OidcTokenEndpointResponseLike;
|
|
17
|
+
/**
|
|
18
|
+
* Token audience the response's access token was minted for: the
|
|
19
|
+
* RFC 8707 `resource` sent with the request, or the reserved userinfo
|
|
20
|
+
* audience when none was sent.
|
|
21
|
+
*/
|
|
22
|
+
audience: string;
|
|
23
|
+
/** The authenticated user's id (from the id_token / cached user data). */
|
|
24
|
+
uid: string;
|
|
25
|
+
/** Audience of the refresh token: the auth server URL. */
|
|
26
|
+
auth_server_url: string;
|
|
27
|
+
/** Clock override for tests (ms since epoch). */
|
|
28
|
+
now?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds the SDK tokens record from a token response.
|
|
32
|
+
*
|
|
33
|
+
* - `access[audience]` is the response's `access_token`, expiring
|
|
34
|
+
* `expires_in` seconds from now.
|
|
35
|
+
* - `refresh` is the inlined `refresh_token` when present; when the auth
|
|
36
|
+
* server delivered it as an HTTP-only cookie instead (platform
|
|
37
|
+
* `refresh_token_delivery=http_only_cookie` extension) the record
|
|
38
|
+
* carries `"AS_HTTP_ONLY_COOKIE"` plus `refresh_token_expiry`, derived
|
|
39
|
+
* from the `refresh_token_expires_in` extension field.
|
|
40
|
+
*/
|
|
41
|
+
export declare function tokenEndpointResponseToTokensRecord({ response, audience, uid, auth_server_url, now, }: TokenEndpointResponseToTokensRecordOptions): SuccessfullyGeneratedTokensRecord;
|
|
42
|
+
export default tokenEndpointResponseToTokensRecord;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// token-endpoint-response-to-tokens-record.ts
|
|
2
|
+
//
|
|
3
|
+
// Translates an RFC 6749 §5.1 token response (as returned by
|
|
4
|
+
// openid-client) into the SDK's internal tokens record — the shape the
|
|
5
|
+
// adapters store and `acquireAccessToken` reads.
|
|
6
|
+
import { accessTokenExpiry, oidcTokenResponseExtensionsSchema, refreshTokenExpiry, } from "@schemavaults/auth-common";
|
|
7
|
+
/**
|
|
8
|
+
* Builds the SDK tokens record from a token response.
|
|
9
|
+
*
|
|
10
|
+
* - `access[audience]` is the response's `access_token`, expiring
|
|
11
|
+
* `expires_in` seconds from now.
|
|
12
|
+
* - `refresh` is the inlined `refresh_token` when present; when the auth
|
|
13
|
+
* server delivered it as an HTTP-only cookie instead (platform
|
|
14
|
+
* `refresh_token_delivery=http_only_cookie` extension) the record
|
|
15
|
+
* carries `"AS_HTTP_ONLY_COOKIE"` plus `refresh_token_expiry`, derived
|
|
16
|
+
* from the `refresh_token_expires_in` extension field.
|
|
17
|
+
*/
|
|
18
|
+
export function tokenEndpointResponseToTokensRecord({ response, audience, uid, auth_server_url, now = Date.now(), }) {
|
|
19
|
+
if (typeof response.access_token !== "string" || !response.access_token) {
|
|
20
|
+
throw new TypeError("Token response is missing 'access_token'!");
|
|
21
|
+
}
|
|
22
|
+
if (typeof response.token_type !== "string" || response.token_type.toLowerCase() !== "bearer") {
|
|
23
|
+
throw new TypeError(`Unsupported token_type '${String(response.token_type)}' in token response; expected 'Bearer'`);
|
|
24
|
+
}
|
|
25
|
+
if (typeof audience !== "string" || audience.length === 0) {
|
|
26
|
+
throw new TypeError("Expected 'audience' to be a non-empty string!");
|
|
27
|
+
}
|
|
28
|
+
if (typeof uid !== "string" || uid.length === 0) {
|
|
29
|
+
throw new TypeError("Expected 'uid' to be a non-empty string!");
|
|
30
|
+
}
|
|
31
|
+
const parsed_extensions = oidcTokenResponseExtensionsSchema.safeParse(response);
|
|
32
|
+
if (!parsed_extensions.success) {
|
|
33
|
+
throw new TypeError("Token response carries malformed extension fields: " +
|
|
34
|
+
parsed_extensions.error.issues.map((i) => i.message).join("; "));
|
|
35
|
+
}
|
|
36
|
+
const refresh_token_expires_in = parsed_extensions.data.refresh_token_expires_in;
|
|
37
|
+
const access_expires_in = typeof response.expires_in === "number" && response.expires_in > 0
|
|
38
|
+
? response.expires_in
|
|
39
|
+
: accessTokenExpiry;
|
|
40
|
+
const access = {
|
|
41
|
+
type: "access",
|
|
42
|
+
uid,
|
|
43
|
+
iat: now,
|
|
44
|
+
exp: now + access_expires_in * 1000,
|
|
45
|
+
token: response.access_token,
|
|
46
|
+
aud: audience,
|
|
47
|
+
};
|
|
48
|
+
const record = {
|
|
49
|
+
access: { [audience]: access },
|
|
50
|
+
};
|
|
51
|
+
if (typeof response.refresh_token === "string" && response.refresh_token) {
|
|
52
|
+
const refresh = {
|
|
53
|
+
type: "refresh",
|
|
54
|
+
uid,
|
|
55
|
+
iat: now,
|
|
56
|
+
exp: now + (refresh_token_expires_in ?? refreshTokenExpiry) * 1000,
|
|
57
|
+
token: response.refresh_token,
|
|
58
|
+
aud: auth_server_url,
|
|
59
|
+
};
|
|
60
|
+
record.refresh = refresh;
|
|
61
|
+
}
|
|
62
|
+
else if (typeof refresh_token_expires_in === "number") {
|
|
63
|
+
record.refresh = "AS_HTTP_ONLY_COOKIE";
|
|
64
|
+
record.refresh_token_expiry = now + refresh_token_expires_in * 1000;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
throw new Error("Token response included neither an inlined 'refresh_token' nor a 'refresh_token_expires_in' for a cookie-delivered refresh token!");
|
|
68
|
+
}
|
|
69
|
+
return record;
|
|
70
|
+
}
|
|
71
|
+
export default tokenEndpointResponseToTokensRecord;
|
|
72
|
+
//# sourceMappingURL=token-endpoint-response-to-tokens-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-endpoint-response-to-tokens-record.js","sourceRoot":"","sources":["../../../src/lib/oidc/token-endpoint-response-to-tokens-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the platform user id from an id_token's `sub` claim, which the
|
|
3
|
+
* auth server namespaces as `<auth_server_app_id>|<uid>` (Auth0-style).
|
|
4
|
+
*
|
|
5
|
+
* The prefix is NOT required to match `expected_auth_server_app_id`:
|
|
6
|
+
* the id_token's `iss` claim (verified by openid-client against the
|
|
7
|
+
* configured auth server URL) already pins the deployment, and external
|
|
8
|
+
* resource servers commonly leave `auth_server_app_id` at its default
|
|
9
|
+
* even against white-label auth servers. A mismatch is only surfaced as
|
|
10
|
+
* a warning in debug mode.
|
|
11
|
+
*/
|
|
12
|
+
export declare function uidFromOidcSubClaim(sub: unknown, expected_auth_server_app_id: string, debug?: boolean): string;
|
|
13
|
+
export default uidFromOidcSubClaim;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// uid-from-oidc-sub-claim.ts
|
|
2
|
+
//
|
|
3
|
+
// Resolves the platform user id from an OIDC id_token `sub` claim.
|
|
4
|
+
import { parseOidcSubClaim } from "@schemavaults/auth-common";
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the platform user id from an id_token's `sub` claim, which the
|
|
7
|
+
* auth server namespaces as `<auth_server_app_id>|<uid>` (Auth0-style).
|
|
8
|
+
*
|
|
9
|
+
* The prefix is NOT required to match `expected_auth_server_app_id`:
|
|
10
|
+
* the id_token's `iss` claim (verified by openid-client against the
|
|
11
|
+
* configured auth server URL) already pins the deployment, and external
|
|
12
|
+
* resource servers commonly leave `auth_server_app_id` at its default
|
|
13
|
+
* even against white-label auth servers. A mismatch is only surfaced as
|
|
14
|
+
* a warning in debug mode.
|
|
15
|
+
*/
|
|
16
|
+
export function uidFromOidcSubClaim(sub, expected_auth_server_app_id, debug = false) {
|
|
17
|
+
if (typeof sub !== "string" || sub.length === 0) {
|
|
18
|
+
throw new TypeError("id_token is missing its 'sub' claim!");
|
|
19
|
+
}
|
|
20
|
+
const parsed = parseOidcSubClaim(sub);
|
|
21
|
+
if (!parsed) {
|
|
22
|
+
throw new Error("id_token 'sub' claim is not in '<app_id>|<uid>' form!");
|
|
23
|
+
}
|
|
24
|
+
if (debug && parsed.auth_server_app_id !== expected_auth_server_app_id) {
|
|
25
|
+
console.warn(`[SchemaVaultsAuthClient] id_token 'sub' claim is namespaced by '${parsed.auth_server_app_id}', ` +
|
|
26
|
+
`but this client is configured with auth_server_app_id '${expected_auth_server_app_id}'. ` +
|
|
27
|
+
"Pass the deployment's SCHEMAVAULTS_AUTH_SERVER_APP_ID as 'auth_server_app_id' when initializing the client.");
|
|
28
|
+
}
|
|
29
|
+
return parsed.uid;
|
|
30
|
+
}
|
|
31
|
+
export default uidFromOidcSubClaim;
|
|
32
|
+
//# sourceMappingURL=uid-from-oidc-sub-claim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uid-from-oidc-sub-claim.js","sourceRoot":"","sources":["../../../src/lib/oidc/uid-from-oidc-sub-claim.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,mEAAmE;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAY,EACZ,2BAAmC,EACnC,QAAiB,KAAK;IAEtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,KAAK,IAAI,MAAM,CAAC,kBAAkB,KAAK,2BAA2B,EAAE,CAAC;QACvE,OAAO,CAAC,IAAI,CACV,mEAAmE,MAAM,CAAC,kBAAkB,KAAK;YAC/F,0DAA0D,2BAA2B,KAAK;YAC1F,6GAA6G,CAChH,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC;AACpB,CAAC;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -155,7 +155,7 @@ export async function sendAuthenticateRequest(opts) {
|
|
|
155
155
|
}
|
|
156
156
|
const parsed_auth_response = await authenticateResultSchema.safeParseAsync(await response.json());
|
|
157
157
|
if (!parsed_auth_response.success) {
|
|
158
|
-
throw new Error(parsed_auth_response.error.
|
|
158
|
+
throw new Error(parsed_auth_response.error.issues.join(", "));
|
|
159
159
|
}
|
|
160
160
|
const data = parsed_auth_response.data;
|
|
161
161
|
if (data.kind === "failure") {
|
|
@@ -100,7 +100,7 @@ export interface ISchemaVaultsAuthClient {
|
|
|
100
100
|
* @throws if `error_id` is not in the catalog or `error_code` is out of range
|
|
101
101
|
*/
|
|
102
102
|
buildErrorPageUrl: (error_id: SchemaVaultsAuthErrorId, error_code?: number) => string;
|
|
103
|
-
handleSuccessfulAuthentication: (authorization_code: string, challenge_time: number, code_verifier?: string, received_state?: string | null, expected_nonce?: string | null) => Promise<void>;
|
|
103
|
+
handleSuccessfulAuthentication: (authorization_code: string, challenge_time: number, code_verifier?: string, received_state?: string | null, expected_nonce?: string | null, received_iss?: string | null) => Promise<void>;
|
|
104
104
|
getAccessTokenFromCache: (token_id: string) => AccessToken | null;
|
|
105
105
|
getRefreshTokenFromCache: () => RefreshToken | null;
|
|
106
106
|
hasHttpOnlyRefreshToken: () => boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schemavaults/auth-client-sdk",
|
|
3
3
|
"description": "TypeScript SDK for interacting with SchemaVaults Auth server or protected resources",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.6",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
7
7
|
"repository": {
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
"main": "dist/index.js",
|
|
14
14
|
"types": "dist/index.d.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"zod": "
|
|
17
|
-
"@schemavaults/auth-common": "0.
|
|
18
|
-
"@schemavaults/app-definitions": "0.12.
|
|
16
|
+
"zod": "4.4.3",
|
|
17
|
+
"@schemavaults/auth-common": "0.26.1",
|
|
18
|
+
"@schemavaults/app-definitions": "0.12.1",
|
|
19
|
+
"openid-client": "6.8.4"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
22
|
"generate-version": "bash generate-version.sh",
|