@block-auth.io/node 0.0.0-stage → 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 +32 -2
- package/dist/express.cjs +71 -0
- package/dist/express.cjs.map +1 -0
- package/dist/express.d.cts +8 -0
- package/dist/express.d.ts +8 -0
- package/dist/express.js +63 -0
- package/dist/express.js.map +1 -0
- package/dist/index.cjs +391 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +383 -0
- package/dist/index.js.map +1 -0
- package/dist/routes-DR0gMSlM.d.cts +132 -0
- package/dist/routes-DR0gMSlM.d.ts +132 -0
- package/package.json +53 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/routes.ts"],"names":["createHash","createHmac","timingSafeEqual","error"],"mappings":"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","file":"index.cjs","sourcesContent":["export class BlockAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BlockAuthError';\n }\n}\n","import { createHash, createHmac, timingSafeEqual } from 'node:crypto';\n\nimport { BlockAuthError } from './errors';\n\nexport const DEFAULT_WS_URL = 'wss://wss.block-auth.io';\nexport const DEFAULT_WEBHOOK_MAX_SKEW_SECONDS = 300;\n\nexport interface BlockAuthRole {\n slug?: string;\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface AuthorizeResult {\n url: string;\n expiresAt: string;\n}\n\nexport interface TokenResult {\n address: string;\n did?: string;\n signedNonce?: number;\n signedAt?: string;\n signedExpiredAt: string;\n roles: BlockAuthRole[];\n externalId?: string | null;\n}\n\nexport interface WebhookEvent {\n event: string;\n apiKey: string;\n address: string;\n externalId?: string | null;\n sessionId?: string | null;\n reason?: string;\n iat: number;\n jti: string;\n}\n\nexport interface AuthCodeClientOptions {\n wsUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n}\n\nexport function sha256Hex(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\nexport function buildWsServerApiUrl(path: string, wsUrl = ''): string {\n const url = new URL(wsUrl || DEFAULT_WS_URL);\n const scheme = url.protocol === 'wss:' ? 'https:' : 'http:';\n return `${scheme}//${url.host}/api${path}`;\n}\n\nexport class AuthCodeClient {\n readonly apiKey: string;\n private readonly secretHash: string;\n private readonly wsUrl: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(apiKey: string, apiSecret: string, options: AuthCodeClientOptions = {}) {\n if (!apiKey || !apiSecret) throw new BlockAuthError('apiKey and apiSecret are required');\n this.apiKey = apiKey;\n this.secretHash = sha256Hex(apiSecret);\n this.wsUrl = options.wsUrl ?? '';\n this.timeoutMs = options.timeoutMs ?? 10_000;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n authorize(redirectUri: string, state: string, responseMode?: string): Promise<AuthorizeResult> {\n return this.post('/auth/authorize', { redirectUri, state, responseMode });\n }\n\n exchangeCode(\n code: string,\n redirectUri: string,\n codeVerifier?: string,\n linkExternalId?: string,\n ): Promise<TokenResult> {\n return this.post('/auth/token', { code, redirectUri, codeVerifier, linkExternalId });\n }\n\n signOut(address: string): Promise<{ closed?: number }> {\n return this.post('/auth/signout', { address });\n }\n\n revokeLink(externalId: string): Promise<{ revoked: boolean }> {\n return this.post('/auth/link/revoke', { externalId });\n }\n\n verifyWebhook(\n rawBody: string | Buffer,\n timestamp: string | undefined,\n signature: string | undefined,\n maxSkewSeconds = DEFAULT_WEBHOOK_MAX_SKEW_SECONDS,\n ): WebhookEvent {\n if (!timestamp || !/^\\d+$/.test(timestamp)) throw new BlockAuthError('Invalid webhook timestamp');\n if (Math.abs(Date.now() / 1000 - Number(timestamp)) > maxSkewSeconds) {\n throw new BlockAuthError('Webhook timestamp out of tolerance');\n }\n const body = typeof rawBody === 'string' ? Buffer.from(rawBody, 'utf8') : rawBody;\n const expected = Buffer.from(\n 'v1=' +\n createHmac('sha256', this.secretHash)\n .update(Buffer.concat([Buffer.from(`${timestamp}.`, 'utf8'), body]))\n .digest('hex'),\n 'utf8',\n );\n const provided = Buffer.from(signature ?? '', 'utf8');\n if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {\n throw new BlockAuthError('Invalid webhook signature');\n }\n let payload: unknown;\n try {\n payload = JSON.parse(body.toString('utf8'));\n } catch {\n throw new BlockAuthError('Invalid webhook body');\n }\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n throw new BlockAuthError('Invalid webhook body');\n }\n return payload as WebhookEvent;\n }\n\n private async post<T>(path: string, params: Record<string, unknown>): Promise<T> {\n const body: Record<string, unknown> = { apiKey: this.apiKey, apiSecret: this.secretHash };\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) body[key] = value;\n }\n let payload: { success?: unknown; message?: unknown };\n try {\n const response = await this.fetchImpl(buildWsServerApiUrl(path, this.wsUrl), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n redirect: 'error',\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n payload = (await response.json().catch(() => ({}))) as typeof payload;\n } catch (error) {\n throw new BlockAuthError(`Request to ${path} failed: ${(error as Error).message}`);\n }\n if (payload.success !== true) {\n throw new BlockAuthError(\n typeof payload.message === 'string' && payload.message ? payload.message : `Request to ${path} failed`,\n );\n }\n if (!payload.message || typeof payload.message !== 'object') {\n throw new BlockAuthError(`Unexpected response from ${path}`);\n }\n return payload.message as T;\n }\n}\n","import {\n AuthCodeClient,\n type BlockAuthRole,\n type TokenResult,\n type WebhookEvent,\n} from './client';\nimport { BlockAuthError } from './errors';\n\nexport const DEFAULT_BASE_PATH = '/auth/blockauth';\n\nconst MAX_PARAM_LENGTH = 2048;\nconst ROUTE_NAMES = ['code', 'me', 'logout', 'link', 'webhook'] as const;\nconst LINK_CONFLICTS = [\n 'Account already linked',\n 'External id already linked',\n 'Link conflict',\n];\nconst WEBHOOK_EVENTS = ['signout', 'account.unlinked'];\nconst JSON_POSTS = ['POST /code', 'POST /logout', 'POST /link'];\n\nexport type RouteName = (typeof ROUTE_NAMES)[number];\n\nexport interface SessionUser {\n address: string;\n did: string;\n roles: BlockAuthRole[];\n externalId: string | null;\n loggedInAt: number;\n expiresAt: number;\n data?: Record<string, unknown>;\n}\n\nexport interface SessionStore {\n get(key: string): unknown | Promise<unknown>;\n set(key: string, value: unknown): void | Promise<void>;\n remove(key: string): void | Promise<void>;\n regenerate(): void | Promise<void>;\n}\n\nexport interface AppUser {\n id: string | number;\n linked?: boolean;\n}\n\nexport interface RouteContext {\n session: SessionStore;\n request?: unknown;\n}\n\nexport interface RouteResponse {\n status: number;\n body: Record<string, unknown>;\n}\n\nexport type MaybePromise<T> = T | Promise<T>;\n\nexport interface BlockAuthRoutesOptions {\n apiKey: string;\n apiSecret: string;\n appUrl?: string;\n allowedOrigins?: string[];\n sessionKey?: string;\n maxSessionSeconds?: number;\n basePath?: string;\n routes?: Partial<Record<RouteName, boolean>>;\n onLogin?: (\n user: SessionUser,\n context: RouteContext,\n ) => MaybePromise<Record<string, unknown> | false | void>;\n onLogout?: (user: SessionUser, context: RouteContext) => MaybePromise<void>;\n appUser?: (context: RouteContext) => MaybePromise<AppUser | null | undefined>;\n onLinked?: (\n appUserId: string | number,\n user: TokenResult,\n context: RouteContext,\n ) => MaybePromise<void>;\n onSignout?: (event: WebhookEvent) => MaybePromise<void>;\n onUnlinked?: (event: WebhookEvent) => MaybePromise<void>;\n isReplay?: (jti: string, event: WebhookEvent) => MaybePromise<boolean>;\n isRevoked?: (user: SessionUser) => MaybePromise<boolean>;\n webhookMaxSkewSeconds?: number;\n wsUrl?: string;\n timeoutMs?: number;\n client?: AuthCodeClient;\n logger?: (message: string, error?: unknown) => void;\n}\n\nexport function originOf(url: unknown): string | null {\n if (typeof url !== 'string') return null;\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (\n !['http:', 'https:'].includes(parsed.protocol) ||\n !parsed.hostname ||\n parsed.username ||\n parsed.password\n ) {\n return null;\n }\n return parsed.origin;\n}\n\nfunction error(status: number, code: string): RouteResponse {\n return { status, body: { error: code } };\n}\n\nfunction isJson(contentType: string | undefined): boolean {\n return (\n (contentType ?? '').split(';', 1)[0].trim().toLowerCase() ===\n 'application/json'\n );\n}\n\nfunction parseJson(rawBody: string): unknown {\n try {\n return JSON.parse(rawBody);\n } catch {\n return null;\n }\n}\n\nfunction publicUser(user: SessionUser): Record<string, unknown> {\n return { address: user.address, did: user.did, roles: user.roles };\n}\n\nexport class BlockAuthRoutes {\n readonly client: AuthCodeClient;\n readonly basePath: string;\n readonly allowedOrigins: ReadonlySet<string>;\n private readonly options: BlockAuthRoutesOptions;\n private readonly sessionKey: string;\n private readonly enabled: Record<RouteName, boolean>;\n\n constructor(options: BlockAuthRoutesOptions) {\n const origins =\n options.allowedOrigins ?? (options.appUrl ? [options.appUrl] : []);\n const resolved = new Set<string>();\n for (const url of origins) {\n const origin = originOf(url);\n if (!origin)\n throw new BlockAuthError(\n `Invalid origin in appUrl or allowedOrigins: ${String(url)}`,\n );\n resolved.add(origin);\n }\n if (resolved.size === 0)\n throw new BlockAuthError(\n 'Set appUrl or allowedOrigins: the origins your frontend runs on',\n );\n const unknown = Object.keys(options.routes ?? {}).filter(\n (name) => !ROUTE_NAMES.includes(name as RouteName),\n );\n if (unknown.length > 0)\n throw new BlockAuthError(`Unknown routes: ${unknown.join(', ')}`);\n\n this.options = options;\n this.client =\n options.client ??\n new AuthCodeClient(options.apiKey, options.apiSecret, {\n wsUrl: options.wsUrl,\n timeoutMs: options.timeoutMs,\n });\n this.allowedOrigins = resolved;\n this.basePath =\n '/' + (options.basePath ?? DEFAULT_BASE_PATH).replace(/^\\/+|\\/+$/g, '');\n this.sessionKey = options.sessionKey ?? 'blockauth_user';\n this.enabled = {\n code: true,\n me: true,\n logout: true,\n link: true,\n webhook: true,\n ...options.routes,\n };\n }\n\n async currentUser(context: RouteContext): Promise<SessionUser | null> {\n const user = (await context.session.get(this.sessionKey)) as\n | SessionUser\n | null\n | undefined;\n const valid =\n !!user &&\n typeof user === 'object' &&\n typeof user.address === 'string' &&\n typeof user.expiresAt === 'number' &&\n Date.now() / 1000 < user.expiresAt &&\n !(this.options.isRevoked && (await this.options.isRevoked(user)));\n if (!valid) {\n if (user != null) await context.session.remove(this.sessionKey);\n return null;\n }\n return user;\n }\n\n async code(body: unknown, context: RouteContext): Promise<RouteResponse> {\n const params = this.codeParams(body);\n if (!params) return error(400, 'invalid_request');\n let result: TokenResult;\n try {\n result = await this.client.exchangeCode(\n params.code,\n params.redirectUri,\n params.codeVerifier,\n );\n } catch (err) {\n this.log('Block-Auth could not redeem the login code', err);\n return error(502, 'blockauth_error');\n }\n const expiresAt = this.expiresAt(result.signedExpiredAt);\n if (\n typeof result.address !== 'string' ||\n !result.address ||\n expiresAt === null\n ) {\n this.log('Block-Auth returned a user without address or signedExpiredAt');\n return error(502, 'blockauth_error');\n }\n const user: SessionUser = {\n address: result.address,\n did:\n typeof result.did === 'string' && result.did\n ? result.did\n : result.address,\n roles: Array.isArray(result.roles) ? result.roles : [],\n externalId: result.externalId ?? null,\n loggedInAt: Math.floor(Date.now() / 1000),\n expiresAt,\n };\n if (this.options.onLogin) {\n const extra = await this.options.onLogin(user, context);\n if (extra === false) {\n await this.client\n .signOut(user.address)\n .catch((err) =>\n this.log('Block-Auth sign out of a rejected login failed', err),\n );\n return error(403, 'forbidden');\n }\n if (extra && typeof extra === 'object') user.data = extra;\n }\n await context.session.regenerate();\n await context.session.set(this.sessionKey, user);\n return { status: 200, body: publicUser(user) };\n }\n\n async me(context: RouteContext): Promise<RouteResponse> {\n const user = await this.currentUser(context);\n return user\n ? { status: 200, body: publicUser(user) }\n : error(401, 'unauthenticated');\n }\n\n async logout(context: RouteContext): Promise<RouteResponse> {\n const user = await this.currentUser(context);\n if (user) {\n await this.client\n .signOut(user.address)\n .catch((err) =>\n this.log(\n 'Block-Auth sign out failed; the local session is closed anyway',\n err,\n ),\n );\n }\n await context.session.remove(this.sessionKey);\n await context.session.regenerate();\n if (user && this.options.onLogout)\n await this.options.onLogout(user, context);\n return { status: 200, body: { ok: true } };\n }\n\n async linkStatus(context: RouteContext): Promise<RouteResponse> {\n if (!this.options.appUser) return error(404, 'not_found');\n const appUser = await this.resolveAppUser(context);\n return appUser\n ? { status: 200, body: { linked: appUser.linked === true } }\n : error(401, 'unauthenticated');\n }\n\n async link(body: unknown, context: RouteContext): Promise<RouteResponse> {\n if (!this.options.appUser) return error(404, 'not_found');\n const appUser = await this.resolveAppUser(context);\n if (!appUser) return error(401, 'unauthenticated');\n const params = this.codeParams(body);\n if (!params) return error(400, 'invalid_request');\n let result: TokenResult;\n try {\n result = await this.client.exchangeCode(\n params.code,\n params.redirectUri,\n params.codeVerifier,\n String(appUser.id),\n );\n } catch (err) {\n if (err instanceof BlockAuthError && LINK_CONFLICTS.includes(err.message))\n return error(409, 'already_linked');\n this.log('Block-Auth could not link the account', err);\n return error(502, 'blockauth_error');\n }\n if (this.options.onLinked)\n await this.options.onLinked(appUser.id, result, context);\n return { status: 200, body: { linked: true } };\n }\n\n async webhook(\n rawBody: string | Buffer,\n timestamp?: string,\n signature?: string,\n ): Promise<RouteResponse> {\n let event: WebhookEvent;\n try {\n event = this.client.verifyWebhook(\n rawBody,\n timestamp,\n signature,\n this.options.webhookMaxSkewSeconds,\n );\n } catch (err) {\n this.log('Rejected a webhook that failed verification', err);\n return error(401, 'invalid_signature');\n }\n if (event.apiKey !== this.options.apiKey)\n return error(401, 'invalid_signature');\n if (\n !WEBHOOK_EVENTS.includes(event.event) ||\n typeof event.address !== 'string'\n ) {\n return { status: 200, body: { ok: true, ignored: true } };\n }\n if (\n this.options.isReplay &&\n (typeof event.jti !== 'string' ||\n (await this.options.isReplay(event.jti, event)))\n ) {\n return error(409, 'replayed');\n }\n const handler =\n event.event === 'signout'\n ? this.options.onSignout\n : this.options.onUnlinked;\n if (handler) await handler(event);\n return { status: 200, body: { ok: true } };\n }\n\n async dispatch(\n method: string,\n path: string,\n rawBody: string | Buffer,\n header: (name: string) => string | undefined,\n context: RouteContext,\n ): Promise<RouteResponse | null> {\n if (!path.startsWith(this.basePath + '/')) return null;\n const route = `${method.toUpperCase()} ${path.slice(this.basePath.length)}`;\n const name = (\n {\n 'POST /code': 'code',\n 'GET /me': 'me',\n 'POST /logout': 'logout',\n 'GET /link': 'link',\n 'POST /link': 'link',\n 'POST /webhook': 'webhook',\n } as Record<string, RouteName>\n )[route];\n if (!name || !this.enabled[name]) return null;\n if (JSON_POSTS.includes(route) && !isJson(header('content-type')))\n return error(415, 'unsupported_media_type');\n const text =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n switch (route) {\n case 'POST /code':\n return this.code(parseJson(text), context);\n case 'GET /me':\n return this.me(context);\n case 'POST /logout':\n return this.logout(context);\n case 'GET /link':\n return this.linkStatus(context);\n case 'POST /link':\n return this.link(parseJson(text), context);\n default:\n return this.webhook(\n rawBody,\n header('x-blockauth-timestamp'),\n header('x-blockauth-signature'),\n );\n }\n }\n\n private codeParams(\n body: unknown,\n ): { code: string; codeVerifier: string; redirectUri: string } | null {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null;\n const { code, codeVerifier, redirectUri } = body as Record<string, unknown>;\n for (const value of [code, codeVerifier, redirectUri]) {\n if (\n typeof value !== 'string' ||\n !value ||\n value.length > MAX_PARAM_LENGTH\n )\n return null;\n }\n const origin = originOf(redirectUri);\n if (!origin || !this.allowedOrigins.has(origin)) return null;\n return {\n code: code as string,\n codeVerifier: codeVerifier as string,\n redirectUri: redirectUri as string,\n };\n }\n\n private expiresAt(signedExpiredAt: unknown): number | null {\n if (typeof signedExpiredAt !== 'string') return null;\n const match = /^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})$/.exec(\n signedExpiredAt,\n );\n if (!match) return null;\n const [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n let expiresAt = Date.UTC(year, month - 1, day, hour, minute, second) / 1000;\n const now = Math.floor(Date.now() / 1000);\n if (this.options.maxSessionSeconds !== undefined) {\n expiresAt = Math.min(\n expiresAt,\n now + Math.max(1, Math.floor(this.options.maxSessionSeconds)),\n );\n }\n return Number.isFinite(expiresAt) && expiresAt > now ? expiresAt : null;\n }\n\n private async resolveAppUser(context: RouteContext): Promise<AppUser | null> {\n const appUser = await this.options.appUser?.(context);\n if (!appUser || typeof appUser !== 'object') return null;\n const { id } = appUser;\n if (\n !(\n (typeof id === 'string' && id) ||\n (typeof id === 'number' && Number.isFinite(id))\n )\n )\n return null;\n return appUser;\n }\n\n private log(message: string, err?: unknown): void {\n this.options.logger?.(message, err);\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { A as AppUser, a as AuthCodeClient, b as AuthCodeClientOptions, c as AuthorizeResult, d as BlockAuthRole, B as BlockAuthRoutes, e as BlockAuthRoutesOptions, D as DEFAULT_BASE_PATH, f as DEFAULT_WS_URL, g as RouteContext, h as RouteName, R as RouteResponse, S as SessionStore, i as SessionUser, T as TokenResult, W as WebhookEvent, j as buildWsServerApiUrl, s as sha256Hex } from './routes-DR0gMSlM.cjs';
|
|
2
|
+
|
|
3
|
+
declare class BlockAuthError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export { BlockAuthError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { A as AppUser, a as AuthCodeClient, b as AuthCodeClientOptions, c as AuthorizeResult, d as BlockAuthRole, B as BlockAuthRoutes, e as BlockAuthRoutesOptions, D as DEFAULT_BASE_PATH, f as DEFAULT_WS_URL, g as RouteContext, h as RouteName, R as RouteResponse, S as SessionStore, i as SessionUser, T as TokenResult, W as WebhookEvent, j as buildWsServerApiUrl, s as sha256Hex } from './routes-DR0gMSlM.js';
|
|
2
|
+
|
|
3
|
+
declare class BlockAuthError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export { BlockAuthError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var BlockAuthError = class extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "BlockAuthError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/client.ts
|
|
14
|
+
var DEFAULT_WS_URL = "wss://wss.block-auth.io";
|
|
15
|
+
var DEFAULT_WEBHOOK_MAX_SKEW_SECONDS = 300;
|
|
16
|
+
function sha256Hex(value) {
|
|
17
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
18
|
+
}
|
|
19
|
+
function buildWsServerApiUrl(path, wsUrl = "") {
|
|
20
|
+
const url = new URL(wsUrl || DEFAULT_WS_URL);
|
|
21
|
+
const scheme = url.protocol === "wss:" ? "https:" : "http:";
|
|
22
|
+
return `${scheme}//${url.host}/api${path}`;
|
|
23
|
+
}
|
|
24
|
+
var AuthCodeClient = class {
|
|
25
|
+
constructor(apiKey, apiSecret, options = {}) {
|
|
26
|
+
if (!apiKey || !apiSecret) throw new BlockAuthError("apiKey and apiSecret are required");
|
|
27
|
+
this.apiKey = apiKey;
|
|
28
|
+
this.secretHash = sha256Hex(apiSecret);
|
|
29
|
+
this.wsUrl = options.wsUrl ?? "";
|
|
30
|
+
this.timeoutMs = options.timeoutMs ?? 1e4;
|
|
31
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
32
|
+
}
|
|
33
|
+
authorize(redirectUri, state, responseMode) {
|
|
34
|
+
return this.post("/auth/authorize", { redirectUri, state, responseMode });
|
|
35
|
+
}
|
|
36
|
+
exchangeCode(code, redirectUri, codeVerifier, linkExternalId) {
|
|
37
|
+
return this.post("/auth/token", { code, redirectUri, codeVerifier, linkExternalId });
|
|
38
|
+
}
|
|
39
|
+
signOut(address) {
|
|
40
|
+
return this.post("/auth/signout", { address });
|
|
41
|
+
}
|
|
42
|
+
revokeLink(externalId) {
|
|
43
|
+
return this.post("/auth/link/revoke", { externalId });
|
|
44
|
+
}
|
|
45
|
+
verifyWebhook(rawBody, timestamp, signature, maxSkewSeconds = DEFAULT_WEBHOOK_MAX_SKEW_SECONDS) {
|
|
46
|
+
if (!timestamp || !/^\d+$/.test(timestamp)) throw new BlockAuthError("Invalid webhook timestamp");
|
|
47
|
+
if (Math.abs(Date.now() / 1e3 - Number(timestamp)) > maxSkewSeconds) {
|
|
48
|
+
throw new BlockAuthError("Webhook timestamp out of tolerance");
|
|
49
|
+
}
|
|
50
|
+
const body = typeof rawBody === "string" ? Buffer.from(rawBody, "utf8") : rawBody;
|
|
51
|
+
const expected = Buffer.from(
|
|
52
|
+
"v1=" + createHmac("sha256", this.secretHash).update(Buffer.concat([Buffer.from(`${timestamp}.`, "utf8"), body])).digest("hex"),
|
|
53
|
+
"utf8"
|
|
54
|
+
);
|
|
55
|
+
const provided = Buffer.from(signature ?? "", "utf8");
|
|
56
|
+
if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {
|
|
57
|
+
throw new BlockAuthError("Invalid webhook signature");
|
|
58
|
+
}
|
|
59
|
+
let payload;
|
|
60
|
+
try {
|
|
61
|
+
payload = JSON.parse(body.toString("utf8"));
|
|
62
|
+
} catch {
|
|
63
|
+
throw new BlockAuthError("Invalid webhook body");
|
|
64
|
+
}
|
|
65
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
66
|
+
throw new BlockAuthError("Invalid webhook body");
|
|
67
|
+
}
|
|
68
|
+
return payload;
|
|
69
|
+
}
|
|
70
|
+
async post(path, params) {
|
|
71
|
+
const body = { apiKey: this.apiKey, apiSecret: this.secretHash };
|
|
72
|
+
for (const [key, value] of Object.entries(params)) {
|
|
73
|
+
if (value !== void 0 && value !== null) body[key] = value;
|
|
74
|
+
}
|
|
75
|
+
let payload;
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.fetchImpl(buildWsServerApiUrl(path, this.wsUrl), {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
body: JSON.stringify(body),
|
|
81
|
+
redirect: "error",
|
|
82
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
83
|
+
});
|
|
84
|
+
payload = await response.json().catch(() => ({}));
|
|
85
|
+
} catch (error2) {
|
|
86
|
+
throw new BlockAuthError(`Request to ${path} failed: ${error2.message}`);
|
|
87
|
+
}
|
|
88
|
+
if (payload.success !== true) {
|
|
89
|
+
throw new BlockAuthError(
|
|
90
|
+
typeof payload.message === "string" && payload.message ? payload.message : `Request to ${path} failed`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (!payload.message || typeof payload.message !== "object") {
|
|
94
|
+
throw new BlockAuthError(`Unexpected response from ${path}`);
|
|
95
|
+
}
|
|
96
|
+
return payload.message;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/routes.ts
|
|
101
|
+
var DEFAULT_BASE_PATH = "/auth/blockauth";
|
|
102
|
+
var MAX_PARAM_LENGTH = 2048;
|
|
103
|
+
var ROUTE_NAMES = ["code", "me", "logout", "link", "webhook"];
|
|
104
|
+
var LINK_CONFLICTS = [
|
|
105
|
+
"Account already linked",
|
|
106
|
+
"External id already linked",
|
|
107
|
+
"Link conflict"
|
|
108
|
+
];
|
|
109
|
+
var WEBHOOK_EVENTS = ["signout", "account.unlinked"];
|
|
110
|
+
var JSON_POSTS = ["POST /code", "POST /logout", "POST /link"];
|
|
111
|
+
function originOf(url) {
|
|
112
|
+
if (typeof url !== "string") return null;
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = new URL(url);
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (!["http:", "https:"].includes(parsed.protocol) || !parsed.hostname || parsed.username || parsed.password) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return parsed.origin;
|
|
123
|
+
}
|
|
124
|
+
function error(status, code) {
|
|
125
|
+
return { status, body: { error: code } };
|
|
126
|
+
}
|
|
127
|
+
function isJson(contentType) {
|
|
128
|
+
return (contentType ?? "").split(";", 1)[0].trim().toLowerCase() === "application/json";
|
|
129
|
+
}
|
|
130
|
+
function parseJson(rawBody) {
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(rawBody);
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function publicUser(user) {
|
|
138
|
+
return { address: user.address, did: user.did, roles: user.roles };
|
|
139
|
+
}
|
|
140
|
+
var BlockAuthRoutes = class {
|
|
141
|
+
constructor(options) {
|
|
142
|
+
const origins = options.allowedOrigins ?? (options.appUrl ? [options.appUrl] : []);
|
|
143
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
144
|
+
for (const url of origins) {
|
|
145
|
+
const origin = originOf(url);
|
|
146
|
+
if (!origin)
|
|
147
|
+
throw new BlockAuthError(
|
|
148
|
+
`Invalid origin in appUrl or allowedOrigins: ${String(url)}`
|
|
149
|
+
);
|
|
150
|
+
resolved.add(origin);
|
|
151
|
+
}
|
|
152
|
+
if (resolved.size === 0)
|
|
153
|
+
throw new BlockAuthError(
|
|
154
|
+
"Set appUrl or allowedOrigins: the origins your frontend runs on"
|
|
155
|
+
);
|
|
156
|
+
const unknown = Object.keys(options.routes ?? {}).filter(
|
|
157
|
+
(name) => !ROUTE_NAMES.includes(name)
|
|
158
|
+
);
|
|
159
|
+
if (unknown.length > 0)
|
|
160
|
+
throw new BlockAuthError(`Unknown routes: ${unknown.join(", ")}`);
|
|
161
|
+
this.options = options;
|
|
162
|
+
this.client = options.client ?? new AuthCodeClient(options.apiKey, options.apiSecret, {
|
|
163
|
+
wsUrl: options.wsUrl,
|
|
164
|
+
timeoutMs: options.timeoutMs
|
|
165
|
+
});
|
|
166
|
+
this.allowedOrigins = resolved;
|
|
167
|
+
this.basePath = "/" + (options.basePath ?? DEFAULT_BASE_PATH).replace(/^\/+|\/+$/g, "");
|
|
168
|
+
this.sessionKey = options.sessionKey ?? "blockauth_user";
|
|
169
|
+
this.enabled = {
|
|
170
|
+
code: true,
|
|
171
|
+
me: true,
|
|
172
|
+
logout: true,
|
|
173
|
+
link: true,
|
|
174
|
+
webhook: true,
|
|
175
|
+
...options.routes
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async currentUser(context) {
|
|
179
|
+
const user = await context.session.get(this.sessionKey);
|
|
180
|
+
const valid = !!user && typeof user === "object" && typeof user.address === "string" && typeof user.expiresAt === "number" && Date.now() / 1e3 < user.expiresAt && !(this.options.isRevoked && await this.options.isRevoked(user));
|
|
181
|
+
if (!valid) {
|
|
182
|
+
if (user != null) await context.session.remove(this.sessionKey);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return user;
|
|
186
|
+
}
|
|
187
|
+
async code(body, context) {
|
|
188
|
+
const params = this.codeParams(body);
|
|
189
|
+
if (!params) return error(400, "invalid_request");
|
|
190
|
+
let result;
|
|
191
|
+
try {
|
|
192
|
+
result = await this.client.exchangeCode(
|
|
193
|
+
params.code,
|
|
194
|
+
params.redirectUri,
|
|
195
|
+
params.codeVerifier
|
|
196
|
+
);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
this.log("Block-Auth could not redeem the login code", err);
|
|
199
|
+
return error(502, "blockauth_error");
|
|
200
|
+
}
|
|
201
|
+
const expiresAt = this.expiresAt(result.signedExpiredAt);
|
|
202
|
+
if (typeof result.address !== "string" || !result.address || expiresAt === null) {
|
|
203
|
+
this.log("Block-Auth returned a user without address or signedExpiredAt");
|
|
204
|
+
return error(502, "blockauth_error");
|
|
205
|
+
}
|
|
206
|
+
const user = {
|
|
207
|
+
address: result.address,
|
|
208
|
+
did: typeof result.did === "string" && result.did ? result.did : result.address,
|
|
209
|
+
roles: Array.isArray(result.roles) ? result.roles : [],
|
|
210
|
+
externalId: result.externalId ?? null,
|
|
211
|
+
loggedInAt: Math.floor(Date.now() / 1e3),
|
|
212
|
+
expiresAt
|
|
213
|
+
};
|
|
214
|
+
if (this.options.onLogin) {
|
|
215
|
+
const extra = await this.options.onLogin(user, context);
|
|
216
|
+
if (extra === false) {
|
|
217
|
+
await this.client.signOut(user.address).catch(
|
|
218
|
+
(err) => this.log("Block-Auth sign out of a rejected login failed", err)
|
|
219
|
+
);
|
|
220
|
+
return error(403, "forbidden");
|
|
221
|
+
}
|
|
222
|
+
if (extra && typeof extra === "object") user.data = extra;
|
|
223
|
+
}
|
|
224
|
+
await context.session.regenerate();
|
|
225
|
+
await context.session.set(this.sessionKey, user);
|
|
226
|
+
return { status: 200, body: publicUser(user) };
|
|
227
|
+
}
|
|
228
|
+
async me(context) {
|
|
229
|
+
const user = await this.currentUser(context);
|
|
230
|
+
return user ? { status: 200, body: publicUser(user) } : error(401, "unauthenticated");
|
|
231
|
+
}
|
|
232
|
+
async logout(context) {
|
|
233
|
+
const user = await this.currentUser(context);
|
|
234
|
+
if (user) {
|
|
235
|
+
await this.client.signOut(user.address).catch(
|
|
236
|
+
(err) => this.log(
|
|
237
|
+
"Block-Auth sign out failed; the local session is closed anyway",
|
|
238
|
+
err
|
|
239
|
+
)
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
await context.session.remove(this.sessionKey);
|
|
243
|
+
await context.session.regenerate();
|
|
244
|
+
if (user && this.options.onLogout)
|
|
245
|
+
await this.options.onLogout(user, context);
|
|
246
|
+
return { status: 200, body: { ok: true } };
|
|
247
|
+
}
|
|
248
|
+
async linkStatus(context) {
|
|
249
|
+
if (!this.options.appUser) return error(404, "not_found");
|
|
250
|
+
const appUser = await this.resolveAppUser(context);
|
|
251
|
+
return appUser ? { status: 200, body: { linked: appUser.linked === true } } : error(401, "unauthenticated");
|
|
252
|
+
}
|
|
253
|
+
async link(body, context) {
|
|
254
|
+
if (!this.options.appUser) return error(404, "not_found");
|
|
255
|
+
const appUser = await this.resolveAppUser(context);
|
|
256
|
+
if (!appUser) return error(401, "unauthenticated");
|
|
257
|
+
const params = this.codeParams(body);
|
|
258
|
+
if (!params) return error(400, "invalid_request");
|
|
259
|
+
let result;
|
|
260
|
+
try {
|
|
261
|
+
result = await this.client.exchangeCode(
|
|
262
|
+
params.code,
|
|
263
|
+
params.redirectUri,
|
|
264
|
+
params.codeVerifier,
|
|
265
|
+
String(appUser.id)
|
|
266
|
+
);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
if (err instanceof BlockAuthError && LINK_CONFLICTS.includes(err.message))
|
|
269
|
+
return error(409, "already_linked");
|
|
270
|
+
this.log("Block-Auth could not link the account", err);
|
|
271
|
+
return error(502, "blockauth_error");
|
|
272
|
+
}
|
|
273
|
+
if (this.options.onLinked)
|
|
274
|
+
await this.options.onLinked(appUser.id, result, context);
|
|
275
|
+
return { status: 200, body: { linked: true } };
|
|
276
|
+
}
|
|
277
|
+
async webhook(rawBody, timestamp, signature) {
|
|
278
|
+
let event;
|
|
279
|
+
try {
|
|
280
|
+
event = this.client.verifyWebhook(
|
|
281
|
+
rawBody,
|
|
282
|
+
timestamp,
|
|
283
|
+
signature,
|
|
284
|
+
this.options.webhookMaxSkewSeconds
|
|
285
|
+
);
|
|
286
|
+
} catch (err) {
|
|
287
|
+
this.log("Rejected a webhook that failed verification", err);
|
|
288
|
+
return error(401, "invalid_signature");
|
|
289
|
+
}
|
|
290
|
+
if (event.apiKey !== this.options.apiKey)
|
|
291
|
+
return error(401, "invalid_signature");
|
|
292
|
+
if (!WEBHOOK_EVENTS.includes(event.event) || typeof event.address !== "string") {
|
|
293
|
+
return { status: 200, body: { ok: true, ignored: true } };
|
|
294
|
+
}
|
|
295
|
+
if (this.options.isReplay && (typeof event.jti !== "string" || await this.options.isReplay(event.jti, event))) {
|
|
296
|
+
return error(409, "replayed");
|
|
297
|
+
}
|
|
298
|
+
const handler = event.event === "signout" ? this.options.onSignout : this.options.onUnlinked;
|
|
299
|
+
if (handler) await handler(event);
|
|
300
|
+
return { status: 200, body: { ok: true } };
|
|
301
|
+
}
|
|
302
|
+
async dispatch(method, path, rawBody, header, context) {
|
|
303
|
+
if (!path.startsWith(this.basePath + "/")) return null;
|
|
304
|
+
const route = `${method.toUpperCase()} ${path.slice(this.basePath.length)}`;
|
|
305
|
+
const name = {
|
|
306
|
+
"POST /code": "code",
|
|
307
|
+
"GET /me": "me",
|
|
308
|
+
"POST /logout": "logout",
|
|
309
|
+
"GET /link": "link",
|
|
310
|
+
"POST /link": "link",
|
|
311
|
+
"POST /webhook": "webhook"
|
|
312
|
+
}[route];
|
|
313
|
+
if (!name || !this.enabled[name]) return null;
|
|
314
|
+
if (JSON_POSTS.includes(route) && !isJson(header("content-type")))
|
|
315
|
+
return error(415, "unsupported_media_type");
|
|
316
|
+
const text = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
317
|
+
switch (route) {
|
|
318
|
+
case "POST /code":
|
|
319
|
+
return this.code(parseJson(text), context);
|
|
320
|
+
case "GET /me":
|
|
321
|
+
return this.me(context);
|
|
322
|
+
case "POST /logout":
|
|
323
|
+
return this.logout(context);
|
|
324
|
+
case "GET /link":
|
|
325
|
+
return this.linkStatus(context);
|
|
326
|
+
case "POST /link":
|
|
327
|
+
return this.link(parseJson(text), context);
|
|
328
|
+
default:
|
|
329
|
+
return this.webhook(
|
|
330
|
+
rawBody,
|
|
331
|
+
header("x-blockauth-timestamp"),
|
|
332
|
+
header("x-blockauth-signature")
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
codeParams(body) {
|
|
337
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
338
|
+
const { code, codeVerifier, redirectUri } = body;
|
|
339
|
+
for (const value of [code, codeVerifier, redirectUri]) {
|
|
340
|
+
if (typeof value !== "string" || !value || value.length > MAX_PARAM_LENGTH)
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
const origin = originOf(redirectUri);
|
|
344
|
+
if (!origin || !this.allowedOrigins.has(origin)) return null;
|
|
345
|
+
return {
|
|
346
|
+
code,
|
|
347
|
+
codeVerifier,
|
|
348
|
+
redirectUri
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
expiresAt(signedExpiredAt) {
|
|
352
|
+
if (typeof signedExpiredAt !== "string") return null;
|
|
353
|
+
const match = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/.exec(
|
|
354
|
+
signedExpiredAt
|
|
355
|
+
);
|
|
356
|
+
if (!match) return null;
|
|
357
|
+
const [year, month, day, hour, minute, second] = match.slice(1).map(Number);
|
|
358
|
+
let expiresAt = Date.UTC(year, month - 1, day, hour, minute, second) / 1e3;
|
|
359
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
360
|
+
if (this.options.maxSessionSeconds !== void 0) {
|
|
361
|
+
expiresAt = Math.min(
|
|
362
|
+
expiresAt,
|
|
363
|
+
now + Math.max(1, Math.floor(this.options.maxSessionSeconds))
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return Number.isFinite(expiresAt) && expiresAt > now ? expiresAt : null;
|
|
367
|
+
}
|
|
368
|
+
async resolveAppUser(context) {
|
|
369
|
+
const appUser = await this.options.appUser?.(context);
|
|
370
|
+
if (!appUser || typeof appUser !== "object") return null;
|
|
371
|
+
const { id } = appUser;
|
|
372
|
+
if (!(typeof id === "string" && id || typeof id === "number" && Number.isFinite(id)))
|
|
373
|
+
return null;
|
|
374
|
+
return appUser;
|
|
375
|
+
}
|
|
376
|
+
log(message, err) {
|
|
377
|
+
this.options.logger?.(message, err);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export { AuthCodeClient, BlockAuthError, BlockAuthRoutes, DEFAULT_BASE_PATH, DEFAULT_WS_URL, buildWsServerApiUrl, sha256Hex };
|
|
382
|
+
//# sourceMappingURL=index.js.map
|
|
383
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/routes.ts"],"names":["error"],"mappings":"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","file":"index.js","sourcesContent":["export class BlockAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'BlockAuthError';\n }\n}\n","import { createHash, createHmac, timingSafeEqual } from 'node:crypto';\n\nimport { BlockAuthError } from './errors';\n\nexport const DEFAULT_WS_URL = 'wss://wss.block-auth.io';\nexport const DEFAULT_WEBHOOK_MAX_SKEW_SECONDS = 300;\n\nexport interface BlockAuthRole {\n slug?: string;\n name?: string;\n [key: string]: unknown;\n}\n\nexport interface AuthorizeResult {\n url: string;\n expiresAt: string;\n}\n\nexport interface TokenResult {\n address: string;\n did?: string;\n signedNonce?: number;\n signedAt?: string;\n signedExpiredAt: string;\n roles: BlockAuthRole[];\n externalId?: string | null;\n}\n\nexport interface WebhookEvent {\n event: string;\n apiKey: string;\n address: string;\n externalId?: string | null;\n sessionId?: string | null;\n reason?: string;\n iat: number;\n jti: string;\n}\n\nexport interface AuthCodeClientOptions {\n wsUrl?: string;\n timeoutMs?: number;\n fetch?: typeof fetch;\n}\n\nexport function sha256Hex(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\nexport function buildWsServerApiUrl(path: string, wsUrl = ''): string {\n const url = new URL(wsUrl || DEFAULT_WS_URL);\n const scheme = url.protocol === 'wss:' ? 'https:' : 'http:';\n return `${scheme}//${url.host}/api${path}`;\n}\n\nexport class AuthCodeClient {\n readonly apiKey: string;\n private readonly secretHash: string;\n private readonly wsUrl: string;\n private readonly timeoutMs: number;\n private readonly fetchImpl: typeof fetch;\n\n constructor(apiKey: string, apiSecret: string, options: AuthCodeClientOptions = {}) {\n if (!apiKey || !apiSecret) throw new BlockAuthError('apiKey and apiSecret are required');\n this.apiKey = apiKey;\n this.secretHash = sha256Hex(apiSecret);\n this.wsUrl = options.wsUrl ?? '';\n this.timeoutMs = options.timeoutMs ?? 10_000;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n authorize(redirectUri: string, state: string, responseMode?: string): Promise<AuthorizeResult> {\n return this.post('/auth/authorize', { redirectUri, state, responseMode });\n }\n\n exchangeCode(\n code: string,\n redirectUri: string,\n codeVerifier?: string,\n linkExternalId?: string,\n ): Promise<TokenResult> {\n return this.post('/auth/token', { code, redirectUri, codeVerifier, linkExternalId });\n }\n\n signOut(address: string): Promise<{ closed?: number }> {\n return this.post('/auth/signout', { address });\n }\n\n revokeLink(externalId: string): Promise<{ revoked: boolean }> {\n return this.post('/auth/link/revoke', { externalId });\n }\n\n verifyWebhook(\n rawBody: string | Buffer,\n timestamp: string | undefined,\n signature: string | undefined,\n maxSkewSeconds = DEFAULT_WEBHOOK_MAX_SKEW_SECONDS,\n ): WebhookEvent {\n if (!timestamp || !/^\\d+$/.test(timestamp)) throw new BlockAuthError('Invalid webhook timestamp');\n if (Math.abs(Date.now() / 1000 - Number(timestamp)) > maxSkewSeconds) {\n throw new BlockAuthError('Webhook timestamp out of tolerance');\n }\n const body = typeof rawBody === 'string' ? Buffer.from(rawBody, 'utf8') : rawBody;\n const expected = Buffer.from(\n 'v1=' +\n createHmac('sha256', this.secretHash)\n .update(Buffer.concat([Buffer.from(`${timestamp}.`, 'utf8'), body]))\n .digest('hex'),\n 'utf8',\n );\n const provided = Buffer.from(signature ?? '', 'utf8');\n if (provided.length !== expected.length || !timingSafeEqual(provided, expected)) {\n throw new BlockAuthError('Invalid webhook signature');\n }\n let payload: unknown;\n try {\n payload = JSON.parse(body.toString('utf8'));\n } catch {\n throw new BlockAuthError('Invalid webhook body');\n }\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n throw new BlockAuthError('Invalid webhook body');\n }\n return payload as WebhookEvent;\n }\n\n private async post<T>(path: string, params: Record<string, unknown>): Promise<T> {\n const body: Record<string, unknown> = { apiKey: this.apiKey, apiSecret: this.secretHash };\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) body[key] = value;\n }\n let payload: { success?: unknown; message?: unknown };\n try {\n const response = await this.fetchImpl(buildWsServerApiUrl(path, this.wsUrl), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n redirect: 'error',\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n payload = (await response.json().catch(() => ({}))) as typeof payload;\n } catch (error) {\n throw new BlockAuthError(`Request to ${path} failed: ${(error as Error).message}`);\n }\n if (payload.success !== true) {\n throw new BlockAuthError(\n typeof payload.message === 'string' && payload.message ? payload.message : `Request to ${path} failed`,\n );\n }\n if (!payload.message || typeof payload.message !== 'object') {\n throw new BlockAuthError(`Unexpected response from ${path}`);\n }\n return payload.message as T;\n }\n}\n","import {\n AuthCodeClient,\n type BlockAuthRole,\n type TokenResult,\n type WebhookEvent,\n} from './client';\nimport { BlockAuthError } from './errors';\n\nexport const DEFAULT_BASE_PATH = '/auth/blockauth';\n\nconst MAX_PARAM_LENGTH = 2048;\nconst ROUTE_NAMES = ['code', 'me', 'logout', 'link', 'webhook'] as const;\nconst LINK_CONFLICTS = [\n 'Account already linked',\n 'External id already linked',\n 'Link conflict',\n];\nconst WEBHOOK_EVENTS = ['signout', 'account.unlinked'];\nconst JSON_POSTS = ['POST /code', 'POST /logout', 'POST /link'];\n\nexport type RouteName = (typeof ROUTE_NAMES)[number];\n\nexport interface SessionUser {\n address: string;\n did: string;\n roles: BlockAuthRole[];\n externalId: string | null;\n loggedInAt: number;\n expiresAt: number;\n data?: Record<string, unknown>;\n}\n\nexport interface SessionStore {\n get(key: string): unknown | Promise<unknown>;\n set(key: string, value: unknown): void | Promise<void>;\n remove(key: string): void | Promise<void>;\n regenerate(): void | Promise<void>;\n}\n\nexport interface AppUser {\n id: string | number;\n linked?: boolean;\n}\n\nexport interface RouteContext {\n session: SessionStore;\n request?: unknown;\n}\n\nexport interface RouteResponse {\n status: number;\n body: Record<string, unknown>;\n}\n\nexport type MaybePromise<T> = T | Promise<T>;\n\nexport interface BlockAuthRoutesOptions {\n apiKey: string;\n apiSecret: string;\n appUrl?: string;\n allowedOrigins?: string[];\n sessionKey?: string;\n maxSessionSeconds?: number;\n basePath?: string;\n routes?: Partial<Record<RouteName, boolean>>;\n onLogin?: (\n user: SessionUser,\n context: RouteContext,\n ) => MaybePromise<Record<string, unknown> | false | void>;\n onLogout?: (user: SessionUser, context: RouteContext) => MaybePromise<void>;\n appUser?: (context: RouteContext) => MaybePromise<AppUser | null | undefined>;\n onLinked?: (\n appUserId: string | number,\n user: TokenResult,\n context: RouteContext,\n ) => MaybePromise<void>;\n onSignout?: (event: WebhookEvent) => MaybePromise<void>;\n onUnlinked?: (event: WebhookEvent) => MaybePromise<void>;\n isReplay?: (jti: string, event: WebhookEvent) => MaybePromise<boolean>;\n isRevoked?: (user: SessionUser) => MaybePromise<boolean>;\n webhookMaxSkewSeconds?: number;\n wsUrl?: string;\n timeoutMs?: number;\n client?: AuthCodeClient;\n logger?: (message: string, error?: unknown) => void;\n}\n\nexport function originOf(url: unknown): string | null {\n if (typeof url !== 'string') return null;\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n if (\n !['http:', 'https:'].includes(parsed.protocol) ||\n !parsed.hostname ||\n parsed.username ||\n parsed.password\n ) {\n return null;\n }\n return parsed.origin;\n}\n\nfunction error(status: number, code: string): RouteResponse {\n return { status, body: { error: code } };\n}\n\nfunction isJson(contentType: string | undefined): boolean {\n return (\n (contentType ?? '').split(';', 1)[0].trim().toLowerCase() ===\n 'application/json'\n );\n}\n\nfunction parseJson(rawBody: string): unknown {\n try {\n return JSON.parse(rawBody);\n } catch {\n return null;\n }\n}\n\nfunction publicUser(user: SessionUser): Record<string, unknown> {\n return { address: user.address, did: user.did, roles: user.roles };\n}\n\nexport class BlockAuthRoutes {\n readonly client: AuthCodeClient;\n readonly basePath: string;\n readonly allowedOrigins: ReadonlySet<string>;\n private readonly options: BlockAuthRoutesOptions;\n private readonly sessionKey: string;\n private readonly enabled: Record<RouteName, boolean>;\n\n constructor(options: BlockAuthRoutesOptions) {\n const origins =\n options.allowedOrigins ?? (options.appUrl ? [options.appUrl] : []);\n const resolved = new Set<string>();\n for (const url of origins) {\n const origin = originOf(url);\n if (!origin)\n throw new BlockAuthError(\n `Invalid origin in appUrl or allowedOrigins: ${String(url)}`,\n );\n resolved.add(origin);\n }\n if (resolved.size === 0)\n throw new BlockAuthError(\n 'Set appUrl or allowedOrigins: the origins your frontend runs on',\n );\n const unknown = Object.keys(options.routes ?? {}).filter(\n (name) => !ROUTE_NAMES.includes(name as RouteName),\n );\n if (unknown.length > 0)\n throw new BlockAuthError(`Unknown routes: ${unknown.join(', ')}`);\n\n this.options = options;\n this.client =\n options.client ??\n new AuthCodeClient(options.apiKey, options.apiSecret, {\n wsUrl: options.wsUrl,\n timeoutMs: options.timeoutMs,\n });\n this.allowedOrigins = resolved;\n this.basePath =\n '/' + (options.basePath ?? DEFAULT_BASE_PATH).replace(/^\\/+|\\/+$/g, '');\n this.sessionKey = options.sessionKey ?? 'blockauth_user';\n this.enabled = {\n code: true,\n me: true,\n logout: true,\n link: true,\n webhook: true,\n ...options.routes,\n };\n }\n\n async currentUser(context: RouteContext): Promise<SessionUser | null> {\n const user = (await context.session.get(this.sessionKey)) as\n | SessionUser\n | null\n | undefined;\n const valid =\n !!user &&\n typeof user === 'object' &&\n typeof user.address === 'string' &&\n typeof user.expiresAt === 'number' &&\n Date.now() / 1000 < user.expiresAt &&\n !(this.options.isRevoked && (await this.options.isRevoked(user)));\n if (!valid) {\n if (user != null) await context.session.remove(this.sessionKey);\n return null;\n }\n return user;\n }\n\n async code(body: unknown, context: RouteContext): Promise<RouteResponse> {\n const params = this.codeParams(body);\n if (!params) return error(400, 'invalid_request');\n let result: TokenResult;\n try {\n result = await this.client.exchangeCode(\n params.code,\n params.redirectUri,\n params.codeVerifier,\n );\n } catch (err) {\n this.log('Block-Auth could not redeem the login code', err);\n return error(502, 'blockauth_error');\n }\n const expiresAt = this.expiresAt(result.signedExpiredAt);\n if (\n typeof result.address !== 'string' ||\n !result.address ||\n expiresAt === null\n ) {\n this.log('Block-Auth returned a user without address or signedExpiredAt');\n return error(502, 'blockauth_error');\n }\n const user: SessionUser = {\n address: result.address,\n did:\n typeof result.did === 'string' && result.did\n ? result.did\n : result.address,\n roles: Array.isArray(result.roles) ? result.roles : [],\n externalId: result.externalId ?? null,\n loggedInAt: Math.floor(Date.now() / 1000),\n expiresAt,\n };\n if (this.options.onLogin) {\n const extra = await this.options.onLogin(user, context);\n if (extra === false) {\n await this.client\n .signOut(user.address)\n .catch((err) =>\n this.log('Block-Auth sign out of a rejected login failed', err),\n );\n return error(403, 'forbidden');\n }\n if (extra && typeof extra === 'object') user.data = extra;\n }\n await context.session.regenerate();\n await context.session.set(this.sessionKey, user);\n return { status: 200, body: publicUser(user) };\n }\n\n async me(context: RouteContext): Promise<RouteResponse> {\n const user = await this.currentUser(context);\n return user\n ? { status: 200, body: publicUser(user) }\n : error(401, 'unauthenticated');\n }\n\n async logout(context: RouteContext): Promise<RouteResponse> {\n const user = await this.currentUser(context);\n if (user) {\n await this.client\n .signOut(user.address)\n .catch((err) =>\n this.log(\n 'Block-Auth sign out failed; the local session is closed anyway',\n err,\n ),\n );\n }\n await context.session.remove(this.sessionKey);\n await context.session.regenerate();\n if (user && this.options.onLogout)\n await this.options.onLogout(user, context);\n return { status: 200, body: { ok: true } };\n }\n\n async linkStatus(context: RouteContext): Promise<RouteResponse> {\n if (!this.options.appUser) return error(404, 'not_found');\n const appUser = await this.resolveAppUser(context);\n return appUser\n ? { status: 200, body: { linked: appUser.linked === true } }\n : error(401, 'unauthenticated');\n }\n\n async link(body: unknown, context: RouteContext): Promise<RouteResponse> {\n if (!this.options.appUser) return error(404, 'not_found');\n const appUser = await this.resolveAppUser(context);\n if (!appUser) return error(401, 'unauthenticated');\n const params = this.codeParams(body);\n if (!params) return error(400, 'invalid_request');\n let result: TokenResult;\n try {\n result = await this.client.exchangeCode(\n params.code,\n params.redirectUri,\n params.codeVerifier,\n String(appUser.id),\n );\n } catch (err) {\n if (err instanceof BlockAuthError && LINK_CONFLICTS.includes(err.message))\n return error(409, 'already_linked');\n this.log('Block-Auth could not link the account', err);\n return error(502, 'blockauth_error');\n }\n if (this.options.onLinked)\n await this.options.onLinked(appUser.id, result, context);\n return { status: 200, body: { linked: true } };\n }\n\n async webhook(\n rawBody: string | Buffer,\n timestamp?: string,\n signature?: string,\n ): Promise<RouteResponse> {\n let event: WebhookEvent;\n try {\n event = this.client.verifyWebhook(\n rawBody,\n timestamp,\n signature,\n this.options.webhookMaxSkewSeconds,\n );\n } catch (err) {\n this.log('Rejected a webhook that failed verification', err);\n return error(401, 'invalid_signature');\n }\n if (event.apiKey !== this.options.apiKey)\n return error(401, 'invalid_signature');\n if (\n !WEBHOOK_EVENTS.includes(event.event) ||\n typeof event.address !== 'string'\n ) {\n return { status: 200, body: { ok: true, ignored: true } };\n }\n if (\n this.options.isReplay &&\n (typeof event.jti !== 'string' ||\n (await this.options.isReplay(event.jti, event)))\n ) {\n return error(409, 'replayed');\n }\n const handler =\n event.event === 'signout'\n ? this.options.onSignout\n : this.options.onUnlinked;\n if (handler) await handler(event);\n return { status: 200, body: { ok: true } };\n }\n\n async dispatch(\n method: string,\n path: string,\n rawBody: string | Buffer,\n header: (name: string) => string | undefined,\n context: RouteContext,\n ): Promise<RouteResponse | null> {\n if (!path.startsWith(this.basePath + '/')) return null;\n const route = `${method.toUpperCase()} ${path.slice(this.basePath.length)}`;\n const name = (\n {\n 'POST /code': 'code',\n 'GET /me': 'me',\n 'POST /logout': 'logout',\n 'GET /link': 'link',\n 'POST /link': 'link',\n 'POST /webhook': 'webhook',\n } as Record<string, RouteName>\n )[route];\n if (!name || !this.enabled[name]) return null;\n if (JSON_POSTS.includes(route) && !isJson(header('content-type')))\n return error(415, 'unsupported_media_type');\n const text =\n typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');\n switch (route) {\n case 'POST /code':\n return this.code(parseJson(text), context);\n case 'GET /me':\n return this.me(context);\n case 'POST /logout':\n return this.logout(context);\n case 'GET /link':\n return this.linkStatus(context);\n case 'POST /link':\n return this.link(parseJson(text), context);\n default:\n return this.webhook(\n rawBody,\n header('x-blockauth-timestamp'),\n header('x-blockauth-signature'),\n );\n }\n }\n\n private codeParams(\n body: unknown,\n ): { code: string; codeVerifier: string; redirectUri: string } | null {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null;\n const { code, codeVerifier, redirectUri } = body as Record<string, unknown>;\n for (const value of [code, codeVerifier, redirectUri]) {\n if (\n typeof value !== 'string' ||\n !value ||\n value.length > MAX_PARAM_LENGTH\n )\n return null;\n }\n const origin = originOf(redirectUri);\n if (!origin || !this.allowedOrigins.has(origin)) return null;\n return {\n code: code as string,\n codeVerifier: codeVerifier as string,\n redirectUri: redirectUri as string,\n };\n }\n\n private expiresAt(signedExpiredAt: unknown): number | null {\n if (typeof signedExpiredAt !== 'string') return null;\n const match = /^(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})$/.exec(\n signedExpiredAt,\n );\n if (!match) return null;\n const [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n let expiresAt = Date.UTC(year, month - 1, day, hour, minute, second) / 1000;\n const now = Math.floor(Date.now() / 1000);\n if (this.options.maxSessionSeconds !== undefined) {\n expiresAt = Math.min(\n expiresAt,\n now + Math.max(1, Math.floor(this.options.maxSessionSeconds)),\n );\n }\n return Number.isFinite(expiresAt) && expiresAt > now ? expiresAt : null;\n }\n\n private async resolveAppUser(context: RouteContext): Promise<AppUser | null> {\n const appUser = await this.options.appUser?.(context);\n if (!appUser || typeof appUser !== 'object') return null;\n const { id } = appUser;\n if (\n !(\n (typeof id === 'string' && id) ||\n (typeof id === 'number' && Number.isFinite(id))\n )\n )\n return null;\n return appUser;\n }\n\n private log(message: string, err?: unknown): void {\n this.options.logger?.(message, err);\n }\n}\n"]}
|