@authon/node 0.1.0 → 0.1.15

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/dist/index.cjs CHANGED
@@ -47,32 +47,44 @@ var AuthonBackend = class {
47
47
  if (options?.limit) params.set("limit", String(options.limit));
48
48
  if (options?.search) params.set("search", options.search);
49
49
  const qs = params.toString();
50
- return this.request("GET", `/v1/users${qs ? `?${qs}` : ""}`);
50
+ return this.request("GET", `/v1/backend/users${qs ? `?${qs}` : ""}`);
51
51
  },
52
52
  get: (userId) => {
53
- return this.request("GET", `/v1/users/${userId}`);
53
+ return this.request("GET", `/v1/backend/users/${userId}`);
54
+ },
55
+ getByExternalId: (externalId) => {
56
+ return this.request("GET", `/v1/backend/users/by-external-id/${externalId}`);
54
57
  },
55
58
  create: (data) => {
56
- return this.request("POST", "/v1/users", data);
59
+ return this.request("POST", "/v1/backend/users", data);
57
60
  },
58
61
  update: (userId, data) => {
59
- return this.request("PATCH", `/v1/users/${userId}`, data);
62
+ return this.request("PATCH", `/v1/backend/users/${userId}`, data);
60
63
  },
61
64
  delete: (userId) => {
62
- return this.request("DELETE", `/v1/users/${userId}`);
65
+ return this.request("DELETE", `/v1/backend/users/${userId}`);
63
66
  },
64
67
  ban: (userId, reason) => {
65
- return this.request("POST", `/v1/users/${userId}/ban`, { reason });
68
+ return this.request("POST", `/v1/backend/users/${userId}/ban`, { reason });
66
69
  },
67
70
  unban: (userId) => {
68
- return this.request("POST", `/v1/users/${userId}/unban`);
71
+ return this.request("POST", `/v1/backend/users/${userId}/unban`);
69
72
  }
70
73
  };
