@tula-tech/contract 0.0.0-stage → 0.1.0-alpha.1

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.
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Schemes that are never a custom-scheme redirect, whatever follows the colon: the two web
3
+ * schemes (which have rules of their own) and the ones a browser or an operating system
4
+ * handles itself, where a redirect would run script, open a file, start a call or hand the
5
+ * URL to something that is nobody's app.
6
+ *
7
+ * **Best effort, and no more.** The entries without a full stop are also refused by the
8
+ * rule that a custom scheme contains one; the ones with a full stop, and the families of
9
+ * {@link REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM}, are refused by this list alone, and a
10
+ * scheme an operating system handles that nobody put here passes. What bounds that is not
11
+ * the list: every entry of `urls.allowedRedirectUrls` is written by the operator by hand,
12
+ * and a redirect carries only a ticket that is useless without the binding (ADR 0044).
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * REDIRECT_SCHEMES_NEVER_CUSTOM.includes('javascript') // true
17
+ * ```
18
+ */
19
+ declare const REDIRECT_SCHEMES_NEVER_CUSTOM: readonly ["http", "https", "javascript", "vbscript", "data", "file", "filesystem", "blob", "about", "mailto", "tel", "sms", "facetime", "geo", "maps", "intent", "content", "android-app", "market", "itms", "itms-apps", "itms-services", "app-settings", "ms-settings", "x-apple.systempreferences", "ftp", "ws", "wss", "chrome", "chrome-extension", "moz-extension", "resource", "jar", "cid", "mid", "view-source"];
20
+ /**
21
+ * Families of schemes that are never a custom-scheme redirect: every scheme that starts
22
+ * with one of these. They are written with full stops, so the reverse-domain rule lets
23
+ * them through, and each belongs to a platform and to no operator's app: the schemes of
24
+ * Windows' built-in apps (`microsoft.windows.camera`, `microsoft.windows.photos.crop`, …),
25
+ * Apple's `x-apple.` schemes (`x-apple.systempreferences`) and Apple's own bundle-id space
26
+ * (`com.apple.`, which no third party's app can be in).
27
+ *
28
+ * Best effort, like {@link REDIRECT_SCHEMES_NEVER_CUSTOM}: a family that is not here passes.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM.some((family) => 'com.apple.tv'.startsWith(family)) // true
33
+ * ```
34
+ */
35
+ declare const REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM: readonly ["microsoft.windows.", "x-apple.", "com.apple."];
36
+ /**
37
+ * The kinds of redirect URL an environment may list.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const kind: RedirectUrlKind = 'custom_scheme'
42
+ * ```
43
+ */
44
+ type RedirectUrlKind = "https" | "loopback" | "custom_scheme";
45
+ /**
46
+ * Whether `value` holds a character no redirect URL of any kind may hold:
47
+ *
48
+ * - whitespace (`\s`) or a wildcard (`*`);
49
+ * - a backslash or a control character (U+0000 to U+001F, U+007F to U+009F);
50
+ * - a character that draws nothing, which is what `hasInvisibleCharacter` says: the one
51
+ * class `[\p{Cf}\p{Variation_Selector}\p{Default_Ignorable_Code_Point}]`, so every format
52
+ * character (the zero-width space, joiner and non-joiner, the word joiner, the soft
53
+ * hyphen, the Mongolian vowel separator, the text-direction controls, the tag
54
+ * characters), the variation selectors (U+FE00 to U+FE0F, U+E0100 to U+E01EF) and whatever
55
+ * else Unicode ignores by default (the combining grapheme joiner, the Hangul fillers);
56
+ * - what `hasHiddenCharacter` refuses beside those: a private-use character (`Co`), an
57
+ * unassigned one (`Cn`), a lone surrogate.
58
+ *
59
+ * A `Location` header cannot carry the controls, and a reader cannot see the rest: two
60
+ * entries that differ by a zero-width space read the same on every screen. An entry is
61
+ * compared as the string it is, so it holds only what can be seen. A visible letter outside
62
+ * ASCII (`münchen.de`) and a percent-encoded octet (`%20`, `%E2%80%8B`) are not refused.
63
+ *
64
+ * The server asks this of every URL it is about to redirect to, listed or not, so that a
65
+ * stored entry from before a rule is refused when a sign-in starts and never at the
66
+ * provider's callback, where the state is already spent.
67
+ *
68
+ * @param value - A URL.
69
+ * @returns `true` when it holds such a character.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * hasForbiddenRedirectCharacter('https://a.com/\u{202E}x') // true
74
+ * hasForbiddenRedirectCharacter('https://a.com/x\u{200B}y') // true
75
+ * hasForbiddenRedirectCharacter('https://a.com/cb?x=1') // false
76
+ * ```
77
+ */
78
+ declare function hasForbiddenRedirectCharacter(value: string): boolean;
79
+ /**
80
+ * Which kind of redirect URL `value` is, or `null` when an environment may not list it.
81
+ *
82
+ * - `https`: any absolute URL that starts with `https://` (lower case) and has no
83
+ * credentials, fragment, wildcard, whitespace, backslash, control character (U+0000 to
84
+ * U+001F, U+007F to U+009F), character that draws nothing (`hasInvisibleCharacter`: `Cf`,
85
+ * the variation selectors, `Default_Ignorable_Code_Point`) or character
86
+ * `hasHiddenCharacter` refuses. A query is allowed.
87
+ * An app link is one of these.
88
+ * - `loopback`: the same over `http://` for `localhost`, `127.0.0.1` and `[::1]`.
89
+ * - `custom_scheme`: `<scheme>:/<path>` or `<scheme>://<host>/<path>` where the scheme is in
90
+ * reverse-domain form (lower case, **with a full stop**: `com.example.app`), is not one of
91
+ * {@link REDIRECT_SCHEMES_NEVER_CUSTOM} nor in a family of
92
+ * {@link REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM}, and what follows is slashes and the characters
93
+ * `A-Z a-z 0-9 . _ ~ -` only. So: no user name or password, no port, **no query**, no
94
+ * fragment (the server adds one), no wildcard, no percent-encoded octet, no `.` or `..`
95
+ * segment, and no control or invisible character.
96
+ *
97
+ * Nothing is normalised, for any kind: the string that is listed is the string a request
98
+ * must send, character for character.
99
+ *
100
+ * @param value - A URL as an operator wrote it.
101
+ * @returns Its kind, or `null`.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * redirectUrlKind('com.example.app:/oauth') // 'custom_scheme'
106
+ * redirectUrlKind('myapp://callback') // null: no full stop in the scheme
107
+ * redirectUrlKind('javascript:alert(1)') // null
108
+ * ```
109
+ */
110
+ declare function redirectUrlKind(value: string): RedirectUrlKind | null;
111
+ /**
112
+ * Whether an environment may list `value` in `urls.allowedRedirectUrls`.
113
+ *
114
+ * @param value - A URL as an operator wrote it.
115
+ * @returns `true` for any {@link RedirectUrlKind}.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * isRedirectUrl('https://app.example.com/oauth/callback') // true
120
+ * ```
121
+ */
122
+ declare function isRedirectUrl(value: string): boolean;
123
+ /**
124
+ * Whether `value` is a custom-scheme redirect URL ({@link redirectUrlKind}).
125
+ *
126
+ * @param value - A URL.
127
+ * @returns `true` only for the `custom_scheme` kind.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * isCustomSchemeRedirectUrl('com.example.app:/oauth') // true
132
+ * isCustomSchemeRedirectUrl('https://app.example.com/oauth') // false
133
+ * ```
134
+ */
135
+ declare function isCustomSchemeRedirectUrl(value: string): boolean;
136
+ /**
137
+ * The providers whose authorization code Tula binds to the attempt with PKCE (ADR 0026): the
138
+ * code is redeemable only with the verifier the attempt holds.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * OAUTH_PROVIDERS_WITH_PKCE.includes('google') // true
143
+ * ```
144
+ */
145
+ declare const OAUTH_PROVIDERS_WITH_PKCE: readonly ["google", "github", "microsoft", "discord", "x"];
146
+ /**
147
+ * The providers that do **not** bind their code with PKCE, as a fact about the provider and
148
+ * not a setting: Apple and LinkedIn document none, and Facebook's has it only in a flow that
149
+ * is not used (ADR 0026).
150
+ *
151
+ * A provider sign-in with one of them is never returned to a custom scheme
152
+ * ({@link customSchemeRedirectRefusal}). A new provider is added to this list or to
153
+ * {@link OAUTH_PROVIDERS_WITH_PKCE}; a test fails for one that is in neither.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * OAUTH_PROVIDERS_WITHOUT_PKCE.includes('apple') // true
158
+ * ```
159
+ */
160
+ declare const OAUTH_PROVIDERS_WITHOUT_PKCE: readonly ["apple", "linkedin", "facebook"];
161
+ /**
162
+ * Whether a provider's code is bound to the attempt with PKCE
163
+ * ({@link OAUTH_PROVIDERS_WITH_PKCE}). The one place that says so.
164
+ *
165
+ * @param provider - A provider's name.
166
+ * @returns `true` only for a provider on the list; `false` for any other string.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * bindsCodeWithPkce('google') // true
171
+ * bindsCodeWithPkce('apple') // false
172
+ * ```
173
+ */
174
+ declare function bindsCodeWithPkce(provider: string): boolean;
175
+ /**
176
+ * Why a listed custom-scheme redirect URL is still refused for one request, as the fixed word
177
+ * in `params.reason` of `request.redirect_not_allowed`:
178
+ *
179
+ * - `provider_without_pkce`: the provider's code is not bound with PKCE.
180
+ * - `client_not_native`: the attempt was not started as a native client (`x-tula-client: ios` or
181
+ * `android`).
182
+ * - `not_a_provider_sign_in`: the redirect is not for a provider sign-in (an emailed link).
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * const reason: CustomSchemeRedirectRefusal = CUSTOM_SCHEME_REDIRECT_REFUSALS[0]
187
+ * ```
188
+ */
189
+ declare const CUSTOM_SCHEME_REDIRECT_REFUSALS: readonly ["provider_without_pkce", "client_not_native", "not_a_provider_sign_in"];
190
+ /** One of {@link CUSTOM_SCHEME_REDIRECT_REFUSALS}. */
191
+ type CustomSchemeRedirectRefusal = (typeof CUSTOM_SCHEME_REDIRECT_REFUSALS)[number];
192
+ /**
193
+ * Why a redirect to `url` is refused for this use although the environment lists it, or
194
+ * `null` when it is not.
195
+ *
196
+ * Only a custom-scheme URL is ever refused here. Any app on the device can claim a scheme,
197
+ * so the app that receives the redirect may be another one. What then keeps a sign-in from
198
+ * being completed by it:
199
+ *
200
+ * - the provider's code is bound with PKCE, so a provider that is told to return to the
201
+ * scheme's owner cannot be made to hand a usable code to someone else. Without PKCE the
202
+ * redirect is refused, whatever else holds;
203
+ * - the attempt belongs to a native client. A browser page has no use for a custom scheme,
204
+ * and a `web` attempt ends in cookies.
205
+ *
206
+ * It depends on the URL, the provider and the client kind, never on a user.
207
+ *
208
+ * @param url - A redirect URL the environment lists.
209
+ * @param use - The client kind of the attempt, and the provider when it is a provider
210
+ * sign-in.
211
+ * @returns The reason, or `null`.
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * customSchemeRedirectRefusal('com.example.app:/oauth', { client: 'ios', provider: 'apple' })
216
+ * // 'provider_without_pkce'
217
+ * ```
218
+ */
219
+ declare function customSchemeRedirectRefusal(url: string, use: {
220
+ client: string;
221
+ provider?: string;
222
+ }): CustomSchemeRedirectRefusal | null;
223
+ export { CUSTOM_SCHEME_REDIRECT_REFUSALS, CustomSchemeRedirectRefusal, OAUTH_PROVIDERS_WITHOUT_PKCE, OAUTH_PROVIDERS_WITH_PKCE, REDIRECT_SCHEMES_NEVER_CUSTOM, REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM, RedirectUrlKind, bindsCodeWithPkce, customSchemeRedirectRefusal, hasForbiddenRedirectCharacter, isCustomSchemeRedirectUrl, isRedirectUrl, redirectUrlKind };
@@ -0,0 +1,26 @@
1
+ import {
2
+ REDIRECT_SCHEMES_NEVER_CUSTOM2,
3
+ REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM2,
4
+ hasForbiddenRedirectCharacter2,
5
+ redirectUrlKind2,
6
+ isRedirectUrl2,
7
+ isCustomSchemeRedirectUrl2,
8
+ OAUTH_PROVIDERS_WITH_PKCE2,
9
+ OAUTH_PROVIDERS_WITHOUT_PKCE2,
10
+ bindsCodeWithPkce2,
11
+ CUSTOM_SCHEME_REDIRECT_REFUSALS2,
12
+ customSchemeRedirectRefusal2
13
+ } from "./shared/chunk-r202nxfw.js";
14
+ export {
15
+ CUSTOM_SCHEME_REDIRECT_REFUSALS2 as CUSTOM_SCHEME_REDIRECT_REFUSALS,
16
+ OAUTH_PROVIDERS_WITHOUT_PKCE2 as OAUTH_PROVIDERS_WITHOUT_PKCE,
17
+ OAUTH_PROVIDERS_WITH_PKCE2 as OAUTH_PROVIDERS_WITH_PKCE,
18
+ REDIRECT_SCHEMES_NEVER_CUSTOM2 as REDIRECT_SCHEMES_NEVER_CUSTOM,
19
+ REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM2 as REDIRECT_SCHEME_FAMILIES_NEVER_CUSTOM,
20
+ bindsCodeWithPkce2 as bindsCodeWithPkce,
21
+ customSchemeRedirectRefusal2 as customSchemeRedirectRefusal,
22
+ hasForbiddenRedirectCharacter2 as hasForbiddenRedirectCharacter,
23
+ isCustomSchemeRedirectUrl2 as isCustomSchemeRedirectUrl,
24
+ isRedirectUrl2 as isRedirectUrl,
25
+ redirectUrlKind2 as redirectUrlKind
26
+ };
@@ -0,0 +1,56 @@
1
+ // src/webhook-signature.ts
2
+ var WEBHOOK_ID_HEADER2 = "webhook-id";
3
+ var WEBHOOK_TIMESTAMP_HEADER2 = "webhook-timestamp";
4
+ var WEBHOOK_SIGNATURE_HEADER2 = "webhook-signature";
5
+ var WEBHOOK_SECRET_PREFIX2 = "whsec_";
6
+ var WEBHOOK_SIGNATURE_VERSION2 = "v1";
7
+ var WEBHOOK_TOLERANCE_SECONDS2 = 5 * 60;
8
+ var MIN_KEY_BYTES = 24;
9
+ var MAX_KEY_BYTES = 64;
10
+ var BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
11
+ function toBase64(bytes) {
12
+ let binary = "";
13
+ for (const byte of bytes) {
14
+ binary += String.fromCharCode(byte);
15
+ }
16
+ return btoa(binary);
17
+ }
18
+ function webhookSecretBytes2(secret) {
19
+ if (!secret.startsWith(WEBHOOK_SECRET_PREFIX2)) {
20
+ return null;
21
+ }
22
+ const encoded = secret.slice(WEBHOOK_SECRET_PREFIX2.length);
23
+ if (!BASE64.test(encoded) || encoded.length % 4 !== 0) {
24
+ return null;
25
+ }
26
+ let binary;
27
+ try {
28
+ binary = atob(encoded);
29
+ } catch {
30
+ return null;
31
+ }
32
+ if (binary.length < MIN_KEY_BYTES || binary.length > MAX_KEY_BYTES) {
33
+ return null;
34
+ }
35
+ const bytes = new Uint8Array(binary.length);
36
+ for (let index = 0;index < binary.length; index++) {
37
+ bytes[index] = binary.charCodeAt(index);
38
+ }
39
+ return bytes;
40
+ }
41
+ function formatWebhookSecret2(key) {
42
+ if (key.length < MIN_KEY_BYTES || key.length > MAX_KEY_BYTES) {
43
+ throw new RangeError("a webhook signing key is 24 to 64 bytes");
44
+ }
45
+ return `${WEBHOOK_SECRET_PREFIX2}${toBase64(key)}`;
46
+ }
47
+ async function signWebhook2(key, id, timestamp, body) {
48
+ const hmac = await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, [
49
+ "sign"
50
+ ]);
51
+ const content = new TextEncoder().encode(`${id}.${timestamp}.${body}`);
52
+ const signature = new Uint8Array(await crypto.subtle.sign("HMAC", hmac, content));
53
+ return `${WEBHOOK_SIGNATURE_VERSION2},${toBase64(signature)}`;
54
+ }
55
+
56
+ export { WEBHOOK_ID_HEADER2, WEBHOOK_TIMESTAMP_HEADER2, WEBHOOK_SIGNATURE_HEADER2, WEBHOOK_SECRET_PREFIX2, WEBHOOK_SIGNATURE_VERSION2, WEBHOOK_TOLERANCE_SECONDS2, webhookSecretBytes2, formatWebhookSecret2, signWebhook2 };
@@ -0,0 +1,102 @@
1
+ // src/custom-claims.ts
2
+ var CUSTOM_CLAIMS_CLAIM2 = "ext";
3
+ var RESERVED_CLAIM_NAMES2 = [
4
+ "iss",
5
+ "sub",
6
+ "aud",
7
+ "exp",
8
+ "nbf",
9
+ "iat",
10
+ "jti",
11
+ "sid",
12
+ "pid",
13
+ "eid",
14
+ "v",
15
+ "auth_time",
16
+ "amr",
17
+ "sp",
18
+ "cnf",
19
+ CUSTOM_CLAIMS_CLAIM2
20
+ ];
21
+ var MAX_CUSTOM_CLAIMS_BYTES2 = 1024;
22
+ var MAX_JWT_TEMPLATES2 = 10;
23
+ var MAX_JWT_TEMPLATE_CLAIMS2 = 16;
24
+ var MAX_CUSTOM_CLAIM_KEY_LENGTH2 = 32;
25
+ var MAX_CUSTOM_CLAIM_CONSTANT_LENGTH2 = 256;
26
+ var KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
27
+ var UNSAFE_KEYS = ["__proto__", "constructor", "prototype"];
28
+ function isCustomClaimKey2(key) {
29
+ return typeof key === "string" && key.length <= MAX_CUSTOM_CLAIM_KEY_LENGTH2 && KEY.test(key) && !UNSAFE_KEYS.includes(key) && !RESERVED_CLAIM_NAMES2.includes(key);
30
+ }
31
+ function isCustomClaimValue2(value) {
32
+ return typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
33
+ }
34
+ function utf8Length(text) {
35
+ let bytes = 0;
36
+ for (let index = 0;index < text.length; index += 1) {
37
+ const unit = text.charCodeAt(index);
38
+ if (unit < 128) {
39
+ bytes += 1;
40
+ } else if (unit < 2048) {
41
+ bytes += 2;
42
+ } else if (unit >= 55296 && unit <= 56319 && isLowSurrogate(text.charCodeAt(index + 1))) {
43
+ bytes += 4;
44
+ index += 1;
45
+ } else {
46
+ bytes += 3;
47
+ }
48
+ }
49
+ return bytes;
50
+ }
51
+ function isLowSurrogate(unit) {
52
+ return unit >= 56320 && unit <= 57343;
53
+ }
54
+ function customClaimsBytes2(claims) {
55
+ return utf8Length(JSON.stringify(claims));
56
+ }
57
+ function isPlainObject(value) {
58
+ if (typeof value !== "object" || value === null) {
59
+ return false;
60
+ }
61
+ const prototype = Object.getPrototypeOf(value);
62
+ return prototype === Object.prototype || prototype === null;
63
+ }
64
+ function checkCustomClaims2(value) {
65
+ if (!isPlainObject(value)) {
66
+ return { problem: "invalid" };
67
+ }
68
+ const entries = [];
69
+ for (const key of Object.keys(value)) {
70
+ const claim = value[key];
71
+ if (!isCustomClaimKey2(key) || !isCustomClaimValue2(claim)) {
72
+ return { problem: "invalid" };
73
+ }
74
+ entries.push([key, claim]);
75
+ }
76
+ const claims = Object.fromEntries(entries);
77
+ return customClaimsBytes2(claims) > MAX_CUSTOM_CLAIMS_BYTES2 ? { problem: "too_large" } : { claims };
78
+ }
79
+ function readCustomClaims2(claims) {
80
+ if (!isPlainObject(claims) || !Object.hasOwn(claims, CUSTOM_CLAIMS_CLAIM2)) {
81
+ return null;
82
+ }
83
+ const raw = claims[CUSTOM_CLAIMS_CLAIM2];
84
+ if (!isPlainObject(raw)) {
85
+ return null;
86
+ }
87
+ const entries = [];
88
+ for (const key of Object.keys(raw)) {
89
+ const value = raw[key];
90
+ if (!isCustomClaimKey2(key) || !isCustomClaimValue2(value)) {
91
+ return null;
92
+ }
93
+ entries.push([key, value]);
94
+ }
95
+ if (entries.length === 0) {
96
+ return null;
97
+ }
98
+ const read = Object.fromEntries(entries);
99
+ return customClaimsBytes2(read) <= MAX_CUSTOM_CLAIMS_BYTES2 ? Object.freeze(read) : null;
100
+ }
101
+
102
+ export { CUSTOM_CLAIMS_CLAIM2, RESERVED_CLAIM_NAMES2, MAX_CUSTOM_CLAIMS_BYTES2, MAX_JWT_TEMPLATES2, MAX_JWT_TEMPLATE_CLAIMS2, MAX_CUSTOM_CLAIM_KEY_LENGTH2, MAX_CUSTOM_CLAIM_CONSTANT_LENGTH2, isCustomClaimKey2, isCustomClaimValue2, customClaimsBytes2, checkCustomClaims2, readCustomClaims2 };
@@ -0,0 +1,58 @@
1
+ // src/device-binding.ts
2
+ var DPOP_HEADER2 = "DPoP";
3
+ var DPOP_NONCE_HEADER2 = "DPoP-Nonce";
4
+ var DPOP_PROOF_TYPE2 = "dpop+jwt";
5
+ var DPOP_ALGORITHMS2 = ["ES256"];
6
+ var MAX_DPOP_PROOF_LENGTH2 = 2048;
7
+ var encoder = new TextEncoder;
8
+ function base64url(bytes) {
9
+ let binary = "";
10
+ for (const byte of bytes) {
11
+ binary += String.fromCharCode(byte);
12
+ }
13
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
14
+ }
15
+ var THIRTY_TWO_BYTES = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
16
+ var COORDINATE = THIRTY_TWO_BYTES;
17
+ var THUMBPRINT = THIRTY_TWO_BYTES;
18
+ function isDevicePublicJwk2(value) {
19
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
20
+ return false;
21
+ }
22
+ const jwk = value;
23
+ return Object.keys(jwk).length === 4 && jwk.kty === "EC" && jwk.crv === "P-256" && typeof jwk.x === "string" && COORDINATE.test(jwk.x) && typeof jwk.y === "string" && COORDINATE.test(jwk.y);
24
+ }
25
+ function isKeyThumbprint2(value) {
26
+ return typeof value === "string" && THUMBPRINT.test(value);
27
+ }
28
+ async function jwkThumbprint2(jwk) {
29
+ const canonical = `{"crv":"${jwk.crv}","kty":"${jwk.kty}","x":"${jwk.x}","y":"${jwk.y}"}`;
30
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(canonical));
31
+ return base64url(new Uint8Array(digest));
32
+ }
33
+ async function createDpopProof2(key, input) {
34
+ const header = { typ: DPOP_PROOF_TYPE2, alg: DPOP_ALGORITHMS2[0], jwk: key.publicJwk };
35
+ const payload = {
36
+ htm: input.method,
37
+ htu: input.url,
38
+ iat: Math.floor((input.now ?? Date.now()) / 1000),
39
+ jti: input.jti ?? key.proofId?.() ?? base64url(crypto.getRandomValues(new Uint8Array(16))),
40
+ ...input.nonce !== undefined && { nonce: input.nonce }
41
+ };
42
+ const signed = `${base64url(encoder.encode(JSON.stringify(header)))}.${base64url(encoder.encode(JSON.stringify(payload)))}`;
43
+ return `${signed}.${base64url(await key.sign(encoder.encode(signed)))}`;
44
+ }
45
+ async function generateSoftwareDeviceKey2() {
46
+ const algorithm = { name: "ECDSA", namedCurve: "P-256" };
47
+ const pair = await crypto.subtle.generateKey(algorithm, false, ["sign"]);
48
+ const { x, y } = await crypto.subtle.exportKey("jwk", pair.publicKey);
49
+ return {
50
+ publicJwk: { kty: "EC", crv: "P-256", x, y },
51
+ async sign(data) {
52
+ const signature = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, pair.privateKey, new Uint8Array(data));
53
+ return new Uint8Array(signature);
54
+ }
55
+ };
56
+ }
57
+
58
+ export { DPOP_HEADER2, DPOP_NONCE_HEADER2, DPOP_PROOF_TYPE2, DPOP_ALGORITHMS2, MAX_DPOP_PROOF_LENGTH2, isDevicePublicJwk2, isKeyThumbprint2, jwkThumbprint2, createDpopProof2, generateSoftwareDeviceKey2 };
@@ -0,0 +1,108 @@
1
+ // src/event-types.ts
2
+ var ACTIVITY_TYPES2 = [
3
+ "user.created",
4
+ "user.email_verified",
5
+ "user.banned",
6
+ "user.unbanned",
7
+ "user.deleted",
8
+ "user.password_changed",
9
+ "user.mfa_enabled",
10
+ "user.mfa_disabled",
11
+ "user.backup_codes_regenerated",
12
+ "user.backup_code_used",
13
+ "user.identity_linked",
14
+ "user.identity_unlinked",
15
+ "user.email_added",
16
+ "user.passkey_added",
17
+ "user.passkey_renamed",
18
+ "user.passkey_removed",
19
+ "user.passkey_counter_regressed",
20
+ "user.phone_number_added",
21
+ "user.phone_number_removed",
22
+ "user.sms_factor_enabled",
23
+ "user.sms_factor_removed",
24
+ "session.created",
25
+ "session.revoked",
26
+ "session.reuse_detected",
27
+ "session.stepped_up",
28
+ "session.refresh_proof_refused",
29
+ "api_key.created",
30
+ "api_key.revoked",
31
+ "signing_key.rotated",
32
+ "environment.settings_updated",
33
+ "oauth_provider.updated",
34
+ "oauth_provider.deleted",
35
+ "webhook_endpoint.created",
36
+ "webhook_endpoint.updated",
37
+ "webhook_endpoint.deleted",
38
+ "webhook_endpoint.disabled",
39
+ "webhook_endpoint.secret_rotated",
40
+ "webhook_endpoint.previous_secret_revoked",
41
+ "hook.created",
42
+ "hook.updated",
43
+ "hook.deleted",
44
+ "native_app.created",
45
+ "native_app.updated",
46
+ "native_app.deleted",
47
+ "proposal.created",
48
+ "proposal.approved",
49
+ "proposal.executed",
50
+ "proposal.failed",
51
+ "sign_in_test.started",
52
+ "sign_in_test.finished"
53
+ ];
54
+ var EVENT_SCHEMA_VERSION2 = 1;
55
+ var EVENT_TARGET_TYPES2 = {
56
+ "user.created": "user",
57
+ "user.email_verified": "user",
58
+ "user.banned": "user",
59
+ "user.unbanned": "user",
60
+ "user.deleted": "user",
61
+ "user.password_changed": "user",
62
+ "user.mfa_enabled": "user",
63
+ "user.mfa_disabled": "user",
64
+ "user.backup_codes_regenerated": "user",
65
+ "user.backup_code_used": "user",
66
+ "user.identity_linked": "user",
67
+ "user.email_added": "user",
68
+ "user.identity_unlinked": "user",
69
+ "user.passkey_added": "user",
70
+ "user.passkey_renamed": "user",
71
+ "user.passkey_removed": "user",
72
+ "user.passkey_counter_regressed": "user",
73
+ "user.phone_number_added": "user",
74
+ "user.phone_number_removed": "user",
75
+ "user.sms_factor_enabled": "user",
76
+ "user.sms_factor_removed": "user",
77
+ "session.created": "session",
78
+ "session.revoked": "session",
79
+ "session.reuse_detected": "session",
80
+ "session.stepped_up": "session",
81
+ "session.refresh_proof_refused": "session",
82
+ "api_key.created": "api_key",
83
+ "api_key.revoked": "api_key",
84
+ "signing_key.rotated": "signing_key",
85
+ "environment.settings_updated": "environment",
86
+ "oauth_provider.updated": "environment",
87
+ "oauth_provider.deleted": "environment",
88
+ "webhook_endpoint.created": "webhook_endpoint",
89
+ "webhook_endpoint.updated": "webhook_endpoint",
90
+ "webhook_endpoint.deleted": "webhook_endpoint",
91
+ "webhook_endpoint.disabled": "webhook_endpoint",
92
+ "webhook_endpoint.secret_rotated": "webhook_endpoint",
93
+ "webhook_endpoint.previous_secret_revoked": "webhook_endpoint",
94
+ "hook.created": "hook",
95
+ "hook.updated": "hook",
96
+ "hook.deleted": "hook",
97
+ "native_app.created": "native_app",
98
+ "native_app.updated": "native_app",
99
+ "native_app.deleted": "native_app",
100
+ "proposal.created": "proposal",
101
+ "proposal.approved": "proposal",
102
+ "proposal.executed": "proposal",
103
+ "proposal.failed": "proposal",
104
+ "sign_in_test.started": "environment",
105
+ "sign_in_test.finished": "environment"
106
+ };
107
+
108
+ export { ACTIVITY_TYPES2, EVENT_SCHEMA_VERSION2, EVENT_TARGET_TYPES2 };
@@ -0,0 +1,9 @@
1
+ // src/issuer.ts
2
+ function environmentIssuer2(apiUrl, environmentId) {
3
+ return `${apiUrl.replace(/\/+$/, "")}/v1/environments/${encodeURIComponent(environmentId)}`;
4
+ }
5
+ function jwksUrl2(issuer) {
6
+ return `${issuer.replace(/\/+$/, "")}/.well-known/jwks.json`;
7
+ }
8
+
9
+ export { environmentIssuer2, jwksUrl2 };