@opensyber/tokenforge 0.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.
Files changed (94) hide show
  1. package/README.md +137 -0
  2. package/dist/adapters/express.d.ts +48 -0
  3. package/dist/adapters/express.d.ts.map +1 -0
  4. package/dist/adapters/express.js +73 -0
  5. package/dist/adapters/express.js.map +1 -0
  6. package/dist/adapters/fastify.d.ts +44 -0
  7. package/dist/adapters/fastify.d.ts.map +1 -0
  8. package/dist/adapters/fastify.js +69 -0
  9. package/dist/adapters/fastify.js.map +1 -0
  10. package/dist/adapters/hono.d.ts +8 -0
  11. package/dist/adapters/hono.d.ts.map +1 -0
  12. package/dist/adapters/hono.js +8 -0
  13. package/dist/adapters/hono.js.map +1 -0
  14. package/dist/adapters/index.d.ts +5 -0
  15. package/dist/adapters/index.d.ts.map +1 -0
  16. package/dist/adapters/index.js +5 -0
  17. package/dist/adapters/index.js.map +1 -0
  18. package/dist/adapters/nextjs.d.ts +51 -0
  19. package/dist/adapters/nextjs.d.ts.map +1 -0
  20. package/dist/adapters/nextjs.js +101 -0
  21. package/dist/adapters/nextjs.js.map +1 -0
  22. package/dist/client/binding.d.ts +14 -0
  23. package/dist/client/binding.d.ts.map +1 -0
  24. package/dist/client/binding.js +49 -0
  25. package/dist/client/binding.js.map +1 -0
  26. package/dist/client/crypto.d.ts +16 -0
  27. package/dist/client/crypto.d.ts.map +1 -0
  28. package/dist/client/crypto.js +22 -0
  29. package/dist/client/crypto.js.map +1 -0
  30. package/dist/client/index.d.ts +38 -0
  31. package/dist/client/index.d.ts.map +1 -0
  32. package/dist/client/index.js +108 -0
  33. package/dist/client/index.js.map +1 -0
  34. package/dist/client/interceptor.d.ts +13 -0
  35. package/dist/client/interceptor.d.ts.map +1 -0
  36. package/dist/client/interceptor.js +85 -0
  37. package/dist/client/interceptor.js.map +1 -0
  38. package/dist/client/signer.d.ts +15 -0
  39. package/dist/client/signer.d.ts.map +1 -0
  40. package/dist/client/signer.js +31 -0
  41. package/dist/client/signer.js.map +1 -0
  42. package/dist/client/storage.d.ts +17 -0
  43. package/dist/client/storage.d.ts.map +1 -0
  44. package/dist/client/storage.js +58 -0
  45. package/dist/client/storage.js.map +1 -0
  46. package/dist/react/index.d.ts +2 -0
  47. package/dist/react/index.d.ts.map +1 -0
  48. package/dist/react/index.js +2 -0
  49. package/dist/react/index.js.map +1 -0
  50. package/dist/react/provider.d.ts +30 -0
  51. package/dist/react/provider.d.ts.map +1 -0
  52. package/dist/react/provider.js +59 -0
  53. package/dist/react/provider.js.map +1 -0
  54. package/dist/server/binding.d.ts +19 -0
  55. package/dist/server/binding.d.ts.map +1 -0
  56. package/dist/server/binding.js +87 -0
  57. package/dist/server/binding.js.map +1 -0
  58. package/dist/server/index.d.ts +7 -0
  59. package/dist/server/index.d.ts.map +1 -0
  60. package/dist/server/index.js +6 -0
  61. package/dist/server/index.js.map +1 -0
  62. package/dist/server/middleware.d.ts +30 -0
  63. package/dist/server/middleware.d.ts.map +1 -0
  64. package/dist/server/middleware.js +107 -0
  65. package/dist/server/middleware.js.map +1 -0
  66. package/dist/server/step-up.d.ts +19 -0
  67. package/dist/server/step-up.d.ts.map +1 -0
  68. package/dist/server/step-up.js +139 -0
  69. package/dist/server/step-up.js.map +1 -0
  70. package/dist/server/storage/index.d.ts +3 -0
  71. package/dist/server/storage/index.d.ts.map +1 -0
  72. package/dist/server/storage/index.js +2 -0
  73. package/dist/server/storage/index.js.map +1 -0
  74. package/dist/server/storage/interface.d.ts +61 -0
  75. package/dist/server/storage/interface.d.ts.map +1 -0
  76. package/dist/server/storage/interface.js +2 -0
  77. package/dist/server/storage/interface.js.map +1 -0
  78. package/dist/server/storage/memory.d.ts +40 -0
  79. package/dist/server/storage/memory.d.ts.map +1 -0
  80. package/dist/server/storage/memory.js +107 -0
  81. package/dist/server/storage/memory.js.map +1 -0
  82. package/dist/server/totp.d.ts +53 -0
  83. package/dist/server/totp.d.ts.map +1 -0
  84. package/dist/server/totp.js +114 -0
  85. package/dist/server/totp.js.map +1 -0
  86. package/dist/shared/types-internal.d.ts +66 -0
  87. package/dist/shared/types-internal.d.ts.map +1 -0
  88. package/dist/shared/types-internal.js +4 -0
  89. package/dist/shared/types-internal.js.map +1 -0
  90. package/dist/shared/types.d.ts +148 -0
  91. package/dist/shared/types.d.ts.map +1 -0
  92. package/dist/shared/types.js +9 -0
  93. package/dist/shared/types.js.map +1 -0
  94. package/package.json +118 -0
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # @opensyber/tokenforge
2
+
3
+ Device-bound session security. Add one script tag — every request is cryptographically signed with a device key that never leaves the browser. A stolen cookie without the device key is useless.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Add the script tag (client — zero code)
8
+
9
+ ```html
10
+ <script
11
+ src="https://tokenforge-api.opensyber.cloud/sdk.js"
12
+ data-api-key="tf_your_api_key"
13
+ ></script>
14
+ ```
15
+
16
+ That's it. The script auto-generates ECDSA P-256 device keys, binds the session, and signs every `fetch()` request with `X-TF-*` headers.
17
+
18
+ ### 2. Add server middleware (one line)
19
+
20
+ ```bash
21
+ npm install @opensyber/tokenforge
22
+ ```
23
+
24
+ **Express**
25
+ ```ts
26
+ import { tokenForgeMiddleware } from '@opensyber/tokenforge/express';
27
+ app.use(tokenForgeMiddleware({ apiKey: process.env.TOKENFORGE_API_KEY! }));
28
+ // req.tf.bound, req.tf.trustScore, req.tf.deviceId
29
+ ```
30
+
31
+ **Next.js**
32
+ ```ts
33
+ import { withTokenForge } from '@opensyber/tokenforge/nextjs';
34
+ export const GET = withTokenForge(handler, { apiKey: process.env.TOKENFORGE_API_KEY! });
35
+ ```
36
+
37
+ **Fastify**
38
+ ```ts
39
+ import { tokenForgePlugin } from '@opensyber/tokenforge/fastify';
40
+ fastify.register(tokenForgePlugin, { apiKey: process.env.TOKENFORGE_API_KEY! });
41
+ ```
42
+
43
+ **Hono**
44
+ ```ts
45
+ import { tokenForgeMiddleware } from '@opensyber/tokenforge/hono';
46
+ app.use('/api/*', tokenForgeMiddleware({ apiKey: env.TOKENFORGE_API_KEY }));
47
+ ```
48
+
49
+ ### 3. Get your API key
50
+
51
+ Sign up free at [tokenforge.opensyber.cloud](https://tokenforge.opensyber.cloud). Free tier: 1,000 verifications/month.
52
+
53
+ ## Why
54
+
55
+ Authentication protects login. TokenForge protects everything **after** login:
56
+
57
+ - **Session hijacking** — stolen cookies are useless without the device's private key
58
+ - **AiTM attacks** — relay proxies can't sign requests on behalf of the device
59
+ - **Token theft** — XSS-exfiltrated tokens fail server-side signature verification
60
+ - **Trust scoring** — 7 signals detect anomalies on every request in real time
61
+
62
+ ## How It Works
63
+
64
+ ```
65
+ Browser TokenForge API Your Server
66
+ │ │ │
67
+ │ 1. Script auto-generates │ │
68
+ │ ECDSA P-256 key pair │ │
69
+ │ │ │
70
+ │ 2. POST /v1/bind ─────────────>│ Store public key │
71
+ │ <──────────────────────────────│ Return deviceId │
72
+ │ │ │
73
+ │ 3. fetch('/api/data') │ │
74
+ │ + X-TF-Signature header ────────────────────────────────> │
75
+ │ │ │
76
+ │ │ <── POST /v1/edge/verify ── │
77
+ │ │ Verify signature │
78
+ │ │ Check nonce replay │
79
+ │ │ Compute trust score │
80
+ │ │ ── { allow, score: 92 } ──> │
81
+ │ │ │
82
+ │ <──────────────────────────────────── 200 OK ─────────────── │
83
+ ```
84
+
85
+ All verification runs on TokenForge's infrastructure. Your server sends request context, gets back allow/deny. No crypto libraries, no database, no storage to manage.
86
+
87
+ ## Trust Score
88
+
89
+ 7 signals scored 0-100 on every request:
90
+
91
+ | Signal | Weight | Detects |
92
+ |--------|--------|---------|
93
+ | Signature | 30 | Tampering, missing device key |
94
+ | IP Address | 15 | IP change since binding |
95
+ | Geo Location | 15 | Country mismatch |
96
+ | Fingerprint | 15 | Browser fingerprint drift |
97
+ | Velocity | 10 | Multiple IPs in short window |
98
+ | Timing | 10 | Clock skew beyond tolerance |
99
+ | Nonce | 5 | Replay attacks |
100
+
101
+ Score >= 80: allow. Score 40-79: step-up auth. Score < 40: block.
102
+
103
+ ## Advanced: npm Client SDK
104
+
105
+ For React apps or when you need more control than the script tag:
106
+
107
+ ```tsx
108
+ import { TokenForgeProvider, useTokenForge } from '@opensyber/tokenforge/react';
109
+
110
+ function App() {
111
+ return (
112
+ <TokenForgeProvider config={{
113
+ apiBase: '/api',
114
+ getSessionId: () => getSession(),
115
+ }}>
116
+ <YourApp />
117
+ </TokenForgeProvider>
118
+ );
119
+ }
120
+ ```
121
+
122
+ ## Works With Any Auth Provider
123
+
124
+ Microsoft Entra ID, Clerk, Auth.js, NextAuth, Firebase, Supabase, Okta, or custom JWT. TokenForge runs after authentication — no changes to your auth flow.
125
+
126
+ ## Pricing
127
+
128
+ | Plan | Price | Verifications/mo |
129
+ |------|-------|-----------------|
130
+ | Free | $0 | 1,000 |
131
+ | Pro | $49/mo | 50,000 |
132
+ | Team | $199/mo | 250,000 |
133
+ | Enterprise | Custom | Unlimited |
134
+
135
+ ## License
136
+
137
+ Client SDK: MIT | Server verification requires a TokenForge API key from [tokenforge.opensyber.cloud](https://tokenforge.opensyber.cloud).
@@ -0,0 +1,48 @@
1
+ /** Express request (minimal interface). */
2
+ interface ExpressRequest {
3
+ path: string;
4
+ method: string;
5
+ headers: Record<string, string | string[] | undefined>;
6
+ ip?: string;
7
+ tf?: TfContext;
8
+ [key: string]: unknown;
9
+ }
10
+ interface ExpressResponse {
11
+ status(code: number): ExpressResponse;
12
+ json(body: unknown): void;
13
+ }
14
+ /** TokenForge context attached to req.tf */
15
+ export interface TfContext {
16
+ bound: boolean;
17
+ trustScore: number;
18
+ deviceId: string | null;
19
+ }
20
+ type NextFunction = (err?: unknown) => void;
21
+ /** Options for the Express middleware. */
22
+ export interface TokenForgeExpressOptions {
23
+ /** Your TokenForge API key (starts with `tf_`). */
24
+ apiKey: string;
25
+ /** API base URL (defaults to TokenForge cloud). */
26
+ apiBase?: string;
27
+ /** Paths to skip verification. */
28
+ skipPaths?: string[];
29
+ /** Paths requiring elevated trust (>90). */
30
+ sensitiveOps?: string[];
31
+ }
32
+ /**
33
+ * Express middleware for TokenForge verification via the cloud API.
34
+ *
35
+ * Usage:
36
+ * ```ts
37
+ * import { tokenForgeMiddleware } from '@opensyber/tokenforge/express';
38
+ * app.use(tokenForgeMiddleware({ apiKey: process.env.TOKENFORGE_API_KEY! }));
39
+ * ```
40
+ *
41
+ * After middleware runs, `req.tf` contains:
42
+ * - `bound: boolean` — whether device is bound
43
+ * - `trustScore: number` — current trust score
44
+ * - `deviceId: string | null` — device identifier
45
+ */
46
+ export declare function tokenForgeMiddleware(options: TokenForgeExpressOptions): (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => Promise<void>;
47
+ export {};
48
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAEA,2CAA2C;AAC3C,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,eAAe;IACvB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAE5C,0CAA0C;AAC1C,MAAM,WAAW,wBAAwB;IACvC,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,wBAAwB,IAGtD,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAwD5F"}
@@ -0,0 +1,73 @@
1
+ const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
2
+ /**
3
+ * Express middleware for TokenForge verification via the cloud API.
4
+ *
5
+ * Usage:
6
+ * ```ts
7
+ * import { tokenForgeMiddleware } from '@opensyber/tokenforge/express';
8
+ * app.use(tokenForgeMiddleware({ apiKey: process.env.TOKENFORGE_API_KEY! }));
9
+ * ```
10
+ *
11
+ * After middleware runs, `req.tf` contains:
12
+ * - `bound: boolean` — whether device is bound
13
+ * - `trustScore: number` — current trust score
14
+ * - `deviceId: string | null` — device identifier
15
+ */
16
+ export function tokenForgeMiddleware(options) {
17
+ const apiBase = options.apiBase ?? DEFAULT_API_BASE;
18
+ return async (req, res, next) => {
19
+ if (shouldSkip(req.path, options.skipPaths)) {
20
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
21
+ next();
22
+ return;
23
+ }
24
+ const header = (name) => {
25
+ const val = req.headers[name.toLowerCase()];
26
+ return typeof val === 'string' ? val : null;
27
+ };
28
+ try {
29
+ const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
30
+ method: 'POST',
31
+ headers: {
32
+ Authorization: `Bearer ${options.apiKey}`,
33
+ 'Content-Type': 'application/json',
34
+ },
35
+ body: JSON.stringify({
36
+ path: req.path,
37
+ method: req.method,
38
+ headers: {
39
+ signature: header('x-tf-signature'),
40
+ nonce: header('x-tf-nonce'),
41
+ timestamp: header('x-tf-timestamp'),
42
+ deviceId: header('x-tf-device-id'),
43
+ },
44
+ ipAddress: req.ip ?? header('x-forwarded-for') ?? '',
45
+ countryCode: header('cf-ipcountry') ?? '',
46
+ userAgent: header('user-agent') ?? '',
47
+ }),
48
+ });
49
+ if (!apiRes.ok) {
50
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
51
+ next();
52
+ return;
53
+ }
54
+ const { data } = (await apiRes.json());
55
+ if (data.status === 'block') {
56
+ res.status(401).json({ error: 'session_blocked', reason: data.reason });
57
+ return;
58
+ }
59
+ req.tf = { bound: data.bound, trustScore: data.trustScore, deviceId: data.deviceId };
60
+ next();
61
+ }
62
+ catch {
63
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
64
+ next();
65
+ }
66
+ };
67
+ }
68
+ function shouldSkip(path, skipPaths) {
69
+ if (!skipPaths)
70
+ return false;
71
+ return skipPaths.some((p) => p.endsWith('*') ? path.startsWith(p.slice(0, -1)) : path === p);
72
+ }
73
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /** TokenForge context attached to request.tf */
2
+ export interface TfContext {
3
+ bound: boolean;
4
+ trustScore: number;
5
+ deviceId: string | null;
6
+ }
7
+ /** Options for the Fastify plugin. */
8
+ export interface TokenForgeFastifyOptions {
9
+ /** Your TokenForge API key (starts with `tf_`). */
10
+ apiKey: string;
11
+ /** API base URL (defaults to TokenForge cloud). */
12
+ apiBase?: string;
13
+ /** Paths to skip verification. */
14
+ skipPaths?: string[];
15
+ }
16
+ interface FastifyRequest {
17
+ url: string;
18
+ method: string;
19
+ headers: Record<string, string | string[] | undefined>;
20
+ ip?: string;
21
+ tf?: TfContext;
22
+ }
23
+ interface FastifyReply {
24
+ code(statusCode: number): FastifyReply;
25
+ send(payload: unknown): void;
26
+ }
27
+ interface FastifyInstance {
28
+ addHook(name: 'preHandler', handler: (req: FastifyRequest, reply: FastifyReply) => Promise<void>): void;
29
+ decorateRequest(name: string, value: unknown): void;
30
+ }
31
+ /**
32
+ * Fastify plugin for TokenForge verification via the cloud API.
33
+ *
34
+ * Usage:
35
+ * ```ts
36
+ * import { tokenForgePlugin } from '@opensyber/tokenforge/fastify';
37
+ * fastify.register(tokenForgePlugin, {
38
+ * apiKey: process.env.TOKENFORGE_API_KEY!,
39
+ * });
40
+ * ```
41
+ */
42
+ export declare function tokenForgePlugin(fastify: FastifyInstance, options: TokenForgeFastifyOptions, done: (err?: Error) => void): void;
43
+ export {};
44
+ //# sourceMappingURL=fastify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"AAEA,gDAAgD;AAChD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,sCAAsC;AACtC,MAAM,WAAW,wBAAwB;IACvC,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,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,SAAS,CAAC;CAChB;AAED,UAAU,YAAY;IACpB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,OAAO,CACL,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GACnE,IAAI,CAAC;IACR,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAC1B,IAAI,CA6DN"}
@@ -0,0 +1,69 @@
1
+ const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
2
+ /**
3
+ * Fastify plugin for TokenForge verification via the cloud API.
4
+ *
5
+ * Usage:
6
+ * ```ts
7
+ * import { tokenForgePlugin } from '@opensyber/tokenforge/fastify';
8
+ * fastify.register(tokenForgePlugin, {
9
+ * apiKey: process.env.TOKENFORGE_API_KEY!,
10
+ * });
11
+ * ```
12
+ */
13
+ export function tokenForgePlugin(fastify, options, done) {
14
+ const apiBase = options.apiBase ?? DEFAULT_API_BASE;
15
+ fastify.decorateRequest('tf', null);
16
+ fastify.addHook('preHandler', async (req, reply) => {
17
+ const path = req.url.split('?')[0] ?? req.url;
18
+ if (shouldSkip(path, options.skipPaths)) {
19
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
20
+ return;
21
+ }
22
+ const header = (name) => {
23
+ const val = req.headers[name.toLowerCase()];
24
+ return typeof val === 'string' ? val : null;
25
+ };
26
+ try {
27
+ const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
28
+ method: 'POST',
29
+ headers: {
30
+ Authorization: `Bearer ${options.apiKey}`,
31
+ 'Content-Type': 'application/json',
32
+ },
33
+ body: JSON.stringify({
34
+ path,
35
+ method: req.method,
36
+ headers: {
37
+ signature: header('x-tf-signature'),
38
+ nonce: header('x-tf-nonce'),
39
+ timestamp: header('x-tf-timestamp'),
40
+ deviceId: header('x-tf-device-id'),
41
+ },
42
+ ipAddress: req.ip ?? header('x-forwarded-for') ?? '',
43
+ countryCode: header('cf-ipcountry') ?? '',
44
+ userAgent: header('user-agent') ?? '',
45
+ }),
46
+ });
47
+ if (!apiRes.ok) {
48
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
49
+ return;
50
+ }
51
+ const { data } = (await apiRes.json());
52
+ if (data.status === 'block') {
53
+ reply.code(401).send({ error: 'session_blocked', reason: data.reason });
54
+ return;
55
+ }
56
+ req.tf = { bound: data.bound, trustScore: data.trustScore, deviceId: data.deviceId };
57
+ }
58
+ catch {
59
+ req.tf = { bound: false, trustScore: 0, deviceId: null };
60
+ }
61
+ });
62
+ done();
63
+ }
64
+ function shouldSkip(path, skipPaths) {
65
+ if (!skipPaths)
66
+ return false;
67
+ return skipPaths.some((p) => p.endsWith('*') ? path.startsWith(p.slice(0, -1)) : path === p);
68
+ }
69
+ //# sourceMappingURL=fastify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.js","sourceRoot":"","sources":["../../src/adapters/fastify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hono adapter — re-export from server/ for the new import path.
3
+ * Usage: import { tokenForgeMiddleware } from '@opensyber/tokenforge/hono'
4
+ */
5
+ export { tokenForgeMiddleware } from '../server/middleware.js';
6
+ export { createTokenForgeRoutes } from '../server/binding.js';
7
+ export { createStepUpRoutes } from '../server/step-up.js';
8
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hono adapter — re-export from server/ for the new import path.
3
+ * Usage: import { tokenForgeMiddleware } from '@opensyber/tokenforge/hono'
4
+ */
5
+ export { tokenForgeMiddleware } from '../server/middleware.js';
6
+ export { createTokenForgeRoutes } from '../server/binding.js';
7
+ export { createStepUpRoutes } from '../server/step-up.js';
8
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { tokenForgeMiddleware as honoMiddleware, createTokenForgeRoutes, createStepUpRoutes } from './hono.js';
2
+ export { tokenForgeMiddleware as expressMiddleware } from './express.js';
3
+ export { withTokenForge, tokenForgeCheck } from './nextjs.js';
4
+ export { tokenForgePlugin } from './fastify.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,IAAI,cAAc,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/G,OAAO,EAAE,oBAAoB,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { tokenForgeMiddleware as honoMiddleware, createTokenForgeRoutes, createStepUpRoutes } from './hono.js';
2
+ export { tokenForgeMiddleware as expressMiddleware } from './express.js';
3
+ export { withTokenForge, tokenForgeCheck } from './nextjs.js';
4
+ export { tokenForgePlugin } from './fastify.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,IAAI,cAAc,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/G,OAAO,EAAE,oBAAoB,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,51 @@
1
+ type NextRequest = Request & {
2
+ nextUrl?: {
3
+ pathname: string;
4
+ };
5
+ };
6
+ /** TokenForge context passed to wrapped handlers. */
7
+ export interface TfContext {
8
+ bound: boolean;
9
+ trustScore: number;
10
+ deviceId: string | null;
11
+ }
12
+ /** Options for the Next.js adapter. */
13
+ export interface TokenForgeNextOptions {
14
+ /** Your TokenForge API key (starts with `tf_`). */
15
+ apiKey: string;
16
+ /** API base URL (defaults to TokenForge cloud). */
17
+ apiBase?: string;
18
+ /** Paths to skip verification. */
19
+ skipPaths?: string[];
20
+ }
21
+ /**
22
+ * Next.js App Router — wrap an API route handler with TokenForge verification.
23
+ *
24
+ * Usage:
25
+ * ```ts
26
+ * import { withTokenForge } from '@opensyber/tokenforge/nextjs';
27
+ *
28
+ * async function handler(req: Request, tf: TfContext) {
29
+ * return Response.json({ bound: tf.bound, score: tf.trustScore });
30
+ * }
31
+ * export const GET = withTokenForge(handler, {
32
+ * apiKey: process.env.TOKENFORGE_API_KEY!,
33
+ * });
34
+ * ```
35
+ */
36
+ export declare function withTokenForge(handler: (req: NextRequest, tf: TfContext) => Promise<Response>, options: TokenForgeNextOptions): (req: NextRequest) => Promise<Response>;
37
+ /**
38
+ * Next.js Middleware compatible function.
39
+ * Use in middleware.ts for edge verification.
40
+ *
41
+ * Returns proceed=true with TfContext, or proceed=false with a Response.
42
+ */
43
+ export declare function tokenForgeCheck(req: NextRequest, options: TokenForgeNextOptions): Promise<{
44
+ proceed: true;
45
+ tf: TfContext;
46
+ } | {
47
+ proceed: false;
48
+ response: Response;
49
+ }>;
50
+ export {};
51
+ //# sourceMappingURL=nextjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,OAAO,GAAG;IAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEhE,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,uCAAuC;AACvC,MAAM,WAAW,qBAAqB;IACpC,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;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,EAC/D,OAAO,EAAE,qBAAqB,IAIhB,KAAK,WAAW,KAAG,OAAO,CAAC,QAAQ,CAAC,CA8CnD;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAgDpF"}
@@ -0,0 +1,101 @@
1
+ const DEFAULT_API_BASE = 'https://tokenforge-api.opensyber.cloud';
2
+ /**
3
+ * Next.js App Router — wrap an API route handler with TokenForge verification.
4
+ *
5
+ * Usage:
6
+ * ```ts
7
+ * import { withTokenForge } from '@opensyber/tokenforge/nextjs';
8
+ *
9
+ * async function handler(req: Request, tf: TfContext) {
10
+ * return Response.json({ bound: tf.bound, score: tf.trustScore });
11
+ * }
12
+ * export const GET = withTokenForge(handler, {
13
+ * apiKey: process.env.TOKENFORGE_API_KEY!,
14
+ * });
15
+ * ```
16
+ */
17
+ export function withTokenForge(handler, options) {
18
+ const apiBase = options.apiBase ?? DEFAULT_API_BASE;
19
+ return async (req) => {
20
+ const url = new URL(req.url);
21
+ const header = (name) => req.headers.get(name);
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) {
43
+ return handler(req, { bound: false, trustScore: 0, deviceId: null });
44
+ }
45
+ const { data } = (await apiRes.json());
46
+ if (data.status === 'block') {
47
+ return new Response(JSON.stringify({ error: 'session_blocked', reason: data.reason }), { status: 401, headers: { 'Content-Type': 'application/json' } });
48
+ }
49
+ return handler(req, {
50
+ bound: data.bound,
51
+ trustScore: data.trustScore,
52
+ deviceId: data.deviceId,
53
+ });
54
+ };
55
+ }
56
+ /**
57
+ * Next.js Middleware compatible function.
58
+ * Use in middleware.ts for edge verification.
59
+ *
60
+ * Returns proceed=true with TfContext, or proceed=false with a Response.
61
+ */
62
+ export async function tokenForgeCheck(req, options) {
63
+ const apiBase = options.apiBase ?? DEFAULT_API_BASE;
64
+ const url = new URL(req.url);
65
+ const header = (name) => req.headers.get(name);
66
+ const apiRes = await fetch(`${apiBase}/v1/edge/verify`, {
67
+ method: 'POST',
68
+ headers: {
69
+ Authorization: `Bearer ${options.apiKey}`,
70
+ 'Content-Type': 'application/json',
71
+ },
72
+ body: JSON.stringify({
73
+ path: url.pathname,
74
+ method: req.method,
75
+ headers: {
76
+ signature: header('x-tf-signature'),
77
+ nonce: header('x-tf-nonce'),
78
+ timestamp: header('x-tf-timestamp'),
79
+ deviceId: header('x-tf-device-id'),
80
+ },
81
+ ipAddress: header('x-forwarded-for') ?? '',
82
+ countryCode: header('cf-ipcountry') ?? '',
83
+ userAgent: header('user-agent') ?? '',
84
+ }),
85
+ });
86
+ if (!apiRes.ok) {
87
+ return { proceed: true, tf: { bound: false, trustScore: 0, deviceId: null } };
88
+ }
89
+ const { data } = (await apiRes.json());
90
+ if (data.status === 'block') {
91
+ return {
92
+ proceed: false,
93
+ response: new Response(JSON.stringify({ error: 'session_blocked', reason: data.reason }), { status: 401, headers: { 'Content-Type': 'application/json' } }),
94
+ };
95
+ }
96
+ return {
97
+ proceed: true,
98
+ tf: { bound: data.bound, trustScore: data.trustScore, deviceId: data.deviceId },
99
+ };
100
+ }
101
+ //# sourceMappingURL=nextjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/adapters/nextjs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Complete device binding flow — called once after authentication.
3
+ *
4
+ * 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
7
+ * 4. Server stores public key and returns device ID
8
+ * 5. Store keypair + device ID locally
9
+ */
10
+ export declare function bindDevice(apiBase: string, sessionId: string): Promise<{
11
+ deviceId: string;
12
+ keyPair: CryptoKeyPair;
13
+ }>;
14
+ //# sourceMappingURL=binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/client/binding.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,CAAA;CAAE,CAAC,CA2CvD"}