@vantageos/cloud-identity 0.7.0 → 0.8.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/CHANGELOG.md +51 -0
- package/README.md +100 -4
- package/dist/authorize-flow.d.ts +169 -0
- package/dist/authorize-flow.d.ts.map +1 -0
- package/dist/authorize-flow.js +262 -0
- package/dist/authorize-flow.js.map +1 -0
- package/dist/authorize-shared.d.ts +44 -0
- package/dist/authorize-shared.d.ts.map +1 -0
- package/dist/authorize-shared.js +134 -0
- package/dist/authorize-shared.js.map +1 -0
- package/dist/clerk-session.d.ts +52 -0
- package/dist/clerk-session.d.ts.map +1 -0
- package/dist/clerk-session.js +93 -0
- package/dist/clerk-session.js.map +1 -0
- package/dist/human-path.d.ts +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/oidc.d.ts +76 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/oidc.js +83 -0
- package/dist/oidc.js.map +1 -0
- package/dist/token-exchange.d.ts +53 -0
- package/dist/token-exchange.d.ts.map +1 -0
- package/dist/token-exchange.js +60 -0
- package/dist/token-exchange.js.map +1 -0
- package/package.json +17 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared vocabulary of the person-authorization flow: the typed refusal, the
|
|
3
|
+
* base64url and PKCE helpers, and the signed state blob. Internal to the
|
|
4
|
+
* package except for the names `index.ts` re-exports.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Why the flow refused. Every refusal of the authorize flow, the token
|
|
8
|
+
* exchange, the session verifier and the OIDC helpers carries one of these.
|
|
9
|
+
*/
|
|
10
|
+
export type AuthorizeRefusalReason = "invalid-request" | "unknown-client" | "redirect-uri-mismatch" | "unsupported-response-type" | "pkce-required" | "resource-not-allowed" | "scope-not-supported" | "state-invalid" | "state-expired" | "session-required" | "session-invalid" | "jwks-unavailable" | "no-organization" | "org-not-a-member" | "consent-required" | "client-lookup-unavailable" | "membership-lookup-unavailable" | "code-store-unavailable" | "server-misconfigured" | "code-unknown" | "code-reused" | "code-expired" | "client-mismatch" | "redirect-uri-changed" | "resource-mismatch" | "verifier-malformed" | "pkce-mismatch" | "subject-mismatch" | "openid-scope-required" | "issuer-invalid";
|
|
11
|
+
/**
|
|
12
|
+
* The typed refusal: a non-empty object carrying its own code, in the same
|
|
13
|
+
* `{ code, reason }` shape as `RoleRefusal` and `TenantAbsence`.
|
|
14
|
+
*/
|
|
15
|
+
export type AuthorizeRefusal = {
|
|
16
|
+
code: "AUTHORIZE_REFUSED";
|
|
17
|
+
reason: AuthorizeRefusalReason;
|
|
18
|
+
};
|
|
19
|
+
export declare function refusal(reason: AuthorizeRefusalReason): AuthorizeRefusal;
|
|
20
|
+
/**
|
|
21
|
+
* Maps a refusal to the OAuth error code and HTTP status a token or
|
|
22
|
+
* authorize endpoint answers with (RFC 6749 section 5.2, RFC 8707).
|
|
23
|
+
*/
|
|
24
|
+
export declare function oauthErrorFor(r: AuthorizeRefusal): {
|
|
25
|
+
error: string;
|
|
26
|
+
status: number;
|
|
27
|
+
};
|
|
28
|
+
export declare function toBase64Url(bytes: Uint8Array): string;
|
|
29
|
+
export declare function fromBase64Url(input: string): Uint8Array | null;
|
|
30
|
+
export declare function utf8(input: string): Uint8Array;
|
|
31
|
+
/** The PKCE method name, assembled as one constant. */
|
|
32
|
+
export declare const PKCE_METHOD: string;
|
|
33
|
+
export declare function isWellFormedChallenge(challenge: string): boolean;
|
|
34
|
+
export declare function isWellFormedVerifier(verifier: string): boolean;
|
|
35
|
+
/** `base64url(SHA-256(verifier))` — the challenge a client derives from its verifier. */
|
|
36
|
+
export declare function pkceChallengeFromVerifier(verifier: string): Promise<string>;
|
|
37
|
+
export declare const MIN_SECRET_LENGTH = 32;
|
|
38
|
+
/** `base64url(json).base64url(hmac)`. */
|
|
39
|
+
export declare function signBlob(payload: unknown, secret: string): Promise<string>;
|
|
40
|
+
/** `base64url(HMAC-SHA-256(secret, message))`. */
|
|
41
|
+
export declare function signMac(message: string, secret: string): Promise<string>;
|
|
42
|
+
/** Returns the parsed payload when the MAC verifies (constant time), else `null`. */
|
|
43
|
+
export declare function verifyBlob(blob: string, secret: string): Promise<unknown | null>;
|
|
44
|
+
//# sourceMappingURL=authorize-shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize-shared.d.ts","sourceRoot":"","sources":["../src/authorize-shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAE9B,iBAAiB,GACjB,gBAAgB,GAChB,uBAAuB,GACvB,2BAA2B,GAC3B,eAAe,GACf,sBAAsB,GACtB,qBAAqB,GAErB,eAAe,GACf,eAAe,GAEf,kBAAkB,GAClB,iBAAiB,GACjB,kBAAkB,GAElB,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAElB,2BAA2B,GAC3B,+BAA+B,GAC/B,wBAAwB,GACxB,sBAAsB,GAEtB,cAAc,GACd,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,sBAAsB,GACtB,mBAAmB,GACnB,oBAAoB,GACpB,eAAe,GAEf,kBAAkB,GAClB,uBAAuB,GACvB,gBAAgB,CAAC;AAErB;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,sBAAsB,CAAC;CAChC,CAAC;AAEF,wBAAgB,OAAO,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAExE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,gBAAgB,GAAG;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAsCA;AAMD,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIrD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAS9D;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAE9C;AAMD,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,MAA8B,CAAC;AAKzD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,yFAAyF;AACzF,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAMjB;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAYpC,yCAAyC;AACzC,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,kDAAkD;AAClD,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO9E;AAED,qFAAqF;AACrF,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAoBzB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared vocabulary of the person-authorization flow: the typed refusal, the
|
|
3
|
+
* base64url and PKCE helpers, and the signed state blob. Internal to the
|
|
4
|
+
* package except for the names `index.ts` re-exports.
|
|
5
|
+
*/
|
|
6
|
+
export function refusal(reason) {
|
|
7
|
+
return { code: "AUTHORIZE_REFUSED", reason };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Maps a refusal to the OAuth error code and HTTP status a token or
|
|
11
|
+
* authorize endpoint answers with (RFC 6749 section 5.2, RFC 8707).
|
|
12
|
+
*/
|
|
13
|
+
export function oauthErrorFor(r) {
|
|
14
|
+
switch (r.reason) {
|
|
15
|
+
case "unknown-client":
|
|
16
|
+
case "client-mismatch":
|
|
17
|
+
return { error: "invalid_client", status: 400 };
|
|
18
|
+
case "unsupported-response-type":
|
|
19
|
+
return { error: "unsupported_response_type", status: 400 };
|
|
20
|
+
case "scope-not-supported":
|
|
21
|
+
return { error: "invalid_scope", status: 400 };
|
|
22
|
+
case "resource-not-allowed":
|
|
23
|
+
case "resource-mismatch":
|
|
24
|
+
return { error: "invalid_target", status: 400 };
|
|
25
|
+
case "code-unknown":
|
|
26
|
+
case "code-reused":
|
|
27
|
+
case "code-expired":
|
|
28
|
+
case "redirect-uri-changed":
|
|
29
|
+
case "verifier-malformed":
|
|
30
|
+
case "pkce-mismatch":
|
|
31
|
+
return { error: "invalid_grant", status: 400 };
|
|
32
|
+
case "session-required":
|
|
33
|
+
case "session-invalid":
|
|
34
|
+
case "no-organization":
|
|
35
|
+
case "org-not-a-member":
|
|
36
|
+
case "consent-required":
|
|
37
|
+
case "subject-mismatch":
|
|
38
|
+
case "openid-scope-required":
|
|
39
|
+
return { error: "access_denied", status: 403 };
|
|
40
|
+
case "jwks-unavailable":
|
|
41
|
+
case "client-lookup-unavailable":
|
|
42
|
+
case "membership-lookup-unavailable":
|
|
43
|
+
case "code-store-unavailable":
|
|
44
|
+
return { error: "temporarily_unavailable", status: 503 };
|
|
45
|
+
case "server-misconfigured":
|
|
46
|
+
case "issuer-invalid":
|
|
47
|
+
return { error: "server_error", status: 500 };
|
|
48
|
+
default:
|
|
49
|
+
return { error: "invalid_request", status: 400 };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// base64url
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
export function toBase64Url(bytes) {
|
|
56
|
+
let bin = "";
|
|
57
|
+
for (const b of bytes)
|
|
58
|
+
bin += String.fromCharCode(b);
|
|
59
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
60
|
+
}
|
|
61
|
+
export function fromBase64Url(input) {
|
|
62
|
+
if (!/^[A-Za-z0-9_-]*$/.test(input))
|
|
63
|
+
return null;
|
|
64
|
+
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
65
|
+
try {
|
|
66
|
+
const bin = atob(padded + "=".repeat((4 - (padded.length % 4)) % 4));
|
|
67
|
+
return Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function utf8(input) {
|
|
74
|
+
return new TextEncoder().encode(input);
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// PKCE (RFC 7636)
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
/** The PKCE method name, assembled as one constant. */
|
|
80
|
+
export const PKCE_METHOD = ["S", "256"].join("");
|
|
81
|
+
const VERIFIER_RE = /^[A-Za-z0-9\-._~]{43,128}$/;
|
|
82
|
+
const CHALLENGE_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
83
|
+
export function isWellFormedChallenge(challenge) {
|
|
84
|
+
return CHALLENGE_RE.test(challenge);
|
|
85
|
+
}
|
|
86
|
+
export function isWellFormedVerifier(verifier) {
|
|
87
|
+
return VERIFIER_RE.test(verifier);
|
|
88
|
+
}
|
|
89
|
+
/** `base64url(SHA-256(verifier))` — the challenge a client derives from its verifier. */
|
|
90
|
+
export async function pkceChallengeFromVerifier(verifier) {
|
|
91
|
+
const digest = await crypto.subtle.digest("SHA-256", utf8(verifier));
|
|
92
|
+
return toBase64Url(new Uint8Array(digest));
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Signed state blob
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
export const MIN_SECRET_LENGTH = 32;
|
|
98
|
+
async function hmacKey(secret, usage) {
|
|
99
|
+
return crypto.subtle.importKey("raw", utf8(secret), { name: "HMAC", hash: "SHA-256" }, false, [usage]);
|
|
100
|
+
}
|
|
101
|
+
/** `base64url(json).base64url(hmac)`. */
|
|
102
|
+
export async function signBlob(payload, secret) {
|
|
103
|
+
const body = toBase64Url(utf8(JSON.stringify(payload)));
|
|
104
|
+
const mac = await crypto.subtle.sign("HMAC", await hmacKey(secret, "sign"), utf8(body));
|
|
105
|
+
return `${body}.${toBase64Url(new Uint8Array(mac))}`;
|
|
106
|
+
}
|
|
107
|
+
/** `base64url(HMAC-SHA-256(secret, message))`. */
|
|
108
|
+
export async function signMac(message, secret) {
|
|
109
|
+
const mac = await crypto.subtle.sign("HMAC", await hmacKey(secret, "sign"), utf8(message));
|
|
110
|
+
return toBase64Url(new Uint8Array(mac));
|
|
111
|
+
}
|
|
112
|
+
/** Returns the parsed payload when the MAC verifies (constant time), else `null`. */
|
|
113
|
+
export async function verifyBlob(blob, secret) {
|
|
114
|
+
const parts = blob.split(".");
|
|
115
|
+
if (parts.length !== 2)
|
|
116
|
+
return null;
|
|
117
|
+
const [body, mac] = parts;
|
|
118
|
+
const macBytes = fromBase64Url(mac);
|
|
119
|
+
if (!macBytes)
|
|
120
|
+
return null;
|
|
121
|
+
const ok = await crypto.subtle.verify("HMAC", await hmacKey(secret, "verify"), macBytes, utf8(body));
|
|
122
|
+
if (!ok)
|
|
123
|
+
return null;
|
|
124
|
+
const bodyBytes = fromBase64Url(body);
|
|
125
|
+
if (!bodyBytes)
|
|
126
|
+
return null;
|
|
127
|
+
try {
|
|
128
|
+
return JSON.parse(new TextDecoder().decode(bodyBytes));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=authorize-shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize-shared.js","sourceRoot":"","sources":["../src/authorize-shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type AuthorizeRefusal } from "./authorize-shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Verification of a Clerk session token (a signed RS256 JWT) against the
|
|
4
|
+
* instance's JWKS. The consumer supplies the issuer, the key set and, where it
|
|
5
|
+
* wants them, the audience and authorized parties; this module holds every
|
|
6
|
+
* decision and performs no network call of its own.
|
|
7
|
+
*
|
|
8
|
+
* @security Only RS256 is accepted. `alg: none`, HMAC algorithms and any key
|
|
9
|
+
* type other than RSA are refused before a signature is looked at.
|
|
10
|
+
*/
|
|
11
|
+
export interface ClerkJwk {
|
|
12
|
+
kty: string;
|
|
13
|
+
kid?: string;
|
|
14
|
+
n?: string;
|
|
15
|
+
e?: string;
|
|
16
|
+
alg?: string;
|
|
17
|
+
use?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ClerkJwks {
|
|
20
|
+
keys: readonly ClerkJwk[];
|
|
21
|
+
}
|
|
22
|
+
export interface ClerkSessionVerifierConfig {
|
|
23
|
+
/** Exact `iss` the token must carry (the Clerk frontend API URL). */
|
|
24
|
+
issuer: string;
|
|
25
|
+
/** The key set, or a function returning it (the consumer owns fetching and caching). */
|
|
26
|
+
jwks: ClerkJwks | (() => Promise<ClerkJwks>);
|
|
27
|
+
/** When set, the token's `aud` must contain one of these. */
|
|
28
|
+
audience?: string | readonly string[];
|
|
29
|
+
/** When set, the token's `azp` must be present and listed. */
|
|
30
|
+
authorizedParties?: readonly string[];
|
|
31
|
+
/** Clock tolerance in seconds for `exp` / `nbf`. Default 5. */
|
|
32
|
+
clockToleranceSeconds?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface VerifiedClerkSession {
|
|
35
|
+
userId: string;
|
|
36
|
+
sessionId: string | null;
|
|
37
|
+
claims: Readonly<Record<string, unknown>>;
|
|
38
|
+
}
|
|
39
|
+
export type VerifyClerkSessionResult = {
|
|
40
|
+
ok: true;
|
|
41
|
+
session: VerifiedClerkSession;
|
|
42
|
+
} | {
|
|
43
|
+
ok: false;
|
|
44
|
+
refusal: AuthorizeRefusal;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Verifies a Clerk session token. `nowMs` is injectable for tests.
|
|
48
|
+
* An unreachable key set is `jwks-unavailable` (retryable, 503), never a
|
|
49
|
+
* bad-credential answer; every other failure is `session-invalid`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function verifyClerkSessionToken(token: string, config: ClerkSessionVerifierConfig, nowMs?: number): Promise<VerifyClerkSessionResult>;
|
|
52
|
+
//# sourceMappingURL=clerk-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk-session.d.ts","sourceRoot":"","sources":["../src/clerk-session.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAItB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;GAQG;AAEH,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,SAAS,GAAG,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACtC,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3C;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAc7C;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,0BAA0B,EAClC,KAAK,GAAE,MAAmB,GACzB,OAAO,CAAC,wBAAwB,CAAC,CA+EnC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { fromBase64Url, refusal, utf8, } from "./authorize-shared.js";
|
|
2
|
+
function parseJson(bytes) {
|
|
3
|
+
if (!bytes)
|
|
4
|
+
return null;
|
|
5
|
+
try {
|
|
6
|
+
const v = JSON.parse(new TextDecoder().decode(bytes));
|
|
7
|
+
return typeof v === "object" && v !== null && !Array.isArray(v)
|
|
8
|
+
? v
|
|
9
|
+
: null;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Verifies a Clerk session token. `nowMs` is injectable for tests.
|
|
17
|
+
* An unreachable key set is `jwks-unavailable` (retryable, 503), never a
|
|
18
|
+
* bad-credential answer; every other failure is `session-invalid`.
|
|
19
|
+
*/
|
|
20
|
+
export async function verifyClerkSessionToken(token, config, nowMs = Date.now()) {
|
|
21
|
+
const bad = () => ({
|
|
22
|
+
ok: false,
|
|
23
|
+
refusal: refusal("session-invalid"),
|
|
24
|
+
});
|
|
25
|
+
const parts = token.split(".");
|
|
26
|
+
if (parts.length !== 3)
|
|
27
|
+
return bad();
|
|
28
|
+
const [h, p, s] = parts;
|
|
29
|
+
const header = parseJson(fromBase64Url(h));
|
|
30
|
+
const claims = parseJson(fromBase64Url(p));
|
|
31
|
+
const sig = fromBase64Url(s);
|
|
32
|
+
if (!header || !claims || !sig || sig.length === 0)
|
|
33
|
+
return bad();
|
|
34
|
+
if (header.alg !== "RS256")
|
|
35
|
+
return bad();
|
|
36
|
+
if (typeof header.kid !== "string" || header.kid.length === 0)
|
|
37
|
+
return bad();
|
|
38
|
+
let jwks;
|
|
39
|
+
try {
|
|
40
|
+
jwks = typeof config.jwks === "function" ? await config.jwks() : config.jwks;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { ok: false, refusal: refusal("jwks-unavailable") };
|
|
44
|
+
}
|
|
45
|
+
const jwk = jwks.keys.find((k) => k.kid === header.kid && k.kty === "RSA");
|
|
46
|
+
if (!jwk)
|
|
47
|
+
return bad();
|
|
48
|
+
let valid = false;
|
|
49
|
+
try {
|
|
50
|
+
const key = await crypto.subtle.importKey("jwk", { kty: "RSA", n: jwk.n, e: jwk.e, alg: "RS256", ext: true }, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["verify"]);
|
|
51
|
+
valid = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", key, sig, utf8(`${h}.${p}`));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return bad();
|
|
55
|
+
}
|
|
56
|
+
if (!valid)
|
|
57
|
+
return bad();
|
|
58
|
+
const tol = config.clockToleranceSeconds ?? 5;
|
|
59
|
+
const nowSec = Math.floor(nowMs / 1000);
|
|
60
|
+
if (claims.iss !== config.issuer)
|
|
61
|
+
return bad();
|
|
62
|
+
if (typeof claims.exp !== "number" || claims.exp + tol < nowSec)
|
|
63
|
+
return bad();
|
|
64
|
+
if (claims.nbf !== undefined) {
|
|
65
|
+
if (typeof claims.nbf !== "number" || claims.nbf - tol > nowSec)
|
|
66
|
+
return bad();
|
|
67
|
+
}
|
|
68
|
+
if (typeof claims.sub !== "string" || claims.sub.length === 0)
|
|
69
|
+
return bad();
|
|
70
|
+
if (config.audience !== undefined) {
|
|
71
|
+
const wanted = typeof config.audience === "string" ? [config.audience] : config.audience;
|
|
72
|
+
const got = Array.isArray(claims.aud)
|
|
73
|
+
? claims.aud
|
|
74
|
+
: [claims.aud];
|
|
75
|
+
if (!got.some((a) => typeof a === "string" && wanted.includes(a)))
|
|
76
|
+
return bad();
|
|
77
|
+
}
|
|
78
|
+
if (config.authorizedParties !== undefined) {
|
|
79
|
+
if (typeof claims.azp !== "string" ||
|
|
80
|
+
!config.authorizedParties.includes(claims.azp)) {
|
|
81
|
+
return bad();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
session: {
|
|
87
|
+
userId: claims.sub,
|
|
88
|
+
sessionId: typeof claims.sid === "string" ? claims.sid : null,
|
|
89
|
+
claims,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=clerk-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk-session.js","sourceRoot":"","sources":["../src/clerk-session.ts"],"names":[],"mappings":"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"}
|
package/dist/human-path.d.ts
CHANGED
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { z } from "zod";
|
|
22
22
|
export declare const humanAccountRoleSchema: z.ZodEnum<{
|
|
23
|
+
client: "client";
|
|
23
24
|
owner: "owner";
|
|
24
25
|
admin: "admin";
|
|
25
26
|
member: "member";
|
|
26
|
-
client: "client";
|
|
27
27
|
}>;
|
|
28
28
|
export type HumanAccountRole = z.infer<typeof humanAccountRoleSchema>;
|
|
29
29
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -17,4 +17,14 @@ export { humanAccountRoleSchema, normalizeVerifiedHumanSession, requireHumanRole
|
|
|
17
17
|
export type { RequiredHumanRole, RoleRefusal, RoleRefusalReason, RoleResolution, HumanAccountRole, ClerkSessionLike, ResolvedHumanIdentity, } from "./human-path.js";
|
|
18
18
|
export { validatePresentedBearer, requireAgentScopedIdentity, storedBearerRowSchema, } from "./presented-bearer.js";
|
|
19
19
|
export type { StoredBearerRow, AgentPresence, ResolvedBearerIdentity, AgentScopedIdentity, ValidatePresentedBearerResult, RequireAgentScopedResult, PresentedBearerDeps, PresentedBearerInternalReason, } from "./presented-bearer.js";
|
|
20
|
+
export { startAuthorize, resumeAuthorize, } from "./authorize-flow.js";
|
|
21
|
+
export type { AuthorizeClient, ClerkOrgMembership, AuthorizationCodeRecord, AuthorizationCodeStore, ConsumeCodeResult, AuthorizeConfig, AuthorizeDeps, AuthorizeOutcome, OrgPickerModel, ResumeInput, } from "./authorize-flow.js";
|
|
22
|
+
export { exchangeAuthorizationCode } from "./token-exchange.js";
|
|
23
|
+
export type { AuthorizedTokenClaims, ExchangeInput, ExchangeDeps, ExchangeResult, } from "./token-exchange.js";
|
|
24
|
+
export { verifyClerkSessionToken } from "./clerk-session.js";
|
|
25
|
+
export type { ClerkJwk, ClerkJwks, ClerkSessionVerifierConfig, VerifiedClerkSession, VerifyClerkSessionResult, } from "./clerk-session.js";
|
|
26
|
+
export { buildDiscoveryDocument, buildUserInfo } from "./oidc.js";
|
|
27
|
+
export type { DiscoveryConfig, DiscoveryDocument, DiscoveryResult, ClerkUserLike, UserInfo, UserInfoResult, } from "./oidc.js";
|
|
28
|
+
export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js";
|
|
29
|
+
export type { AuthorizeRefusal, AuthorizeRefusalReason, } from "./authorize-shared.js";
|
|
20
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,6BAA6B,EAC7B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,6BAA6B,EAC7B,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EACV,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EACV,QAAQ,EACR,SAAS,EACT,0BAA0B,EAC1B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,QAAQ,EACR,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACjF,YAAY,EACV,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,4 +18,11 @@ export { humanAccountRoleSchema, normalizeVerifiedHumanSession, requireHumanRole
|
|
|
18
18
|
// NON-MASTER credential. The package's single answer to "who is calling"
|
|
19
19
|
// for a presented bearer; consumers must not keep a local variant.
|
|
20
20
|
export { validatePresentedBearer, requireAgentScopedIdentity, storedBearerRowSchema, } from "./presented-bearer.js";
|
|
21
|
+
// 0.8.0 — Authorize a PERSON: Clerk session -> verified organisation -> a code
|
|
22
|
+
// bound to both; the exchange that redeems it; the OIDC documents.
|
|
23
|
+
export { startAuthorize, resumeAuthorize, } from "./authorize-flow.js";
|
|
24
|
+
export { exchangeAuthorizationCode } from "./token-exchange.js";
|
|
25
|
+
export { verifyClerkSessionToken } from "./clerk-session.js";
|
|
26
|
+
export { buildDiscoveryDocument, buildUserInfo } from "./oidc.js";
|
|
27
|
+
export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js";
|
|
21
28
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAU1E,kFAAkF;AAClF,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAU1E,kFAAkF;AAClF,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AASlE,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/oidc.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type AuthorizeRefusal } from "./authorize-shared.js";
|
|
2
|
+
import type { AuthorizedTokenClaims } from "./token-exchange.js";
|
|
3
|
+
/** Inputs of the discovery document. Every endpoint is an absolute URL. */
|
|
4
|
+
export interface DiscoveryConfig {
|
|
5
|
+
issuer: string;
|
|
6
|
+
authorizationEndpoint: string;
|
|
7
|
+
tokenEndpoint: string;
|
|
8
|
+
jwksUri: string;
|
|
9
|
+
userinfoEndpoint: string;
|
|
10
|
+
registrationEndpoint?: string;
|
|
11
|
+
scopesSupported?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface DiscoveryDocument {
|
|
14
|
+
issuer: string;
|
|
15
|
+
authorization_endpoint: string;
|
|
16
|
+
token_endpoint: string;
|
|
17
|
+
jwks_uri: string;
|
|
18
|
+
userinfo_endpoint: string;
|
|
19
|
+
registration_endpoint?: string;
|
|
20
|
+
response_types_supported: string[];
|
|
21
|
+
response_modes_supported: string[];
|
|
22
|
+
grant_types_supported: string[];
|
|
23
|
+
subject_types_supported: string[];
|
|
24
|
+
scopes_supported: string[];
|
|
25
|
+
code_challenge_methods_supported: string[];
|
|
26
|
+
claims_supported: string[];
|
|
27
|
+
}
|
|
28
|
+
export type DiscoveryResult = {
|
|
29
|
+
ok: true;
|
|
30
|
+
document: DiscoveryDocument;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
refusal: AuthorizeRefusal;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Builds the discovery document (OAuth authorization-server metadata with the
|
|
37
|
+
* OIDC fields this package honours). It advertises only what is implemented:
|
|
38
|
+
* the code flow, PKCE, the `authorization_code` grant, UserInfo. It does NOT
|
|
39
|
+
* advertise id_tokens, refresh tokens or client-authentication methods; the
|
|
40
|
+
* consumer may add those keys to the returned object if it implements them. The issuer must be an https URL with no query or fragment (plain
|
|
41
|
+
* http only for loopback); every endpoint must be an absolute URL.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildDiscoveryDocument(config: DiscoveryConfig): DiscoveryResult;
|
|
44
|
+
/** The subset of a Clerk user the UserInfo helper reads. */
|
|
45
|
+
export interface ClerkUserLike {
|
|
46
|
+
id: string;
|
|
47
|
+
primaryEmailAddressId?: string | null;
|
|
48
|
+
emailAddresses: readonly {
|
|
49
|
+
id: string;
|
|
50
|
+
emailAddress: string;
|
|
51
|
+
verification?: {
|
|
52
|
+
status?: string | null;
|
|
53
|
+
} | null;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
export interface UserInfo {
|
|
57
|
+
sub: string;
|
|
58
|
+
email?: string;
|
|
59
|
+
email_verified?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export type UserInfoResult = {
|
|
62
|
+
ok: true;
|
|
63
|
+
userinfo: UserInfo;
|
|
64
|
+
} | {
|
|
65
|
+
ok: false;
|
|
66
|
+
refusal: AuthorizeRefusal;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Builds the UserInfo response from the Clerk user and the claims of the
|
|
70
|
+
* presented token. The token must carry `openid`; the user must be the token's
|
|
71
|
+
* subject; `email` and `email_verified` appear only when the token also
|
|
72
|
+
* carries `email` AND the user has a primary address. `email_verified` is true
|
|
73
|
+
* only for a Clerk verification status of exactly `verified`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildUserInfo(claims: Pick<AuthorizedTokenClaims, "sub" | "scope">, user: ClerkUserLike): UserInfoResult;
|
|
76
|
+
//# sourceMappingURL=oidc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.d.ts","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAClC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,gCAAgC,EAAE,MAAM,EAAE,CAAC;IAC3C,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,GACzC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAgB7C;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAgC/E;AAED,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,cAAc,EAAE,SAAS;QACvB,EAAE,EAAE,MAAM,CAAC;QACX,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;KAClD,EAAE,CAAC;CACL;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,GAAG,OAAO,CAAC,EACpD,IAAI,EAAE,aAAa,GAClB,cAAc,CAmBhB"}
|
package/dist/oidc.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { PKCE_METHOD, refusal, } from "./authorize-shared.js";
|
|
2
|
+
function isAbsoluteUrl(value, requireHttps) {
|
|
3
|
+
try {
|
|
4
|
+
const u = new URL(value);
|
|
5
|
+
if (u.protocol === "https:")
|
|
6
|
+
return true;
|
|
7
|
+
return (!requireHttps &&
|
|
8
|
+
u.protocol === "http:" &&
|
|
9
|
+
(u.hostname === "localhost" || u.hostname === "127.0.0.1"));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Builds the discovery document (OAuth authorization-server metadata with the
|
|
17
|
+
* OIDC fields this package honours). It advertises only what is implemented:
|
|
18
|
+
* the code flow, PKCE, the `authorization_code` grant, UserInfo. It does NOT
|
|
19
|
+
* advertise id_tokens, refresh tokens or client-authentication methods; the
|
|
20
|
+
* consumer may add those keys to the returned object if it implements them. The issuer must be an https URL with no query or fragment (plain
|
|
21
|
+
* http only for loopback); every endpoint must be an absolute URL.
|
|
22
|
+
*/
|
|
23
|
+
export function buildDiscoveryDocument(config) {
|
|
24
|
+
const bad = { ok: false, refusal: refusal("issuer-invalid") };
|
|
25
|
+
if (!isAbsoluteUrl(config.issuer, true))
|
|
26
|
+
return bad;
|
|
27
|
+
const issuer = new URL(config.issuer);
|
|
28
|
+
if (issuer.search !== "" || issuer.hash !== "")
|
|
29
|
+
return bad;
|
|
30
|
+
const endpoints = [
|
|
31
|
+
config.authorizationEndpoint,
|
|
32
|
+
config.tokenEndpoint,
|
|
33
|
+
config.jwksUri,
|
|
34
|
+
config.userinfoEndpoint,
|
|
35
|
+
...(config.registrationEndpoint ? [config.registrationEndpoint] : []),
|
|
36
|
+
];
|
|
37
|
+
if (!endpoints.every((e) => isAbsoluteUrl(e, true)))
|
|
38
|
+
return bad;
|
|
39
|
+
const document = {
|
|
40
|
+
issuer: config.issuer,
|
|
41
|
+
authorization_endpoint: config.authorizationEndpoint,
|
|
42
|
+
token_endpoint: config.tokenEndpoint,
|
|
43
|
+
jwks_uri: config.jwksUri,
|
|
44
|
+
userinfo_endpoint: config.userinfoEndpoint,
|
|
45
|
+
response_types_supported: ["code"],
|
|
46
|
+
response_modes_supported: ["query"],
|
|
47
|
+
grant_types_supported: ["authorization_code"],
|
|
48
|
+
subject_types_supported: ["public"],
|
|
49
|
+
scopes_supported: [...(config.scopesSupported ?? ["openid", "email"])],
|
|
50
|
+
code_challenge_methods_supported: [PKCE_METHOD],
|
|
51
|
+
claims_supported: ["sub", "email", "email_verified"],
|
|
52
|
+
};
|
|
53
|
+
if (config.registrationEndpoint) {
|
|
54
|
+
document.registration_endpoint = config.registrationEndpoint;
|
|
55
|
+
}
|
|
56
|
+
return { ok: true, document };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds the UserInfo response from the Clerk user and the claims of the
|
|
60
|
+
* presented token. The token must carry `openid`; the user must be the token's
|
|
61
|
+
* subject; `email` and `email_verified` appear only when the token also
|
|
62
|
+
* carries `email` AND the user has a primary address. `email_verified` is true
|
|
63
|
+
* only for a Clerk verification status of exactly `verified`.
|
|
64
|
+
*/
|
|
65
|
+
export function buildUserInfo(claims, user) {
|
|
66
|
+
const scopes = claims.scope.split(/\s+/);
|
|
67
|
+
if (!scopes.includes("openid")) {
|
|
68
|
+
return { ok: false, refusal: refusal("openid-scope-required") };
|
|
69
|
+
}
|
|
70
|
+
if (user.id !== claims.sub) {
|
|
71
|
+
return { ok: false, refusal: refusal("subject-mismatch") };
|
|
72
|
+
}
|
|
73
|
+
const userinfo = { sub: user.id };
|
|
74
|
+
if (scopes.includes("email")) {
|
|
75
|
+
const primary = user.emailAddresses.find((a) => a.id === user.primaryEmailAddressId);
|
|
76
|
+
if (primary) {
|
|
77
|
+
userinfo.email = primary.emailAddress;
|
|
78
|
+
userinfo.email_verified = primary.verification?.status === "verified";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { ok: true, userinfo };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=oidc.js.map
|
package/dist/oidc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../src/oidc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AuthorizationCodeStore } from "./authorize-flow.js";
|
|
2
|
+
import { type AuthorizeRefusal } from "./authorize-shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* The claims a token minted from an authorization code carries. They are
|
|
5
|
+
* copied from the code's own binding, which was written from the verified
|
|
6
|
+
* Clerk user and a membership that user holds. There is no `scopeProfile`
|
|
7
|
+
* here and no code path that reads one: an identity is never supplied by the
|
|
8
|
+
* client-registration row.
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthorizedTokenClaims {
|
|
11
|
+
sub: string;
|
|
12
|
+
org_id: string;
|
|
13
|
+
org_slug: string | null;
|
|
14
|
+
org_role: string;
|
|
15
|
+
aud: string;
|
|
16
|
+
client_id: string;
|
|
17
|
+
scope: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ExchangeInput {
|
|
20
|
+
/** The authorization code the client presents. */
|
|
21
|
+
code: string;
|
|
22
|
+
/** PKCE verifier (RFC 7636). */
|
|
23
|
+
codeVerifier: string;
|
|
24
|
+
/** Must equal the redirect_uri of the authorize request. */
|
|
25
|
+
redirectUri: string;
|
|
26
|
+
/** The authenticated client's id. */
|
|
27
|
+
clientId: string;
|
|
28
|
+
/** Optional RFC 8707 resource; when present it must equal the bound one. */
|
|
29
|
+
resource?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ExchangeDeps {
|
|
32
|
+
codeStore: AuthorizationCodeStore;
|
|
33
|
+
/** Epoch milliseconds. */
|
|
34
|
+
now?: () => number;
|
|
35
|
+
}
|
|
36
|
+
export type ExchangeResult = {
|
|
37
|
+
ok: true;
|
|
38
|
+
claims: AuthorizedTokenClaims;
|
|
39
|
+
} | {
|
|
40
|
+
ok: false;
|
|
41
|
+
refusal: AuthorizeRefusal;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Redeems an authorization code. The code is CONSUMED first (atomically, by
|
|
45
|
+
* the store) so that every attempt, successful or not, burns it: a wrong
|
|
46
|
+
* verifier cannot be retried against the same code. A code presented twice is
|
|
47
|
+
* `code-reused`, distinct from `code-unknown`, so the consumer can revoke what
|
|
48
|
+
* the first redemption issued.
|
|
49
|
+
*
|
|
50
|
+
* Checks, in order: expiry, client, redirect_uri, resource, PKCE.
|
|
51
|
+
*/
|
|
52
|
+
export declare function exchangeAuthorizationCode(input: ExchangeInput, deps: ExchangeDeps): Promise<ExchangeResult>;
|
|
53
|
+
//# sourceMappingURL=token-exchange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-exchange.d.ts","sourceRoot":"","sources":["../src/token-exchange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,sBAAsB,CAAC;IAClC,0BAA0B;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,CAAC;AAO7C;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAuCzB"}
|