71
74
  webhooks = {
72
- verify: (payload, signature, secret) => {
75
+ /**
76
+ * Verify an Authon webhook signature.
77
+ * @param payload - Raw request body (string or Buffer)
78
+ * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)
79
+ * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)
80
+ * @param secret - Your webhook signing secret
81
+ * @returns Parsed payload object
82
+ */
83
+ verify: (payload, signature, timestamp, secret) => {
73
84
  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=", "");
85
+ const signedPayload = `${timestamp}.${body}`;
86
+ const expected = (0, import_crypto.createHmac)("sha256", secret).update(signedPayload).digest("hex");
87
+ const actual = signature.replace("v1=", "");
76
88
  const expectedBuf = Buffer.from(expected, "hex");
77
89
  const actualBuf = Buffer.from(actual, "hex");
78
90
  if (expectedBuf.length !== actualBuf.length || !(0, import_crypto.timingSafeEqual)(expectedBuf, actualBuf)) {
@@ -1 +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":[]}
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/backend/users${qs ? `?${qs}` : ''}`);\n },\n\n get: (userId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/backend/users/${userId}`);\n },\n\n getByExternalId: (externalId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/backend/users/by-external-id/${externalId}`);\n },\n\n create: (data: {\n email: string;\n password?: string;\n displayName?: string;\n externalId?: string;\n provider?: string;\n avatarUrl?: string;\n phone?: string;\n emailVerified?: boolean;\n publicMetadata?: Record<string, unknown>;\n privateMetadata?: Record<string, unknown>;\n }): Promise<AuthonUser> => {\n return this.request('POST', '/v1/backend/users', data);\n },\n\n update: (\n userId: string,\n data: Partial<{\n displayName: string;\n externalId: string;\n avatarUrl: string;\n phone: string;\n emailVerified: boolean;\n publicMetadata: Record<string, unknown>;\n privateMetadata: Record<string, unknown>;\n }>,\n ): Promise<AuthonUser> => {\n return this.request('PATCH', `/v1/backend/users/${userId}`, data);\n },\n\n delete: (userId: string): Promise<void> => {\n return this.request('DELETE', `/v1/backend/users/${userId}`);\n },\n\n ban: (userId: string, reason?: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/backend/users/${userId}/ban`, { reason });\n },\n\n unban: (userId: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/backend/users/${userId}/unban`);\n },\n };\n\n webhooks = {\n /**\n * Verify an Authon webhook signature.\n * @param payload - Raw request body (string or Buffer)\n * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)\n * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)\n * @param secret - Your webhook signing secret\n * @returns Parsed payload object\n */\n verify: (\n payload: string | Buffer,\n signature: string,\n timestamp: string,\n secret: string,\n ): Record<string, unknown> => {\n const body = typeof payload === 'string' ? payload : payload.toString('utf8');\n const signedPayload = `${timestamp}.${body}`;\n const expected = createHmac('sha256', secret).update(signedPayload).digest('hex');\n const actual = signature.replace('v1=', '');\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":[]}
package/dist/index.d.cts CHANGED
@@ -22,22 +22,42 @@ declare class AuthonBackend {
22
22
  users: {
23
23
  list: (options?: ListOptions) => Promise<ListResult<AuthonUser>>;
24
24
  get: (userId: string) => Promise<AuthonUser>;
25
+ getByExternalId: (externalId: string) => Promise<AuthonUser>;
25
26
  create: (data: {
26
27
  email: string;
27
28
  password?: string;
28
29
  displayName?: string;
30
+ externalId?: string;
31
+ provider?: string;
32
+ avatarUrl?: string;
33
+ phone?: string;
34
+ emailVerified?: boolean;
35
+ publicMetadata?: Record<string, unknown>;
36
+ privateMetadata?: Record<string, unknown>;
29
37
  }) => Promise<AuthonUser>;
30
38
  update: (userId: string, data: Partial<{
31
- email: string;
32
39
  displayName: string;
40
+ externalId: string;
41
+ avatarUrl: string;
42
+ phone: string;
43
+ emailVerified: boolean;
33
44
  publicMetadata: Record<string, unknown>;
45
+ privateMetadata: Record<string, unknown>;
34
46
  }>) => Promise<AuthonUser>;
35
47
  delete: (userId: string) => Promise<void>;
36
48
  ban: (userId: string, reason?: string) => Promise<AuthonUser>;
37
49
  unban: (userId: string) => Promise<AuthonUser>;
38
50
  };
39
51
  webhooks: {
40
- verify: (payload: string | Buffer, signature: string, secret: string) => Record<string, unknown>;
52
+ /**
53
+ * Verify an Authon webhook signature.
54
+ * @param payload - Raw request body (string or Buffer)
55
+ * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)
56
+ * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)
57
+ * @param secret - Your webhook signing secret
58
+ * @returns Parsed payload object
59
+ */
60
+ verify: (payload: string | Buffer, signature: string, timestamp: string, secret: string) => Record<string, unknown>;
41
61
  };
42
62
  private request;
43
63
  }
package/dist/index.d.ts CHANGED
@@ -22,22 +22,42 @@ declare class AuthonBackend {
22
22
  users: {
23
23
  list: (options?: ListOptions) => Promise<ListResult<AuthonUser>>;
24
24
  get: (userId: string) => Promise<AuthonUser>;
25
+ getByExternalId: (externalId: string) => Promise<AuthonUser>;
25
26
  create: (data: {
26
27
  email: string;
27
28
  password?: string;
28
29
  displayName?: string;
30
+ externalId?: string;
31
+ provider?: string;
32
+ avatarUrl?: string;
33
+ phone?: string;
34
+ emailVerified?: boolean;
35
+ publicMetadata?: Record<string, unknown>;
36
+ privateMetadata?: Record<string, unknown>;
29
37
  }) => Promise<AuthonUser>;
30
38
  update: (userId: string, data: Partial<{
31
- email: string;
32
39
  displayName: string;
40
+ externalId: string;
41
+ avatarUrl: string;
42
+ phone: string;
43
+ emailVerified: boolean;
33
44
  publicMetadata: Record<string, unknown>;
45
+ privateMetadata: Record<string, unknown>;
34
46
  }>) => Promise<AuthonUser>;
35
47
  delete: (userId: string) => Promise<void>;
36
48
  ban: (userId: string, reason?: string) => Promise<AuthonUser>;
37
49
  unban: (userId: string) => Promise<AuthonUser>;
38
50
  };
39
51
  webhooks: {
40
- verify: (payload: string | Buffer, signature: string, secret: string) => Record<string, unknown>;
52
+ /**
53
+ * Verify an Authon webhook signature.
54
+ * @param payload - Raw request body (string or Buffer)
55
+ * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)
56
+ * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)
57
+ * @param secret - Your webhook signing secret
58
+ * @returns Parsed payload object
59
+ */
60
+ verify: (payload: string | Buffer, signature: string, timestamp: string, secret: string) => Record<string, unknown>;
41
61
  };
42
62
  private request;
43
63
  }
package/dist/index.js CHANGED
@@ -19,32 +19,44 @@ var AuthonBackend = class {
19
19
  if (options?.limit) params.set("limit", String(options.limit));
20
20
  if (options?.search) params.set("search", options.search);
21
21
  const qs = params.toString();
22
- return this.request("GET", `/v1/users${qs ? `?${qs}` : ""}`);
22
+ return this.request("GET", `/v1/backend/users${qs ? `?${qs}` : ""}`);
23
23
  },
24
24
  get: (userId) => {
25
- return this.request("GET", `/v1/users/${userId}`);
25
+ return this.request("GET", `/v1/backend/users/${userId}`);
26
+ },
27
+ getByExternalId: (externalId) => {
28
+ return this.request("GET", `/v1/backend/users/by-external-id/${externalId}`);
26
29
  },
27
30
  create: (data) => {
28
- return this.request("POST", "/v1/users", data);
31
+ return this.request("POST", "/v1/backend/users", data);
29
32
  },
30
33
  update: (userId, data) => {
31
- return this.request("PATCH", `/v1/users/${userId}`, data);
34
+ return this.request("PATCH", `/v1/backend/users/${userId}`, data);
32
35
  },
33
36
  delete: (userId) => {
34
- return this.request("DELETE", `/v1/users/${userId}`);
37
+ return this.request("DELETE", `/v1/backend/users/${userId}`);
35
38
  },
36
39
  ban: (userId, reason) => {
37
- return this.request("POST", `/v1/users/${userId}/ban`, { reason });
40
+ return this.request("POST", `/v1/backend/users/${userId}/ban`, { reason });
38
41
  },
39
42
  unban: (userId) => {
40
- return this.request("POST", `/v1/users/${userId}/unban`);
43
+ return this.request("POST", `/v1/backend/users/${userId}/unban`);
41
44
  }
42
45
  };
43
46
  webhooks = {
44
- verify: (payload, signature, secret) => {
47
+ /**
48
+ * Verify an Authon webhook signature.
49
+ * @param payload - Raw request body (string or Buffer)
50
+ * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)
51
+ * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)
52
+ * @param secret - Your webhook signing secret
53
+ * @returns Parsed payload object
54
+ */
55
+ verify: (payload, signature, timestamp, secret) => {
45
56
  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=", "");
57
+ const signedPayload = `${timestamp}.${body}`;
58
+ const expected = createHmac("sha256", secret).update(signedPayload).digest("hex");
59
+ const actual = signature.replace("v1=", "");
48
60
  const expectedBuf = Buffer.from(expected, "hex");
49
61
  const actualBuf = Buffer.from(actual, "hex");
50
62
  if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) {
package/dist/index.js.map CHANGED
@@ -1 +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":"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","names":[]}
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/backend/users${qs ? `?${qs}` : ''}`);\n },\n\n get: (userId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/backend/users/${userId}`);\n },\n\n getByExternalId: (externalId: string): Promise<AuthonUser> => {\n return this.request('GET', `/v1/backend/users/by-external-id/${externalId}`);\n },\n\n create: (data: {\n email: string;\n password?: string;\n displayName?: string;\n externalId?: string;\n provider?: string;\n avatarUrl?: string;\n phone?: string;\n emailVerified?: boolean;\n publicMetadata?: Record<string, unknown>;\n privateMetadata?: Record<string, unknown>;\n }): Promise<AuthonUser> => {\n return this.request('POST', '/v1/backend/users', data);\n },\n\n update: (\n userId: string,\n data: Partial<{\n displayName: string;\n externalId: string;\n avatarUrl: string;\n phone: string;\n emailVerified: boolean;\n publicMetadata: Record<string, unknown>;\n privateMetadata: Record<string, unknown>;\n }>,\n ): Promise<AuthonUser> => {\n return this.request('PATCH', `/v1/backend/users/${userId}`, data);\n },\n\n delete: (userId: string): Promise<void> => {\n return this.request('DELETE', `/v1/backend/users/${userId}`);\n },\n\n ban: (userId: string, reason?: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/backend/users/${userId}/ban`, { reason });\n },\n\n unban: (userId: string): Promise<AuthonUser> => {\n return this.request('POST', `/v1/backend/users/${userId}/unban`);\n },\n };\n\n webhooks = {\n /**\n * Verify an Authon webhook signature.\n * @param payload - Raw request body (string or Buffer)\n * @param signature - Value of `X-Authon-Signature` header (format: `v1=<hex>`)\n * @param timestamp - Value of `X-Authon-Timestamp` header (ISO 8601)\n * @param secret - Your webhook signing secret\n * @returns Parsed payload object\n */\n verify: (\n payload: string | Buffer,\n signature: string,\n timestamp: string,\n secret: string,\n ): Record<string, unknown> => {\n const body = typeof payload === 'string' ? payload : payload.toString('utf8');\n const signedPayload = `${timestamp}.${body}`;\n const expected = createHmac('sha256', secret).update(signedPayload).digest('hex');\n const actual = signature.replace('v1=', '');\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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/node",
3
- "version": "0.1.0",
3
+ "version": "0.1.15",
4
4
  "description": "Authon Node.js SDK — verify tokens, manage users, validate webhooks",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",