@opensyber/tokenforge 0.1.2 → 1.1.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/LICENSE.md +32 -0
- package/README.md +329 -52
- package/dist/adapters/astro.d.ts +64 -0
- package/dist/adapters/astro.d.ts.map +1 -0
- package/dist/adapters/astro.js +104 -0
- package/dist/adapters/astro.js.map +1 -0
- package/dist/adapters/cloud-v2-bridge.d.ts +8 -0
- package/dist/adapters/cloud-v2-bridge.d.ts.map +1 -0
- package/dist/adapters/cloud-v2-bridge.js +24 -0
- package/dist/adapters/cloud-v2-bridge.js.map +1 -0
- package/dist/adapters/express.d.ts +30 -2
- package/dist/adapters/express.d.ts.map +1 -1
- package/dist/adapters/express.js +65 -0
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/fastify.d.ts +32 -6
- package/dist/adapters/fastify.d.ts.map +1 -1
- package/dist/adapters/fastify.js +65 -0
- package/dist/adapters/fastify.js.map +1 -1
- package/dist/adapters/hono.d.ts +13 -1
- package/dist/adapters/hono.d.ts.map +1 -1
- package/dist/adapters/hono.js +14 -1
- package/dist/adapters/hono.js.map +1 -1
- package/dist/adapters/index.d.ts +5 -3
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +5 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/nextjs.d.ts +31 -0
- package/dist/adapters/nextjs.d.ts.map +1 -1
- package/dist/adapters/nextjs.js +83 -30
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/sveltekit.d.ts +66 -0
- package/dist/adapters/sveltekit.d.ts.map +1 -0
- package/dist/adapters/sveltekit.js +101 -0
- package/dist/adapters/sveltekit.js.map +1 -0
- package/dist/client/action-signer.d.ts +53 -0
- package/dist/client/action-signer.d.ts.map +1 -0
- package/dist/client/action-signer.js +48 -0
- package/dist/client/action-signer.js.map +1 -0
- package/dist/client/binding.d.ts +24 -7
- package/dist/client/binding.d.ts.map +1 -1
- package/dist/client/binding.js +17 -6
- package/dist/client/binding.js.map +1 -1
- package/dist/client/cloud-device.d.ts +54 -0
- package/dist/client/cloud-device.d.ts.map +1 -0
- package/dist/client/cloud-device.js +301 -0
- package/dist/client/cloud-device.js.map +1 -0
- package/dist/client/crypto.d.ts.map +1 -1
- package/dist/client/crypto.js.map +1 -1
- package/dist/client/index.d.ts +14 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +30 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/interceptor.d.ts.map +1 -1
- package/dist/client/interceptor.js +27 -3
- package/dist/client/interceptor.js.map +1 -1
- package/dist/client/signer.d.ts +40 -3
- package/dist/client/signer.d.ts.map +1 -1
- package/dist/client/signer.js +56 -5
- package/dist/client/signer.js.map +1 -1
- package/dist/client/storage.d.ts.map +1 -1
- package/dist/client/storage.js.map +1 -1
- package/dist/client/webauthn.d.ts +45 -0
- package/dist/client/webauthn.d.ts.map +1 -0
- package/dist/client/webauthn.js +118 -0
- package/dist/client/webauthn.js.map +1 -0
- package/dist/protocol/v2.d.ts +23 -0
- package/dist/protocol/v2.d.ts.map +1 -0
- package/dist/protocol/v2.js +157 -0
- package/dist/protocol/v2.js.map +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/provider.js.map +1 -1
- package/dist/server/action-verify.d.ts +56 -0
- package/dist/server/action-verify.d.ts.map +1 -0
- package/dist/server/action-verify.js +47 -0
- package/dist/server/action-verify.js.map +1 -0
- package/dist/server/activity-histogram.d.ts +47 -0
- package/dist/server/activity-histogram.d.ts.map +1 -0
- package/dist/server/activity-histogram.js +76 -0
- package/dist/server/activity-histogram.js.map +1 -0
- package/dist/server/aitm-heuristics.d.ts +57 -0
- package/dist/server/aitm-heuristics.d.ts.map +1 -0
- package/dist/server/aitm-heuristics.js +120 -0
- package/dist/server/aitm-heuristics.js.map +1 -0
- package/dist/server/bind-with-id-token.d.ts +55 -0
- package/dist/server/bind-with-id-token.d.ts.map +1 -0
- package/dist/server/bind-with-id-token.js +70 -0
- package/dist/server/bind-with-id-token.js.map +1 -0
- package/dist/server/bound-cookie.d.ts +39 -0
- package/dist/server/bound-cookie.d.ts.map +1 -0
- package/dist/server/bound-cookie.js +59 -0
- package/dist/server/bound-cookie.js.map +1 -0
- package/dist/server/cbor.d.ts +20 -0
- package/dist/server/cbor.d.ts.map +1 -0
- package/dist/server/cbor.js +110 -0
- package/dist/server/cbor.js.map +1 -0
- package/dist/server/cloud-bind.d.ts +42 -0
- package/dist/server/cloud-bind.d.ts.map +1 -0
- package/dist/server/cloud-bind.js +387 -0
- package/dist/server/cloud-bind.js.map +1 -0
- package/dist/server/cloud-middleware.d.ts +46 -0
- package/dist/server/cloud-middleware.d.ts.map +1 -0
- package/dist/server/cloud-middleware.js +344 -0
- package/dist/server/cloud-middleware.js.map +1 -0
- package/dist/server/cloud-verify.d.ts +30 -0
- package/dist/server/cloud-verify.d.ts.map +1 -0
- package/dist/server/cloud-verify.js +206 -0
- package/dist/server/cloud-verify.js.map +1 -0
- package/dist/server/dbsc-challenge.d.ts +51 -0
- package/dist/server/dbsc-challenge.d.ts.map +1 -0
- package/dist/server/dbsc-challenge.js +78 -0
- package/dist/server/dbsc-challenge.js.map +1 -0
- package/dist/server/dbsc-endpoint.d.ts +20 -0
- package/dist/server/dbsc-endpoint.d.ts.map +1 -0
- package/dist/server/dbsc-endpoint.js +85 -0
- package/dist/server/dbsc-endpoint.js.map +1 -0
- package/dist/server/dbsc-verify.d.ts +32 -0
- package/dist/server/dbsc-verify.d.ts.map +1 -0
- package/dist/server/dbsc-verify.js +65 -0
- package/dist/server/dbsc-verify.js.map +1 -0
- package/dist/server/identity-profile.d.ts +18 -0
- package/dist/server/identity-profile.d.ts.map +1 -0
- package/dist/server/identity-profile.js +113 -0
- package/dist/server/identity-profile.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/jws-verify.d.ts +46 -0
- package/dist/server/jws-verify.d.ts.map +1 -0
- package/dist/server/jws-verify.js +64 -0
- package/dist/server/jws-verify.js.map +1 -0
- package/dist/server/middleware.d.ts +24 -0
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +87 -20
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/oidc-verify.d.ts +71 -0
- package/dist/server/oidc-verify.d.ts.map +1 -0
- package/dist/server/oidc-verify.js +115 -0
- package/dist/server/oidc-verify.js.map +1 -0
- package/dist/server/policy.d.ts +47 -0
- package/dist/server/policy.d.ts.map +1 -0
- package/dist/server/policy.js +84 -0
- package/dist/server/policy.js.map +1 -0
- package/dist/server/step-up-policy.d.ts +36 -0
- package/dist/server/step-up-policy.d.ts.map +1 -0
- package/dist/server/step-up-policy.js +101 -0
- package/dist/server/step-up-policy.js.map +1 -0
- package/dist/server/storage/index.d.ts +3 -0
- package/dist/server/storage/index.d.ts.map +1 -0
- package/dist/server/storage/index.js +2 -0
- package/dist/server/storage/index.js.map +1 -0
- package/dist/server/storage/interface.d.ts +73 -0
- package/dist/server/storage/interface.d.ts.map +1 -0
- package/dist/server/storage/interface.js +2 -0
- package/dist/server/storage/interface.js.map +1 -0
- package/dist/server/storage/memory.d.ts +50 -0
- package/dist/server/storage/memory.d.ts.map +1 -0
- package/dist/server/storage/memory.js +121 -0
- package/dist/server/storage/memory.js.map +1 -0
- package/dist/server/webauthn-verify.d.ts +41 -0
- package/dist/server/webauthn-verify.d.ts.map +1 -0
- package/dist/server/webauthn-verify.js +175 -0
- package/dist/server/webauthn-verify.js.map +1 -0
- package/dist/server/webhooks.d.ts +42 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +77 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/shared/action-hash.d.ts +23 -0
- package/dist/shared/action-hash.d.ts.map +1 -0
- package/dist/shared/action-hash.js +35 -0
- package/dist/shared/action-hash.js.map +1 -0
- package/dist/shared/identity.d.ts +45 -0
- package/dist/shared/identity.d.ts.map +1 -0
- package/dist/shared/identity.js +176 -0
- package/dist/shared/identity.js.map +1 -0
- package/dist/shared/types-internal.d.ts.map +1 -1
- package/dist/shared/types-internal.js.map +1 -1
- package/dist/shared/types.d.ts +49 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +1 -0
- package/dist/shared/types.js.map +1 -1
- package/package.json +31 -33
package/dist/adapters/nextjs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createTokenForgeCloudV2Gate, } from './cloud-v2-bridge.js';
|
|
1
2
|
const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
|
|
2
3
|
/**
|
|
3
4
|
* Next.js App Router — wrap an API route handler with TokenForge verification.
|
|
@@ -18,39 +19,36 @@ export function withTokenForge(handler, options) {
|
|
|
18
19
|
const apiBase = options.apiBase ?? DEFAULT_API_BASE;
|
|
19
20
|
return async (req) => {
|
|
20
21
|
const url = new URL(req.url);
|
|
21
|
-
|
|
22
|
-
const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: {
|
|
25
|
-
Authorization: `Bearer ${options.apiKey}`,
|
|
26
|
-
'Content-Type': 'application/json',
|
|
27
|
-
},
|
|
28
|
-
body: JSON.stringify({
|
|
29
|
-
path: url.pathname,
|
|
30
|
-
method: req.method,
|
|
31
|
-
headers: {
|
|
32
|
-
signature: header('x-tf-signature'),
|
|
33
|
-
nonce: header('x-tf-nonce'),
|
|
34
|
-
timestamp: header('x-tf-timestamp'),
|
|
35
|
-
deviceId: header('x-tf-device-id'),
|
|
36
|
-
},
|
|
37
|
-
ipAddress: header('x-forwarded-for') ?? '',
|
|
38
|
-
countryCode: header('cf-ipcountry') ?? '',
|
|
39
|
-
userAgent: header('user-agent') ?? '',
|
|
40
|
-
}),
|
|
41
|
-
});
|
|
42
|
-
if (!apiRes.ok) {
|
|
22
|
+
if (shouldSkip(url.pathname, options.skipPaths)) {
|
|
43
23
|
return handler(req, { bound: false, trustScore: 0, deviceId: null });
|
|
44
24
|
}
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
const header = (name) => req.headers.get(name);
|
|
26
|
+
try {
|
|
27
|
+
const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { Authorization: `Bearer ${options.apiKey}`, 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
path: url.pathname, method: req.method,
|
|
32
|
+
headers: {
|
|
33
|
+
signature: header('x-tf-signature'), nonce: header('x-tf-nonce'),
|
|
34
|
+
timestamp: header('x-tf-timestamp'), deviceId: header('x-tf-device-id'),
|
|
35
|
+
},
|
|
36
|
+
ipAddress: header('x-forwarded-for') ?? '',
|
|
37
|
+
countryCode: header('cf-ipcountry') ?? '',
|
|
38
|
+
userAgent: header('user-agent') ?? '',
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
if (!apiRes.ok)
|
|
42
|
+
return handler(req, { bound: false, trustScore: 0, deviceId: null });
|
|
43
|
+
const { data } = (await apiRes.json());
|
|
44
|
+
if (data.status === 'block') {
|
|
45
|
+
return new Response(JSON.stringify({ error: 'session_blocked', reason: data.reason }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
|
46
|
+
}
|
|
47
|
+
return handler(req, { bound: data.bound, trustScore: data.trustScore, deviceId: data.deviceId });
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return handler(req, { bound: false, trustScore: 0, deviceId: null });
|
|
48
51
|
}
|
|
49
|
-
return handler(req, {
|
|
50
|
-
bound: data.bound,
|
|
51
|
-
trustScore: data.trustScore,
|
|
52
|
-
deviceId: data.deviceId,
|
|
53
|
-
});
|
|
54
52
|
};
|
|
55
53
|
}
|
|
56
54
|
/**
|
|
@@ -62,6 +60,9 @@ export function withTokenForge(handler, options) {
|
|
|
62
60
|
export async function tokenForgeCheck(req, options) {
|
|
63
61
|
const apiBase = options.apiBase ?? DEFAULT_API_BASE;
|
|
64
62
|
const url = new URL(req.url);
|
|
63
|
+
if (shouldSkip(url.pathname, options.skipPaths)) {
|
|
64
|
+
return { proceed: true, tf: { bound: false, trustScore: 0, deviceId: null } };
|
|
65
|
+
}
|
|
65
66
|
const header = (name) => req.headers.get(name);
|
|
66
67
|
const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
|
|
67
68
|
method: 'POST',
|
|
@@ -98,4 +99,56 @@ export async function tokenForgeCheck(req, options) {
|
|
|
98
99
|
tf: { bound: data.bound, trustScore: data.trustScore, deviceId: data.deviceId },
|
|
99
100
|
};
|
|
100
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Per-route step-up gate. Wrap an inner handler that already received a
|
|
104
|
+
* `TfContext` (typically via `withTokenForge`). Returns 403 with
|
|
105
|
+
* `elevated_trust_required` when the device's `trustScore` is below the
|
|
106
|
+
* threshold (default 90).
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* export const POST = withTokenForge(
|
|
110
|
+
* withFreshSig(adminHandler, { minTrustScore: 90 }),
|
|
111
|
+
* { apiKey: process.env.TOKENFORGE_API_KEY! },
|
|
112
|
+
* );
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function withFreshSig(handler, opts = {}) {
|
|
116
|
+
const min = opts.minTrustScore ?? 90;
|
|
117
|
+
return async (req, tf) => {
|
|
118
|
+
if (!tf || tf.trustScore < min) {
|
|
119
|
+
return new Response(JSON.stringify({
|
|
120
|
+
error: 'elevated_trust_required',
|
|
121
|
+
action: 'step_up_required',
|
|
122
|
+
trustScore: tf?.trustScore ?? 0,
|
|
123
|
+
}), { status: 403, headers: { 'Content-Type': 'application/json' } });
|
|
124
|
+
}
|
|
125
|
+
return handler(req, tf);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Cross-adapter alias for {@link withFreshSig}. The 5 non-Next.js adapters
|
|
130
|
+
* (astro / express / fastify / hono / sveltekit) all export `requireFreshSig`;
|
|
131
|
+
* exposing the same name here keeps the per-adapter docs uniform so the
|
|
132
|
+
* Sprint 39 step-up snippet doesn't fork by framework.
|
|
133
|
+
*/
|
|
134
|
+
export const requireFreshSig = withFreshSig;
|
|
135
|
+
function shouldSkip(path, skipPaths) {
|
|
136
|
+
return skipPaths?.some((p) => p.endsWith('*') ? path.startsWith(p.slice(0, -1)) : path === p) ?? false;
|
|
137
|
+
}
|
|
138
|
+
/** Wrap a Next.js route handler with the Cloud v2 local and remote gates. */
|
|
139
|
+
export function withTokenForgeCloudV2(handler, options) {
|
|
140
|
+
const gate = createTokenForgeCloudV2Gate(options);
|
|
141
|
+
return async (request) => {
|
|
142
|
+
const rejection = await gate(request);
|
|
143
|
+
return rejection ?? handler(request);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** Run only the Cloud v2 authorization decision for middleware composition. */
|
|
147
|
+
export function tokenForgeCloudV2Check(options) {
|
|
148
|
+
return createTokenForgeCloudV2Gate(options);
|
|
149
|
+
}
|
|
150
|
+
/** Expose the generic Cloud v2 bind handler as a Next.js route handler. */
|
|
151
|
+
export function tokenForgeCloudV2BindRoute(handler) {
|
|
152
|
+
return handler;
|
|
153
|
+
}
|
|
101
154
|
//# sourceMappingURL=nextjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nextjs.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"nextjs.js","sourceRoot":"../src/","sources":["adapters/nextjs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type TokenForgeCloudV2AdapterOptions, type TokenForgeCloudV2RequestHandler } from './cloud-v2-bridge.js';
|
|
2
|
+
/** TokenForge context attached to event.locals.tf */
|
|
3
|
+
export interface TfContext {
|
|
4
|
+
bound: boolean;
|
|
5
|
+
trustScore: number;
|
|
6
|
+
deviceId: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** Options for the SvelteKit adapter. */
|
|
9
|
+
export interface TokenForgeSvelteKitOptions {
|
|
10
|
+
/** Your TokenForge API key (starts with `tf_`). */
|
|
11
|
+
apiKey: string;
|
|
12
|
+
/** API base URL (defaults to TokenForge cloud). */
|
|
13
|
+
apiBase?: string;
|
|
14
|
+
/** Paths to skip verification. */
|
|
15
|
+
skipPaths?: string[];
|
|
16
|
+
}
|
|
17
|
+
/** Minimal SvelteKit Handle event surface — avoids @sveltejs/kit dep. */
|
|
18
|
+
interface RequestEvent {
|
|
19
|
+
request: Request;
|
|
20
|
+
url: URL;
|
|
21
|
+
locals: {
|
|
22
|
+
tf?: TfContext;
|
|
23
|
+
};
|
|
24
|
+
getClientAddress?: () => string;
|
|
25
|
+
}
|
|
26
|
+
type Handle = (input: {
|
|
27
|
+
event: RequestEvent;
|
|
28
|
+
resolve: (event: RequestEvent) => Promise<Response>;
|
|
29
|
+
}) => Promise<Response>;
|
|
30
|
+
/**
|
|
31
|
+
* SvelteKit `handle` hook for TokenForge verification via the cloud API.
|
|
32
|
+
*
|
|
33
|
+
* Usage:
|
|
34
|
+
* ```ts
|
|
35
|
+
* // src/hooks.server.ts
|
|
36
|
+
* import { tokenForgeHandle } from '@opensyber/tokenforge/sveltekit';
|
|
37
|
+
* export const handle = tokenForgeHandle({ apiKey: process.env.TOKENFORGE_API_KEY! });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* Reads `event.locals.tf` for downstream `+page.server.ts` / `+server.ts` use.
|
|
41
|
+
*/
|
|
42
|
+
export declare function tokenForgeHandle(options: TokenForgeSvelteKitOptions): Handle;
|
|
43
|
+
/**
|
|
44
|
+
* Per-route step-up gate. Call inside a `+page.server.ts` `load` or
|
|
45
|
+
* `+server.ts` handler after `tokenForgeHandle` set `event.locals.tf`.
|
|
46
|
+
*
|
|
47
|
+
* Throws a 403 `Response` when below threshold; SvelteKit propagates it
|
|
48
|
+
* up. Returns `void` on pass.
|
|
49
|
+
*/
|
|
50
|
+
export declare function requireFreshSig(event: {
|
|
51
|
+
locals: {
|
|
52
|
+
tf?: TfContext;
|
|
53
|
+
};
|
|
54
|
+
}, opts?: {
|
|
55
|
+
minTrustScore?: number;
|
|
56
|
+
}): void;
|
|
57
|
+
export type TokenForgeSvelteKitCloudV2Options = TokenForgeCloudV2AdapterOptions;
|
|
58
|
+
/** SvelteKit handle hook backed by the shared fail-closed Cloud v2 contract. */
|
|
59
|
+
export declare function tokenForgeCloudV2Handle(options: TokenForgeSvelteKitCloudV2Options): Handle;
|
|
60
|
+
export interface TokenForgeSvelteKitCloudV2RouteEvent {
|
|
61
|
+
request: Request;
|
|
62
|
+
}
|
|
63
|
+
/** Adapt the generic Cloud v2 bind handler to a SvelteKit server route. */
|
|
64
|
+
export declare function tokenForgeCloudV2BindRoute(handler: TokenForgeCloudV2RequestHandler): (event: TokenForgeSvelteKitCloudV2RouteEvent) => Promise<Response>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=sveltekit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sveltekit.d.ts","sourceRoot":"../src/","sources":["adapters/sveltekit.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,+BAA+B,EACrC,MAAM,sBAAsB,CAAC;AAI9B,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,yCAAyC;AACzC,MAAM,WAAW,0BAA0B;IACzC,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,yEAAyE;AACzE,UAAU,YAAY;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAC;CACjC;AAED,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrD,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAExB;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,CAqE5E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;CAAE,EACrC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC,IAAI,CAaN;AASD,MAAM,MAAM,iCAAiC,GAAG,+BAA+B,CAAC;AAEhF,gFAAgF;AAChF,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iCAAiC,GACzC,MAAM,CAOR;AAED,MAAM,WAAW,oCAAoC;IACnD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,2EAA2E;AAC3E,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,+BAA+B,IAGtC,OAAO,oCAAoC,KAC1C,OAAO,CAAC,QAAQ,CAAC,CACrB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createTokenForgeCloudV2Gate, } from './cloud-v2-bridge.js';
|
|
2
|
+
const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
|
|
3
|
+
/**
|
|
4
|
+
* SvelteKit `handle` hook for TokenForge verification via the cloud API.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* ```ts
|
|
8
|
+
* // src/hooks.server.ts
|
|
9
|
+
* import { tokenForgeHandle } from '@opensyber/tokenforge/sveltekit';
|
|
10
|
+
* export const handle = tokenForgeHandle({ apiKey: process.env.TOKENFORGE_API_KEY! });
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Reads `event.locals.tf` for downstream `+page.server.ts` / `+server.ts` use.
|
|
14
|
+
*/
|
|
15
|
+
export function tokenForgeHandle(options) {
|
|
16
|
+
const apiBase = options.apiBase ?? DEFAULT_API_BASE;
|
|
17
|
+
return async ({ event, resolve }) => {
|
|
18
|
+
const path = event.url.pathname;
|
|
19
|
+
if (shouldSkip(path, options.skipPaths)) {
|
|
20
|
+
event.locals.tf = { bound: false, trustScore: 0, deviceId: null };
|
|
21
|
+
return resolve(event);
|
|
22
|
+
}
|
|
23
|
+
const header = (name) => event.request.headers.get(name);
|
|
24
|
+
try {
|
|
25
|
+
const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({
|
|
32
|
+
path,
|
|
33
|
+
method: event.request.method,
|
|
34
|
+
headers: {
|
|
35
|
+
signature: header('x-tf-signature'),
|
|
36
|
+
nonce: header('x-tf-nonce'),
|
|
37
|
+
timestamp: header('x-tf-timestamp'),
|
|
38
|
+
deviceId: header('x-tf-device-id'),
|
|
39
|
+
},
|
|
40
|
+
ipAddress: event.getClientAddress?.() ?? header('x-forwarded-for') ?? '',
|
|
41
|
+
countryCode: header('cf-ipcountry') ?? '',
|
|
42
|
+
userAgent: header('user-agent') ?? '',
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
if (!apiRes.ok) {
|
|
46
|
+
event.locals.tf = { bound: false, trustScore: 0, deviceId: null };
|
|
47
|
+
return resolve(event);
|
|
48
|
+
}
|
|
49
|
+
const { data } = (await apiRes.json());
|
|
50
|
+
if (data.status === 'block') {
|
|
51
|
+
return new Response(JSON.stringify({ error: 'session_blocked', reason: data.reason }), { status: 401, headers: { 'Content-Type': 'application/json' } });
|
|
52
|
+
}
|
|
53
|
+
event.locals.tf = {
|
|
54
|
+
bound: data.bound,
|
|
55
|
+
trustScore: data.trustScore,
|
|
56
|
+
deviceId: data.deviceId,
|
|
57
|
+
};
|
|
58
|
+
return resolve(event);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
event.locals.tf = { bound: false, trustScore: 0, deviceId: null };
|
|
62
|
+
return resolve(event);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Per-route step-up gate. Call inside a `+page.server.ts` `load` or
|
|
68
|
+
* `+server.ts` handler after `tokenForgeHandle` set `event.locals.tf`.
|
|
69
|
+
*
|
|
70
|
+
* Throws a 403 `Response` when below threshold; SvelteKit propagates it
|
|
71
|
+
* up. Returns `void` on pass.
|
|
72
|
+
*/
|
|
73
|
+
export function requireFreshSig(event, opts = {}) {
|
|
74
|
+
const min = opts.minTrustScore ?? 90;
|
|
75
|
+
const score = event.locals.tf?.trustScore ?? 0;
|
|
76
|
+
if (score < min) {
|
|
77
|
+
throw new Response(JSON.stringify({
|
|
78
|
+
error: 'elevated_trust_required',
|
|
79
|
+
action: 'step_up_required',
|
|
80
|
+
trustScore: score,
|
|
81
|
+
}), { status: 403, headers: { 'Content-Type': 'application/json' } });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function shouldSkip(path, skipPaths) {
|
|
85
|
+
if (!skipPaths)
|
|
86
|
+
return false;
|
|
87
|
+
return skipPaths.some((p) => p.endsWith('*') ? path.startsWith(p.slice(0, -1)) : path === p);
|
|
88
|
+
}
|
|
89
|
+
/** SvelteKit handle hook backed by the shared fail-closed Cloud v2 contract. */
|
|
90
|
+
export function tokenForgeCloudV2Handle(options) {
|
|
91
|
+
const gate = createTokenForgeCloudV2Gate(options);
|
|
92
|
+
return async ({ event, resolve }) => {
|
|
93
|
+
const rejection = await gate(event.request);
|
|
94
|
+
return rejection ?? resolve(event);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Adapt the generic Cloud v2 bind handler to a SvelteKit server route. */
|
|
98
|
+
export function tokenForgeCloudV2BindRoute(handler) {
|
|
99
|
+
return async (event) => handler(event.request);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=sveltekit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sveltekit.js","sourceRoot":"../src/","sources":["adapters/sveltekit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action signing client SDK (Sprint 39).
|
|
3
|
+
*
|
|
4
|
+
* Produces a compact-form JWS that binds the user's intent to perform a
|
|
5
|
+
* specific action (e.g. `checkout`, `password_change`, `admin_grant`) to
|
|
6
|
+
* their device-bound private key. Server verifies via the existing
|
|
7
|
+
* `verifyCompactJws` (server/jws-verify.ts), then asserts the
|
|
8
|
+
* `action` + `actionHash` claims match the request body.
|
|
9
|
+
*
|
|
10
|
+
* Even if a session cookie is stolen and replayed, the action signature
|
|
11
|
+
* is bound to the action payload — replay against a different action
|
|
12
|
+
* fails actionHash match; replay against the same action fails the
|
|
13
|
+
* one-shot nonce check (caller's responsibility, see action-verify.ts
|
|
14
|
+
* for the server side).
|
|
15
|
+
*/
|
|
16
|
+
export interface ActionPayload {
|
|
17
|
+
/** Required action verb the server will assert. */
|
|
18
|
+
action: string;
|
|
19
|
+
/** Arbitrary payload fields (e.g. amount, recipient). Hashed into actionHash. */
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface SignActionOptions {
|
|
23
|
+
/** Device-bound private key from the bind ceremony (CryptoKey). */
|
|
24
|
+
privateKey: CryptoKey;
|
|
25
|
+
/** Bound DBSC sessionId — becomes the JWS `sub` claim. */
|
|
26
|
+
sessionId: string;
|
|
27
|
+
/** Public-key id (optional `kid` header). */
|
|
28
|
+
kid?: string;
|
|
29
|
+
/** TTL in seconds (default 60). */
|
|
30
|
+
ttlSeconds?: number;
|
|
31
|
+
/** Test override for clock. */
|
|
32
|
+
now?: () => Date;
|
|
33
|
+
/** Test override for nonce (default: crypto.randomUUID()). */
|
|
34
|
+
nonce?: string;
|
|
35
|
+
/**
|
|
36
|
+
* RFC 9266 TLS channel-binding token (hex). When the runtime exposes
|
|
37
|
+
* the TLS exporter (e.g. self-host node:tls) the caller can mix it
|
|
38
|
+
* into the signed claims so the JWS cannot be replayed across a
|
|
39
|
+
* different TLS connection. workerd does not expose this material;
|
|
40
|
+
* pass undefined and the server emits Sec-TF-Channel-Bound: 0.
|
|
41
|
+
*/
|
|
42
|
+
tlsExporter?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface SignActionResult {
|
|
45
|
+
/** Compact-form JWS string `<header>.<payload>.<signature>`. */
|
|
46
|
+
jws: string;
|
|
47
|
+
/** Server-side hash of the action payload — also embedded in claims. */
|
|
48
|
+
actionHash: string;
|
|
49
|
+
/** Per-call nonce — server should verify it has not been seen before. */
|
|
50
|
+
nonce: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function signAction(payload: ActionPayload, opts: SignActionOptions): Promise<SignActionResult>;
|
|
53
|
+
//# sourceMappingURL=action-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-signer.d.ts","sourceRoot":"../src/","sources":["client/action-signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,UAAU,EAAE,SAAS,CAAC;IACtB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAqC3B"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action signing client SDK (Sprint 39).
|
|
3
|
+
*
|
|
4
|
+
* Produces a compact-form JWS that binds the user's intent to perform a
|
|
5
|
+
* specific action (e.g. `checkout`, `password_change`, `admin_grant`) to
|
|
6
|
+
* their device-bound private key. Server verifies via the existing
|
|
7
|
+
* `verifyCompactJws` (server/jws-verify.ts), then asserts the
|
|
8
|
+
* `action` + `actionHash` claims match the request body.
|
|
9
|
+
*
|
|
10
|
+
* Even if a session cookie is stolen and replayed, the action signature
|
|
11
|
+
* is bound to the action payload — replay against a different action
|
|
12
|
+
* fails actionHash match; replay against the same action fails the
|
|
13
|
+
* one-shot nonce check (caller's responsibility, see action-verify.ts
|
|
14
|
+
* for the server side).
|
|
15
|
+
*/
|
|
16
|
+
import { canonicalizeAction, sha256B64Url, b64UrlEncodeBytes, } from '../shared/action-hash.js';
|
|
17
|
+
const DEFAULT_TTL = 60;
|
|
18
|
+
export async function signAction(payload, opts) {
|
|
19
|
+
if (!payload || typeof payload.action !== 'string' || payload.action.length === 0) {
|
|
20
|
+
throw new Error('signAction: payload.action is required');
|
|
21
|
+
}
|
|
22
|
+
const now = opts.now?.() ?? new Date();
|
|
23
|
+
const iat = Math.floor(now.getTime() / 1000);
|
|
24
|
+
const exp = iat + (opts.ttlSeconds ?? DEFAULT_TTL);
|
|
25
|
+
const nonce = opts.nonce ?? crypto.randomUUID();
|
|
26
|
+
const actionHash = await sha256B64Url(canonicalizeAction(payload));
|
|
27
|
+
const header = { alg: 'ES256', typ: 'JWT' };
|
|
28
|
+
if (opts.kid)
|
|
29
|
+
header.kid = opts.kid;
|
|
30
|
+
const claims = {
|
|
31
|
+
sub: opts.sessionId,
|
|
32
|
+
iat,
|
|
33
|
+
exp,
|
|
34
|
+
nonce,
|
|
35
|
+
action: payload.action,
|
|
36
|
+
actionHash,
|
|
37
|
+
};
|
|
38
|
+
if (opts.tlsExporter)
|
|
39
|
+
claims.tlsExporter = opts.tlsExporter;
|
|
40
|
+
const enc = new TextEncoder();
|
|
41
|
+
const headerB64 = b64UrlEncodeBytes(enc.encode(JSON.stringify(header)));
|
|
42
|
+
const claimsB64 = b64UrlEncodeBytes(enc.encode(JSON.stringify(claims)));
|
|
43
|
+
const signingInput = `${headerB64}.${claimsB64}`;
|
|
44
|
+
const sigBuf = await crypto.subtle.sign({ name: 'ECDSA', hash: { name: 'SHA-256' } }, opts.privateKey, enc.encode(signingInput));
|
|
45
|
+
const jws = `${signingInput}.${b64UrlEncodeBytes(new Uint8Array(sigBuf))}`;
|
|
46
|
+
return { jws, actionHash, nonce };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=action-signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-signer.js","sourceRoot":"../src/","sources":["client/action-signer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAyClC,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAsB,EACtB,IAAuB;IAEvB,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClF,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,WAAW,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;IAEhD,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAEnE,MAAM,MAAM,GAA4B,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACrE,IAAI,IAAI,CAAC,GAAG;QAAE,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IAEpC,MAAM,MAAM,GAA4B;QACtC,GAAG,EAAE,IAAI,CAAC,SAAS;QACnB,GAAG;QACH,GAAG;QACH,KAAK;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU;KACX,CAAC;IACF,IAAI,IAAI,CAAC,WAAW;QAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAE5D,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,YAAY,GAAG,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC;IAEjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CACrC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAC5C,IAAI,CAAC,UAAU,EACf,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CACzB,CAAC;IAEF,MAAM,GAAG,GAAG,GAAG,YAAY,IAAI,iBAAiB,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IAC3E,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/client/binding.d.ts
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
|
+
/** Optional per-call options for {@link bindDevice}. */
|
|
2
|
+
export interface BindDeviceOptions {
|
|
3
|
+
/** Bind algorithm preference. Default 'ecdsa' (existing behavior). */
|
|
4
|
+
prefer?: 'webauthn' | 'ecdsa';
|
|
5
|
+
/** Required when prefer === 'webauthn'. */
|
|
6
|
+
userId?: string;
|
|
7
|
+
/** Required when prefer === 'webauthn'. */
|
|
8
|
+
displayName?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Result of a successful bind. WebAuthn binds have no in-memory keypair. */
|
|
11
|
+
export type BindDeviceResult = {
|
|
12
|
+
type: 'ecdsa';
|
|
13
|
+
deviceId: string;
|
|
14
|
+
keyPair: CryptoKeyPair;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'webauthn';
|
|
17
|
+
deviceId: string;
|
|
18
|
+
credentialId: string;
|
|
19
|
+
};
|
|
1
20
|
/**
|
|
2
21
|
* Complete device binding flow — called once after authentication.
|
|
3
22
|
*
|
|
4
23
|
* 1. Check if we already have a valid key for this session
|
|
5
|
-
* 2. If not, generate new keypair
|
|
6
|
-
* 3. Send public key to server to bind to session
|
|
24
|
+
* 2. If not, generate new keypair (or invoke WebAuthn ceremony)
|
|
25
|
+
* 3. Send public key / attestation to server to bind to session
|
|
7
26
|
* 4. Server stores public key and returns device ID
|
|
8
|
-
* 5. Store keypair + device ID locally
|
|
27
|
+
* 5. Store keypair + device ID locally (ECDSA path only — WebAuthn lives
|
|
28
|
+
* on the authenticator)
|
|
9
29
|
*/
|
|
10
|
-
export declare function bindDevice(apiBase: string, sessionId: string): Promise<
|
|
11
|
-
deviceId: string;
|
|
12
|
-
keyPair: CryptoKeyPair;
|
|
13
|
-
}>;
|
|
30
|
+
export declare function bindDevice(apiBase: string, sessionId: string, options?: BindDeviceOptions): Promise<BindDeviceResult>;
|
|
14
31
|
//# sourceMappingURL=binding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"../src/","sources":["client/binding.ts"],"names":[],"mappings":"AAKA,wDAAwD;AACxD,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC9B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAwD3B"}
|
package/dist/client/binding.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { generateDeviceKeyPair, exportPublicKey } from './crypto.js';
|
|
2
2
|
import { storeDeviceKey, getDeviceKey } from './storage.js';
|
|
3
|
+
import { bindDeviceWebAuthn } from './webauthn.js';
|
|
3
4
|
/**
|
|
4
5
|
* Complete device binding flow — called once after authentication.
|
|
5
6
|
*
|
|
6
7
|
* 1. Check if we already have a valid key for this session
|
|
7
|
-
* 2. If not, generate new keypair
|
|
8
|
-
* 3. Send public key to server to bind to session
|
|
8
|
+
* 2. If not, generate new keypair (or invoke WebAuthn ceremony)
|
|
9
|
+
* 3. Send public key / attestation to server to bind to session
|
|
9
10
|
* 4. Server stores public key and returns device ID
|
|
10
|
-
* 5. Store keypair + device ID locally
|
|
11
|
+
* 5. Store keypair + device ID locally (ECDSA path only — WebAuthn lives
|
|
12
|
+
* on the authenticator)
|
|
11
13
|
*/
|
|
12
|
-
export async function bindDevice(apiBase, sessionId) {
|
|
14
|
+
export async function bindDevice(apiBase, sessionId, options) {
|
|
15
|
+
const prefer = options?.prefer ?? 'ecdsa';
|
|
16
|
+
if (prefer === 'webauthn') {
|
|
17
|
+
if (!options?.userId || !options?.displayName) {
|
|
18
|
+
throw new Error('bindDevice(webauthn): userId and displayName are required');
|
|
19
|
+
}
|
|
20
|
+
const { deviceId, credentialId } = await bindDeviceWebAuthn(apiBase, sessionId, options.userId, options.displayName);
|
|
21
|
+
return { type: 'webauthn', deviceId, credentialId };
|
|
22
|
+
}
|
|
23
|
+
// ── ECDSA path (unchanged) ──
|
|
13
24
|
const existing = await getDeviceKey();
|
|
14
25
|
if (existing && existing.sessionId === sessionId) {
|
|
15
|
-
return { deviceId: existing.deviceId, keyPair: existing.keyPair };
|
|
26
|
+
return { type: 'ecdsa', deviceId: existing.deviceId, keyPair: existing.keyPair };
|
|
16
27
|
}
|
|
17
28
|
const keyPair = await generateDeviceKeyPair();
|
|
18
29
|
const publicKeyJwk = await exportPublicKey(keyPair);
|
|
@@ -44,6 +55,6 @@ export async function bindDevice(apiBase, sessionId) {
|
|
|
44
55
|
createdAt: Date.now(),
|
|
45
56
|
sessionId,
|
|
46
57
|
});
|
|
47
|
-
return { deviceId, keyPair };
|
|
58
|
+
return { type: 'ecdsa', deviceId, keyPair };
|
|
48
59
|
}
|
|
49
60
|
//# sourceMappingURL=binding.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"../src/","sources":["client/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAkBnD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,SAAiB,EACjB,OAA2B;IAE3B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC;IAE1C,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,kBAAkB,CACzD,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CACxD,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACtD,CAAC;IAED,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;IACtC,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACjD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;IACnF,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAC9C,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;IAEpD,MAAM,QAAQ,GAAmB;QAC/B,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,gBAAgB,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE;QACpD,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,eAAe,EAAE,CAAC,QAAQ;QAC1D,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,SAAS,EAAE,YAAY;YACvB,SAAS;YACT,QAAQ;SACT,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;IAErE,MAAM,cAAc,CAAC;QACnB,QAAQ;QACR,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PROTOCOL_V2 } from '../protocol/v2.js';
|
|
2
|
+
export interface BrowserDeviceRecord {
|
|
3
|
+
keyPair: CryptoKeyPair;
|
|
4
|
+
localDeviceId: string;
|
|
5
|
+
projectId?: string;
|
|
6
|
+
deviceId?: string;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
expiresAt?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BrowserDeviceStore {
|
|
11
|
+
load(): Promise<BrowserDeviceRecord | null>;
|
|
12
|
+
save(record: BrowserDeviceRecord): Promise<void>;
|
|
13
|
+
clear(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export interface TokenForgeDeviceOptions {
|
|
16
|
+
/** Authenticated application endpoint. This endpoint, not the browser, owns the bearer key. */
|
|
17
|
+
bindEndpoint: string;
|
|
18
|
+
/** Existing application fetch wrapper carrying its normal cookie/session authentication. */
|
|
19
|
+
fetch: typeof fetch;
|
|
20
|
+
/** Expected browser origin. Defaults to location.origin or the bind endpoint origin. */
|
|
21
|
+
origin?: string;
|
|
22
|
+
/** API origins allowed to receive TokenForge proof headers. Defaults to the bind origin. */
|
|
23
|
+
allowedOrigins?: readonly string[];
|
|
24
|
+
/** Exact URL paths that must pass through without TokenForge proof headers. */
|
|
25
|
+
skipPaths?: readonly string[];
|
|
26
|
+
/** Test/custom storage adapter. Production defaults to IndexedDB. */
|
|
27
|
+
store?: BrowserDeviceStore;
|
|
28
|
+
}
|
|
29
|
+
export interface BoundResult {
|
|
30
|
+
protocol: typeof PROTOCOL_V2;
|
|
31
|
+
mode: 'monitor' | 'enforce';
|
|
32
|
+
bound: true;
|
|
33
|
+
projectId: string;
|
|
34
|
+
deviceId: string;
|
|
35
|
+
sessionId: string;
|
|
36
|
+
expiresAt: string;
|
|
37
|
+
}
|
|
38
|
+
export interface UnboundResult {
|
|
39
|
+
protocol: typeof PROTOCOL_V2;
|
|
40
|
+
mode: 'off' | 'monitor';
|
|
41
|
+
bound: false;
|
|
42
|
+
status?: 'unavailable';
|
|
43
|
+
}
|
|
44
|
+
export type BindResult = BoundResult | UnboundResult;
|
|
45
|
+
export declare class TokenForgeDevice {
|
|
46
|
+
#private;
|
|
47
|
+
private constructor();
|
|
48
|
+
static open(options: TokenForgeDeviceOptions): Promise<TokenForgeDevice>;
|
|
49
|
+
get privateKeyExtractable(): boolean;
|
|
50
|
+
get deviceId(): string | null;
|
|
51
|
+
bindAuthenticatedSession(): Promise<BindResult>;
|
|
52
|
+
wrapFetch(authenticatedFetch?: typeof fetch): typeof fetch;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=cloud-device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-device.d.ts","sourceRoot":"../src/","sources":["client/cloud-device.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAMZ,MAAM,mBAAmB,CAAC;AAkB3B,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,aAAa,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,+FAA+F;IAC/F,YAAY,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,qEAAqE;IACrE,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,WAAW,CAAC;IAC7B,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE,IAAI,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,WAAW,CAAC;IAC7B,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;AAErD,qBAAa,gBAAgB;;IAO3B,OAAO;WAcM,IAAI,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8C9E,IAAI,qBAAqB,IAAI,OAAO,CAEnC;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAEK,wBAAwB,IAAI,OAAO,CAAC,UAAU,CAAC;IAwCrD,SAAS,CAAC,kBAAkB,GAAE,OAAO,KAA2B,GAAG,OAAO,KAAK;CAuDhF"}
|