@authon/node 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.
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @authon/node
2
+
3
+ Node.js server SDK for [Authon](https://authon.dev) — verify tokens, manage users, and validate webhooks.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @authon/node
9
+ # or
10
+ pnpm add @authon/node
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### Token Verification
16
+
17
+ ```ts
18
+ import { AuthonBackend } from '@authon/node';
19
+
20
+ const authon = new AuthonBackend('sk_live_...');
21
+
22
+ const user = await authon.verifyToken(accessToken);
23
+ console.log(user.id, user.email);
24
+ ```
25
+
26
+ ### Express Middleware
27
+
28
+ ```ts
29
+ import express from 'express';
30
+ import { expressMiddleware } from '@authon/node';
31
+
32
+ const app = express();
33
+
34
+ app.use('/api', expressMiddleware({
35
+ secretKey: process.env.AUTHON_SECRET_KEY!,
36
+ }));
37
+
38
+ app.get('/api/profile', (req, res) => {
39
+ // req.auth is the verified AuthonUser
40
+ res.json({ user: req.auth });
41
+ });
42
+ ```
43
+
44
+ ### Fastify Plugin
45
+
46
+ ```ts
47
+ import Fastify from 'fastify';
48
+ import { fastifyPlugin } from '@authon/node';
49
+
50
+ const app = Fastify();
51
+
52
+ app.addHook('onRequest', fastifyPlugin({
53
+ secretKey: process.env.AUTHON_SECRET_KEY!,
54
+ }));
55
+
56
+ app.get('/api/profile', (request, reply) => {
57
+ reply.send({ user: request.auth });
58
+ });
59
+ ```
60
+
61
+ ### User Management
62
+
63
+ ```ts
64
+ const authon = new AuthonBackend('sk_live_...');
65
+
66
+ // List users
67
+ const { data, total } = await authon.users.list({ page: 1, limit: 20 });
68
+
69
+ // Get a user
70
+ const user = await authon.users.get('user_abc123');
71
+
72
+ // Create a user
73
+ const newUser = await authon.users.create({
74
+ email: 'new@example.com',
75
+ password: 'securePassword',
76
+ displayName: 'New User',
77
+ });
78
+
79
+ // Update a user
80
+ await authon.users.update('user_abc123', {
81
+ displayName: 'Updated Name',
82
+ publicMetadata: { role: 'admin' },
83
+ });
84
+
85
+ // Ban / unban
86
+ await authon.users.ban('user_abc123', 'Violation of ToS');
87
+ await authon.users.unban('user_abc123');
88
+
89
+ // Delete a user
90
+ await authon.users.delete('user_abc123');
91
+ ```
92
+
93
+ ### Webhook Verification
94
+
95
+ ```ts
96
+ import { AuthonBackend } from '@authon/node';
97
+
98
+ const authon = new AuthonBackend('sk_live_...');
99
+
100
+ app.post('/webhooks/authon', express.raw({ type: 'application/json' }), (req, res) => {
101
+ const signature = req.headers['x-authon-signature'] as string;
102
+ try {
103
+ const event = authon.webhooks.verify(req.body, signature, process.env.WEBHOOK_SECRET!);
104
+ console.log('Event:', event);
105
+ res.sendStatus(200);
106
+ } catch (err) {
107
+ res.sendStatus(400);
108
+ }
109
+ });
110
+ ```
111
+
112
+ ## API Reference
113
+
114
+ ### `AuthonBackend`
115
+
116
+ | Method | Returns | Description |
117
+ |--------|---------|-------------|
118
+ | `verifyToken(token)` | `Promise<AuthonUser>` | Verify an access token |
119
+ | `users.list(options?)` | `Promise<ListResult<AuthonUser>>` | List users with pagination |
120
+ | `users.get(id)` | `Promise<AuthonUser>` | Get a user by ID |
121
+ | `users.create(data)` | `Promise<AuthonUser>` | Create a user |
122
+ | `users.update(id, data)` | `Promise<AuthonUser>` | Update a user |
123
+ | `users.delete(id)` | `Promise<void>` | Delete a user |
124
+ | `users.ban(id, reason?)` | `Promise<AuthonUser>` | Ban a user |
125
+ | `users.unban(id)` | `Promise<AuthonUser>` | Unban a user |
126
+ | `webhooks.verify(payload, signature, secret)` | `Record<string, unknown>` | Verify webhook HMAC-SHA256 signature |
127
+
128
+ ### Middleware
129
+
130
+ | Export | Description |
131
+ |--------|-------------|
132
+ | `expressMiddleware(options)` | Express/Connect middleware, sets `req.auth` |
133
+ | `fastifyPlugin(options)` | Fastify onRequest hook, sets `request.auth` |
134
+
135
+ ## Documentation
136
+
137
+ [authon.dev/docs](https://authon.dev/docs)
138
+
139
+ ## License
140
+
141
+ [MIT](../../LICENSE)
package/dist/index.cjs ADDED
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthonBackend: () => AuthonBackend,
24
+ expressMiddleware: () => expressMiddleware,
25
+ fastifyPlugin: () => fastifyPlugin
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/authon.ts
30
+ var import_crypto = require("crypto");
31
+ var AuthonBackend = class {
32
+ secretKey;
33
+ apiUrl;
34
+ constructor(secretKey, config) {
35
+ this.secretKey = secretKey;
36
+ this.apiUrl = config?.apiUrl || "https://api.authon.dev";
37
+ }
38
+ async verifyToken(accessToken) {
39
+ return this.request("GET", "/v1/auth/token/verify", void 0, {
40
+ Authorization: `Bearer ${accessToken}`
41
+ });
42
+ }
43
+ users = {
44
+ list: (options) => {
45
+ const params = new URLSearchParams();
46
+ if (options?.page) params.set("page", String(options.page));
47
+ if (options?.limit) params.set("limit", String(options.limit));
48
+ if (options?.search) params.set("search", options.search);
49
+ const qs = params.toString();
50
+ return this.request("GET", `/v1/users${qs ? `?${qs}` : ""}`);
51
+ },
52
+ get: (userId) => {
53
+ return this.request("GET", `/v1/users/${userId}`);
54
+ },
55
+ create: (data) => {
56
+ return this.request("POST", "/v1/users", data);
57
+ },
58
+ update: (userId, data) => {
59
+ return this.request("PATCH", `/v1/users/${userId}`, data);
60
+ },
61
+ delete: (userId) => {
62
+ return this.request("DELETE", `/v1/users/${userId}`);
63
+ },
64
+ ban: (userId, reason) => {
65
+ return this.request("POST", `/v1/users/${userId}/ban`, { reason });
66
+ },
67
+ unban: (userId) => {
68
+ return this.request("POST", `/v1/users/${userId}/unban`);
69
+ }
70
+ };
71
+ webhooks = {
72
+ verify: (payload, signature, secret) => {
73
+ const body = typeof payload === "string" ? payload : payload.toString("utf8");
74
+ const expected = (0, import_crypto.createHmac)("sha256", secret).update(body).digest("hex");
75
+ const actual = signature.replace("sha256=", "");
76
+ const expectedBuf = Buffer.from(expected, "hex");
77
+ const actualBuf = Buffer.from(actual, "hex");
78
+ if (expectedBuf.length !== actualBuf.length || !(0, import_crypto.timingSafeEqual)(expectedBuf, actualBuf)) {
79
+ throw new Error("Invalid webhook signature");
80
+ }
81
+ return JSON.parse(body);
82
+ }
83
+ };
84
+ async request(method, path, body, extraHeaders) {
85
+ const res = await fetch(`${this.apiUrl}${path}`, {
86
+ method,
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ "x-api-key": this.secretKey,
90
+ ...extraHeaders
91
+ },
92
+ body: body ? JSON.stringify(body) : void 0
93
+ });
94
+ if (!res.ok) {
95
+ const text = await res.text();
96
+ throw new Error(`Authon API error ${res.status}: ${text}`);
97
+ }
98
+ if (res.status === 204) return void 0;
99
+ return res.json();
100
+ }
101
+ };
102
+
103
+ // src/middleware.ts
104
+ function expressMiddleware(options) {
105
+ const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });
106
+ return async (req, res, next) => {
107
+ const authHeader = req.headers["authorization"];
108
+ const token = typeof authHeader === "string" ? authHeader.replace("Bearer ", "") : null;
109
+ if (!token) {
110
+ res.status(401).json({ error: "Missing authorization header" });
111
+ return;
112
+ }
113
+ try {
114
+ req.auth = await client.verifyToken(token);
115
+ next();
116
+ } catch (err) {
117
+ options.onError?.(err instanceof Error ? err : new Error(String(err)));
118
+ res.status(401).json({ error: "Invalid token" });
119
+ }
120
+ };
121
+ }
122
+ function fastifyPlugin(options) {
123
+ const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });
124
+ return async (request, reply) => {
125
+ const authHeader = request.headers["authorization"];
126
+ const token = typeof authHeader === "string" ? authHeader.replace("Bearer ", "") : null;
127
+ if (!token) {
128
+ reply.code(401).send({ error: "Missing authorization header" });
129
+ return;
130
+ }
131
+ try {
132
+ request.auth = await client.verifyToken(token);
133
+ } catch (err) {
134
+ options.onError?.(err instanceof Error ? err : new Error(String(err)));
135
+ reply.code(401).send({ error: "Invalid token" });
136
+ }
137
+ };
138
+ }
139
+ // Annotate the CommonJS export names for ESM import in node:
140
+ 0 && (module.exports = {
141
+ AuthonBackend,
142
+ expressMiddleware,
143
+ fastifyPlugin
144
+ });
145
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/authon.ts","../src/middleware.ts"],"sourcesContent":["export { AuthonBackend } from './authon';\nexport { expressMiddleware, fastifyPlugin } from './middleware';\nexport type { AuthonMiddlewareOptions } from './middleware';\n","import type { AuthonUser } from '@authon/shared';\nimport { createHmac, timingSafeEqual } from 'crypto';\n\ninterface AuthonBackendConfig {\n apiUrl?: string;\n}\n\ninterface ListOptions {\n page?: number;\n limit?: number;\n search?: string;\n}\n\ninterface ListResult<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n}\n\nexport class AuthonBackend {\n private secretKey: string;\n private apiUrl: string;\n\n constructor(secretKey: string, config?: AuthonBackendConfig) {\n this.secretKey = secretKey;\n this.apiUrl = config?.apiUrl || 'https://api.authon.dev';\n }\n\n async verifyToken(accessToken: string): Promise<AuthonUser> {\n return this.request<AuthonUser>('GET', '/v1/auth/token/verify', undefined, {\n Authorization: `Bearer ${accessToken}`,\n });\n }\n\n users = {\n list: (options?: ListOptions): Promise<ListResult<AuthonUser>> => {\n const params = new URLSearchParams();\n if (options?.page) params.set('page', String(options.page));\n if (options?.limit) params.set('limit', String(options.limit));\n if (options?.search) params.set('search', options.search);\n const qs = params.toString();\n return this.request('GET', `/v1/users${qs ? `?${qs}` : ''}`);\n },\n\n get: (userId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/users/${userId}`);\n },\n\n create: (data: {\n email: string;\n password?: string;\n displayName?: string;\n }): Promise<AuthonUser> => {\n return this.request('POST', '/v1/users', data);\n },\n\n update: (\n userId: string,\n data: Partial<{ email: string; displayName: string; publicMetadata: Record<string, unknown> }>,\n ): Promise<AuthonUser> => {\n return this.request('PATCH', `/v1/users/${userId}`, data);\n },\n\n delete: (userId: string): Promise<void> => {\n return this.request('DELETE', `/v1/users/${userId}`);\n },\n\n ban: (userId: string, reason?: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/users/${userId}/ban`, { reason });\n },\n\n unban: (userId: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/users/${userId}/unban`);\n },\n };\n\n webhooks = {\n verify: (\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): Record<string, unknown> => {\n const body = typeof payload === 'string' ? payload : payload.toString('utf8');\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const actual = signature.replace('sha256=', '');\n\n const expectedBuf = Buffer.from(expected, 'hex');\n const actualBuf = Buffer.from(actual, 'hex');\n\n if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) {\n throw new Error('Invalid webhook signature');\n }\n\n return JSON.parse(body);\n },\n };\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.secretKey,\n ...extraHeaders,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Authon API error ${res.status}: ${text}`);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n}\n","import type { AuthonUser } from '@authon/shared';\nimport { AuthonBackend } from './authon';\n\nexport interface AuthonMiddlewareOptions {\n secretKey: string;\n apiUrl?: string;\n onError?: (error: Error) => void;\n}\n\nexport function expressMiddleware(options: AuthonMiddlewareOptions) {\n const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });\n\n return async (\n req: { headers: Record<string, string | string[] | undefined>; auth?: AuthonUser },\n res: { status: (code: number) => { json: (body: unknown) => void } },\n next: (err?: unknown) => void,\n ) => {\n const authHeader = req.headers['authorization'];\n const token = typeof authHeader === 'string' ? authHeader.replace('Bearer ', '') : null;\n\n if (!token) {\n res.status(401).json({ error: 'Missing authorization header' });\n return;\n }\n\n try {\n req.auth = await client.verifyToken(token);\n next();\n } catch (err) {\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n res.status(401).json({ error: 'Invalid token' });\n }\n };\n}\n\nexport function fastifyPlugin(options: AuthonMiddlewareOptions) {\n const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });\n\n return async (\n request: { headers: Record<string, string | string[] | undefined>; auth?: AuthonUser },\n reply: { code: (code: number) => { send: (body: unknown) => void } },\n ) => {\n const authHeader = request.headers['authorization'];\n const token = typeof authHeader === 'string' ? authHeader.replace('Bearer ', '') : null;\n\n if (!token) {\n reply.code(401).send({ error: 'Missing authorization header' });\n return;\n }\n\n try {\n request.auth = await client.verifyToken(token);\n } catch (err) {\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n reply.code(401).send({ error: 'Invalid token' });\n }\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,67 @@
1
+ import { AuthonUser } from '@authon/shared';
2
+
3
+ interface AuthonBackendConfig {
4
+ apiUrl?: string;
5
+ }
6
+ interface ListOptions {
7
+ page?: number;
8
+ limit?: number;
9
+ search?: string;
10
+ }
11
+ interface ListResult<T> {
12
+ data: T[];
13
+ total: number;
14
+ page: number;
15
+ limit: number;
16
+ }
17
+ declare class AuthonBackend {
18
+ private secretKey;
19
+ private apiUrl;
20
+ constructor(secretKey: string, config?: AuthonBackendConfig);
21
+ verifyToken(accessToken: string): Promise<AuthonUser>;
22
+ users: {
23
+ list: (options?: ListOptions) => Promise<ListResult<AuthonUser>>;
24
+ get: (userId: string) => Promise<AuthonUser>;
25
+ create: (data: {
26
+ email: string;
27
+ password?: string;
28
+ displayName?: string;
29
+ }) => Promise<AuthonUser>;
30
+ update: (userId: string, data: Partial<{
31
+ email: string;
32
+ displayName: string;
33
+ publicMetadata: Record<string, unknown>;
34
+ }>) => Promise<AuthonUser>;
35
+ delete: (userId: string) => Promise<void>;
36
+ ban: (userId: string, reason?: string) => Promise<AuthonUser>;
37
+ unban: (userId: string) => Promise<AuthonUser>;
38
+ };
39
+ webhooks: {
40
+ verify: (payload: string | Buffer, signature: string, secret: string) => Record<string, unknown>;
41
+ };
42
+ private request;
43
+ }
44
+
45
+ interface AuthonMiddlewareOptions {
46
+ secretKey: string;
47
+ apiUrl?: string;
48
+ onError?: (error: Error) => void;
49
+ }
50
+ declare function expressMiddleware(options: AuthonMiddlewareOptions): (req: {
51
+ headers: Record<string, string | string[] | undefined>;
52
+ auth?: AuthonUser;
53
+ }, res: {
54
+ status: (code: number) => {
55
+ json: (body: unknown) => void;
56
+ };
57
+ }, next: (err?: unknown) => void) => Promise<void>;
58
+ declare function fastifyPlugin(options: AuthonMiddlewareOptions): (request: {
59
+ headers: Record<string, string | string[] | undefined>;
60
+ auth?: AuthonUser;
61
+ }, reply: {
62
+ code: (code: number) => {
63
+ send: (body: unknown) => void;
64
+ };
65
+ }) => Promise<void>;
66
+
67
+ export { AuthonBackend, type AuthonMiddlewareOptions, expressMiddleware, fastifyPlugin };
@@ -0,0 +1,67 @@
1
+ import { AuthonUser } from '@authon/shared';
2
+
3
+ interface AuthonBackendConfig {
4
+ apiUrl?: string;
5
+ }
6
+ interface ListOptions {
7
+ page?: number;
8
+ limit?: number;
9
+ search?: string;
10
+ }
11
+ interface ListResult<T> {
12
+ data: T[];
13
+ total: number;
14
+ page: number;
15
+ limit: number;
16
+ }
17
+ declare class AuthonBackend {
18
+ private secretKey;
19
+ private apiUrl;
20
+ constructor(secretKey: string, config?: AuthonBackendConfig);
21
+ verifyToken(accessToken: string): Promise<AuthonUser>;
22
+ users: {
23
+ list: (options?: ListOptions) => Promise<ListResult<AuthonUser>>;
24
+ get: (userId: string) => Promise<AuthonUser>;
25
+ create: (data: {
26
+ email: string;
27
+ password?: string;
28
+ displayName?: string;
29
+ }) => Promise<AuthonUser>;
30
+ update: (userId: string, data: Partial<{
31
+ email: string;
32
+ displayName: string;
33
+ publicMetadata: Record<string, unknown>;
34
+ }>) => Promise<AuthonUser>;
35
+ delete: (userId: string) => Promise<void>;
36
+ ban: (userId: string, reason?: string) => Promise<AuthonUser>;
37
+ unban: (userId: string) => Promise<AuthonUser>;
38
+ };
39
+ webhooks: {
40
+ verify: (payload: string | Buffer, signature: string, secret: string) => Record<string, unknown>;
41
+ };
42
+ private request;
43
+ }
44
+
45
+ interface AuthonMiddlewareOptions {
46
+ secretKey: string;
47
+ apiUrl?: string;
48
+ onError?: (error: Error) => void;
49
+ }
50
+ declare function expressMiddleware(options: AuthonMiddlewareOptions): (req: {
51
+ headers: Record<string, string | string[] | undefined>;
52
+ auth?: AuthonUser;
53
+ }, res: {
54
+ status: (code: number) => {
55
+ json: (body: unknown) => void;
56
+ };
57
+ }, next: (err?: unknown) => void) => Promise<void>;
58
+ declare function fastifyPlugin(options: AuthonMiddlewareOptions): (request: {
59
+ headers: Record<string, string | string[] | undefined>;
60
+ auth?: AuthonUser;
61
+ }, reply: {
62
+ code: (code: number) => {
63
+ send: (body: unknown) => void;
64
+ };
65
+ }) => Promise<void>;
66
+
67
+ export { AuthonBackend, type AuthonMiddlewareOptions, expressMiddleware, fastifyPlugin };
package/dist/index.js ADDED
@@ -0,0 +1,116 @@
1
+ // src/authon.ts
2
+ import { createHmac, timingSafeEqual } from "crypto";
3
+ var AuthonBackend = class {
4
+ secretKey;
5
+ apiUrl;
6
+ constructor(secretKey, config) {
7
+ this.secretKey = secretKey;
8
+ this.apiUrl = config?.apiUrl || "https://api.authon.dev";
9
+ }
10
+ async verifyToken(accessToken) {
11
+ return this.request("GET", "/v1/auth/token/verify", void 0, {
12
+ Authorization: `Bearer ${accessToken}`
13
+ });
14
+ }
15
+ users = {
16
+ list: (options) => {
17
+ const params = new URLSearchParams();
18
+ if (options?.page) params.set("page", String(options.page));
19
+ if (options?.limit) params.set("limit", String(options.limit));
20
+ if (options?.search) params.set("search", options.search);
21
+ const qs = params.toString();
22
+ return this.request("GET", `/v1/users${qs ? `?${qs}` : ""}`);
23
+ },
24
+ get: (userId) => {
25
+ return this.request("GET", `/v1/users/${userId}`);
26
+ },
27
+ create: (data) => {
28
+ return this.request("POST", "/v1/users", data);
29
+ },
30
+ update: (userId, data) => {
31
+ return this.request("PATCH", `/v1/users/${userId}`, data);
32
+ },
33
+ delete: (userId) => {
34
+ return this.request("DELETE", `/v1/users/${userId}`);
35
+ },
36
+ ban: (userId, reason) => {
37
+ return this.request("POST", `/v1/users/${userId}/ban`, { reason });
38
+ },
39
+ unban: (userId) => {
40
+ return this.request("POST", `/v1/users/${userId}/unban`);
41
+ }
42
+ };
43
+ webhooks = {
44
+ verify: (payload, signature, secret) => {
45
+ const body = typeof payload === "string" ? payload : payload.toString("utf8");
46
+ const expected = createHmac("sha256", secret).update(body).digest("hex");
47
+ const actual = signature.replace("sha256=", "");
48
+ const expectedBuf = Buffer.from(expected, "hex");
49
+ const actualBuf = Buffer.from(actual, "hex");
50
+ if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) {
51
+ throw new Error("Invalid webhook signature");
52
+ }
53
+ return JSON.parse(body);
54
+ }
55
+ };
56
+ async request(method, path, body, extraHeaders) {
57
+ const res = await fetch(`${this.apiUrl}${path}`, {
58
+ method,
59
+ headers: {
60
+ "Content-Type": "application/json",
61
+ "x-api-key": this.secretKey,
62
+ ...extraHeaders
63
+ },
64
+ body: body ? JSON.stringify(body) : void 0
65
+ });
66
+ if (!res.ok) {
67
+ const text = await res.text();
68
+ throw new Error(`Authon API error ${res.status}: ${text}`);
69
+ }
70
+ if (res.status === 204) return void 0;
71
+ return res.json();
72
+ }
73
+ };
74
+
75
+ // src/middleware.ts
76
+ function expressMiddleware(options) {
77
+ const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });
78
+ return async (req, res, next) => {
79
+ const authHeader = req.headers["authorization"];
80
+ const token = typeof authHeader === "string" ? authHeader.replace("Bearer ", "") : null;
81
+ if (!token) {
82
+ res.status(401).json({ error: "Missing authorization header" });
83
+ return;
84
+ }
85
+ try {
86
+ req.auth = await client.verifyToken(token);
87
+ next();
88
+ } catch (err) {
89
+ options.onError?.(err instanceof Error ? err : new Error(String(err)));
90
+ res.status(401).json({ error: "Invalid token" });
91
+ }
92
+ };
93
+ }
94
+ function fastifyPlugin(options) {
95
+ const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });
96
+ return async (request, reply) => {
97
+ const authHeader = request.headers["authorization"];
98
+ const token = typeof authHeader === "string" ? authHeader.replace("Bearer ", "") : null;
99
+ if (!token) {
100
+ reply.code(401).send({ error: "Missing authorization header" });
101
+ return;
102
+ }
103
+ try {
104
+ request.auth = await client.verifyToken(token);
105
+ } catch (err) {
106
+ options.onError?.(err instanceof Error ? err : new Error(String(err)));
107
+ reply.code(401).send({ error: "Invalid token" });
108
+ }
109
+ };
110
+ }
111
+ export {
112
+ AuthonBackend,
113
+ expressMiddleware,
114
+ fastifyPlugin
115
+ };
116
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/authon.ts","../src/middleware.ts"],"sourcesContent":["import type { AuthonUser } from '@authon/shared';\nimport { createHmac, timingSafeEqual } from 'crypto';\n\ninterface AuthonBackendConfig {\n apiUrl?: string;\n}\n\ninterface ListOptions {\n page?: number;\n limit?: number;\n search?: string;\n}\n\ninterface ListResult<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n}\n\nexport class AuthonBackend {\n private secretKey: string;\n private apiUrl: string;\n\n constructor(secretKey: string, config?: AuthonBackendConfig) {\n this.secretKey = secretKey;\n this.apiUrl = config?.apiUrl || 'https://api.authon.dev';\n }\n\n async verifyToken(accessToken: string): Promise<AuthonUser> {\n return this.request<AuthonUser>('GET', '/v1/auth/token/verify', undefined, {\n Authorization: `Bearer ${accessToken}`,\n });\n }\n\n users = {\n list: (options?: ListOptions): Promise<ListResult<AuthonUser>> => {\n const params = new URLSearchParams();\n if (options?.page) params.set('page', String(options.page));\n if (options?.limit) params.set('limit', String(options.limit));\n if (options?.search) params.set('search', options.search);\n const qs = params.toString();\n return this.request('GET', `/v1/users${qs ? `?${qs}` : ''}`);\n },\n\n get: (userId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/users/${userId}`);\n },\n\n create: (data: {\n email: string;\n password?: string;\n displayName?: string;\n }): Promise<AuthonUser> => {\n return this.request('POST', '/v1/users', data);\n },\n\n update: (\n userId: string,\n data: Partial<{ email: string; displayName: string; publicMetadata: Record<string, unknown> }>,\n ): Promise<AuthonUser> => {\n return this.request('PATCH', `/v1/users/${userId}`, data);\n },\n\n delete: (userId: string): Promise<void> => {\n return this.request('DELETE', `/v1/users/${userId}`);\n },\n\n ban: (userId: string, reason?: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/users/${userId}/ban`, { reason });\n },\n\n unban: (userId: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/users/${userId}/unban`);\n },\n };\n\n webhooks = {\n verify: (\n payload: string | Buffer,\n signature: string,\n secret: string,\n ): Record<string, unknown> => {\n const body = typeof payload === 'string' ? payload : payload.toString('utf8');\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const actual = signature.replace('sha256=', '');\n\n const expectedBuf = Buffer.from(expected, 'hex');\n const actualBuf = Buffer.from(actual, 'hex');\n\n if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) {\n throw new Error('Invalid webhook signature');\n }\n\n return JSON.parse(body);\n },\n };\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.secretKey,\n ...extraHeaders,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Authon API error ${res.status}: ${text}`);\n }\n\n if (res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n}\n","import type { AuthonUser } from '@authon/shared';\nimport { AuthonBackend } from './authon';\n\nexport interface AuthonMiddlewareOptions {\n secretKey: string;\n apiUrl?: string;\n onError?: (error: Error) => void;\n}\n\nexport function expressMiddleware(options: AuthonMiddlewareOptions) {\n const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });\n\n return async (\n req: { headers: Record<string, string | string[] | undefined>; auth?: AuthonUser },\n res: { status: (code: number) => { json: (body: unknown) => void } },\n next: (err?: unknown) => void,\n ) => {\n const authHeader = req.headers['authorization'];\n const token = typeof authHeader === 'string' ? authHeader.replace('Bearer ', '') : null;\n\n if (!token) {\n res.status(401).json({ error: 'Missing authorization header' });\n return;\n }\n\n try {\n req.auth = await client.verifyToken(token);\n next();\n } catch (err) {\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n res.status(401).json({ error: 'Invalid token' });\n }\n };\n}\n\nexport function fastifyPlugin(options: AuthonMiddlewareOptions) {\n const client = new AuthonBackend(options.secretKey, { apiUrl: options.apiUrl });\n\n return async (\n request: { headers: Record<string, string | string[] | undefined>; auth?: AuthonUser },\n reply: { code: (code: number) => { send: (body: unknown) => void } },\n ) => {\n const authHeader = request.headers['authorization'];\n const token = typeof authHeader === 'string' ? authHeader.replace('Bearer ', '') : null;\n\n if (!token) {\n reply.code(401).send({ error: 'Missing authorization header' });\n return;\n }\n\n try {\n request.auth = await client.verifyToken(token);\n } catch (err) {\n options.onError?.(err instanceof Error ? err : new Error(String(err)));\n reply.code(401).send({ error: 'Invalid token' });\n }\n };\n}\n"],"mappings":";AACA,SAAS,YAAY,uBAAuB;AAmBrC,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EACA;AAAA,EAER,YAAY,WAAmB,QAA8B;AAC3D,SAAK,YAAY;AACjB,SAAK,SAAS,QAAQ,UAAU;AAAA,EAClC;AAAA,EAEA,MAAM,YAAY,aAA0C;AAC1D,WAAO,KAAK,QAAoB,OAAO,yBAAyB,QAAW;AAAA,MACzE,eAAe,UAAU,WAAW;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ;AAAA,IACN,MAAM,CAAC,YAA2D;AAChE,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,KAAM,QAAO,IAAI,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAC1D,UAAI,SAAS,MAAO,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC7D,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACxD,YAAM,KAAK,OAAO,SAAS;AAC3B,aAAO,KAAK,QAAQ,OAAO,YAAY,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE;AAAA,IAC7D;AAAA,IAEA,KAAK,CAAC,WAAwC;AAC5C,aAAO,KAAK,QAAQ,OAAO,aAAa,MAAM,EAAE;AAAA,IAClD;AAAA,IAEA,QAAQ,CAAC,SAIkB;AACzB,aAAO,KAAK,QAAQ,QAAQ,aAAa,IAAI;AAAA,IAC/C;AAAA,IAEA,QAAQ,CACN,QACA,SACwB;AACxB,aAAO,KAAK,QAAQ,SAAS,aAAa,MAAM,IAAI,IAAI;AAAA,IAC1D;AAAA,IAEA,QAAQ,CAAC,WAAkC;AACzC,aAAO,KAAK,QAAQ,UAAU,aAAa,MAAM,EAAE;AAAA,IACrD;AAAA,IAEA,KAAK,CAAC,QAAgB,WAAyC;AAC7D,aAAO,KAAK,QAAQ,QAAQ,aAAa,MAAM,QAAQ,EAAE,OAAO,CAAC;AAAA,IACnE;AAAA,IAEA,OAAO,CAAC,WAAwC;AAC9C,aAAO,KAAK,QAAQ,QAAQ,aAAa,MAAM,QAAQ;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,WAAW;AAAA,IACT,QAAQ,CACN,SACA,WACA,WAC4B;AAC5B,YAAM,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,MAAM;AAC5E,YAAM,WAAW,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvE,YAAM,SAAS,UAAU,QAAQ,WAAW,EAAE;AAE9C,YAAM,cAAc,OAAO,KAAK,UAAU,KAAK;AAC/C,YAAM,YAAY,OAAO,KAAK,QAAQ,KAAK;AAE3C,UAAI,YAAY,WAAW,UAAU,UAAU,CAAC,gBAAgB,aAAa,SAAS,GAAG;AACvF,cAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C;AAEA,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAc,QACZ,QACA,MACA,MACA,cACY;AACZ,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,MAC/C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,GAAG;AAAA,MACL;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAM,IAAI,MAAM,oBAAoB,IAAI,MAAM,KAAK,IAAI,EAAE;AAAA,IAC3D;AAEA,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;;;ACjHO,SAAS,kBAAkB,SAAkC;AAClE,QAAM,SAAS,IAAI,cAAc,QAAQ,WAAW,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAE9E,SAAO,OACL,KACA,KACA,SACG;AACH,UAAM,aAAa,IAAI,QAAQ,eAAe;AAC9C,UAAM,QAAQ,OAAO,eAAe,WAAW,WAAW,QAAQ,WAAW,EAAE,IAAI;AAEnF,QAAI,CAAC,OAAO;AACV,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAC9D;AAAA,IACF;AAEA,QAAI;AACF,UAAI,OAAO,MAAM,OAAO,YAAY,KAAK;AACzC,WAAK;AAAA,IACP,SAAS,KAAK;AACZ,cAAQ,UAAU,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACrE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAAA,IACjD;AAAA,EACF;AACF;AAEO,SAAS,cAAc,SAAkC;AAC9D,QAAM,SAAS,IAAI,cAAc,QAAQ,WAAW,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAE9E,SAAO,OACL,SACA,UACG;AACH,UAAM,aAAa,QAAQ,QAAQ,eAAe;AAClD,UAAM,QAAQ,OAAO,eAAe,WAAW,WAAW,QAAQ,WAAW,EAAE,IAAI;AAEnF,QAAI,CAAC,OAAO;AACV,YAAM,KAAK,GAAG,EAAE,KAAK,EAAE,OAAO,+BAA+B,CAAC;AAC9D;AAAA,IACF;AAEA,QAAI;AACF,cAAQ,OAAO,MAAM,OAAO,YAAY,KAAK;AAAA,IAC/C,SAAS,KAAK;AACZ,cAAQ,UAAU,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACrE,YAAM,KAAK,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@authon/node",
3
+ "version": "0.1.0",
4
+ "description": "Authon Node.js SDK — verify tokens, manage users, validate webhooks",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "dev": "tsup --watch"
20
+ },
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/mikusnuz/authon-sdk.git",
25
+ "directory": "packages/node"
26
+ },
27
+ "homepage": "https://github.com/mikusnuz/authon-sdk/tree/main/packages/node",
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "keywords": ["authon", "auth", "authentication", "node", "server", "sdk"],
32
+ "dependencies": {
33
+ "@authon/shared": "workspace:*"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.0.0",
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.9.3"
39
+ }
40
+ }