@~lyre/auth 0.0.8 → 0.0.9

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.
@@ -278,6 +278,36 @@ async function introspectServiceKey(input) {
278
278
  return null;
279
279
  }
280
280
  }
281
+ async function logoutFromAccounts(input) {
282
+ const base = normalizeOptionalString(input.config.baseUrl);
283
+ if (!base || !input.accessToken) return { success: false };
284
+ const fetchImpl = input.fetchImpl ?? fetch;
285
+ try {
286
+ const res = await fetchImpl(new URL("/api/auth/logout", base), {
287
+ method: "POST",
288
+ headers: {
289
+ "content-type": "application/json",
290
+ authorization: `Bearer ${input.accessToken}`,
291
+ accept: "application/json"
292
+ },
293
+ body: JSON.stringify({ all: input.all ?? false })
294
+ });
295
+ if (!res.ok) return { success: false };
296
+ return await res.json();
297
+ } catch {
298
+ return { success: false };
299
+ }
300
+ }
301
+ function verifyWebhookSignature(rawBody, signature, secret) {
302
+ if (!signature || !secret) return false;
303
+ const provided = (signature.startsWith("sha256=") ? signature.slice(7) : signature).toLowerCase();
304
+ if (!/^[0-9a-f]{64}$/.test(provided)) return false;
305
+ const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
306
+ const a = Buffer.from(provided, "hex");
307
+ const b = Buffer.from(expected, "hex");
308
+ if (a.length !== b.length) return false;
309
+ return timingSafeEqual(a, b);
310
+ }
281
311
  function scopeSatisfied(granted, required) {
282
312
  if (!granted || granted.length === 0) return false;
283
313
  return granted.includes(required);
@@ -296,5 +326,7 @@ export {
296
326
  clearPlatformSessionCookie,
297
327
  serviceKeyFromRequest,
298
328
  introspectServiceKey,
329
+ logoutFromAccounts,
330
+ verifyWebhookSignature,
299
331
  scopeSatisfied
300
332
  };
package/dist/index.d.ts CHANGED
@@ -162,6 +162,20 @@ declare function introspectServiceKey(input: {
162
162
  key: string;
163
163
  fetchImpl?: typeof fetch;
164
164
  }): Promise<ServiceKeyContext | null>;
165
+ declare function logoutFromAccounts(input: {
166
+ config: Pick<AccountsClientConfig, 'baseUrl'>;
167
+ accessToken: string;
168
+ all?: boolean;
169
+ fetchImpl?: typeof fetch;
170
+ }): Promise<{
171
+ success: boolean;
172
+ revokedCount?: number;
173
+ }>;
174
+ declare function verifyWebhookSignature(rawBody: string, signature: string | null | undefined, secret: string): boolean;
175
+ type AccountsWebhookEvent = {
176
+ type: string;
177
+ [key: string]: unknown;
178
+ };
165
179
  declare function scopeSatisfied(granted: string[] | undefined, required: string): boolean;
166
180
 
167
- export { type AccountsClientConfig, type AccountsIdentity, type AccountsTokenResponse, type AuthenticatedPrincipal, type AuthorizationState, type CookieOptions, type LocalUser, type PermissionSet, type PlatformSession, type ServiceKeyApp, type ServiceKeyContext, type SyncAccountsUser, type SyncAccountsUserResult, type TenantAccessContext, type TenantContext, type TenantMembership, beginAccountsLoginRedirect, clearPlatformSessionCookie, createAccountsClientConfig, createPlatformAuth, exchangeAuthorizationCode, handleAccountsCallback, identityPassthroughSync, introspectServiceKey, readPlatformSessionCookie, resolveActiveTenant, scopeSatisfied, serviceKeyFromRequest, syncAccountsUser };
181
+ export { type AccountsClientConfig, type AccountsIdentity, type AccountsTokenResponse, type AccountsWebhookEvent, type AuthenticatedPrincipal, type AuthorizationState, type CookieOptions, type LocalUser, type PermissionSet, type PlatformSession, type ServiceKeyApp, type ServiceKeyContext, type SyncAccountsUser, type SyncAccountsUserResult, type TenantAccessContext, type TenantContext, type TenantMembership, beginAccountsLoginRedirect, clearPlatformSessionCookie, createAccountsClientConfig, createPlatformAuth, exchangeAuthorizationCode, handleAccountsCallback, identityPassthroughSync, introspectServiceKey, logoutFromAccounts, readPlatformSessionCookie, resolveActiveTenant, scopeSatisfied, serviceKeyFromRequest, syncAccountsUser, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -7,12 +7,14 @@ import {
7
7
  handleAccountsCallback,
8
8
  identityPassthroughSync,
9
9
  introspectServiceKey,
10
+ logoutFromAccounts,
10
11
  readPlatformSessionCookie,
11
12
  resolveActiveTenant,
12
13
  scopeSatisfied,
13
14
  serviceKeyFromRequest,
14
- syncAccountsUser
15
- } from "./chunk-2FSPKJAC.js";
15
+ syncAccountsUser,
16
+ verifyWebhookSignature
17
+ } from "./chunk-QIAL6IDV.js";
16
18
  export {
17
19
  beginAccountsLoginRedirect,
18
20
  clearPlatformSessionCookie,
@@ -22,9 +24,11 @@ export {
22
24
  handleAccountsCallback,
23
25
  identityPassthroughSync,
24
26
  introspectServiceKey,
27
+ logoutFromAccounts,
25
28
  readPlatformSessionCookie,
26
29
  resolveActiveTenant,
27
30
  scopeSatisfied,
28
31
  serviceKeyFromRequest,
29
- syncAccountsUser
32
+ syncAccountsUser,
33
+ verifyWebhookSignature
30
34
  };
@@ -1,5 +1,5 @@
1
1
  import { RequestEvent, Handle } from '@sveltejs/kit';
2
- import { PlatformSession, AccountsClientConfig, ServiceKeyContext, SyncAccountsUser, CookieOptions } from './index.js';
2
+ import { AccountsWebhookEvent, PlatformSession, AccountsClientConfig, ServiceKeyContext, SyncAccountsUser, CookieOptions } from './index.js';
3
3
  export { clearPlatformSessionCookie } from './index.js';
4
4
 
5
5
  type SvelteKitAuthOptions = {
@@ -51,5 +51,15 @@ type ServiceKeyLocals = {
51
51
  serviceKey?: ServiceKeyContext | null;
52
52
  };
53
53
  declare function createServiceKeyHandle(opts: ServiceKeyHandleOptions): Handle;
54
+ type AccountsWebhookOptions = {
55
+ secret: string;
56
+ /** Route to receive webhooks on. Default '/api/auth/accounts/webhook'. */
57
+ path?: string;
58
+ /** Header carrying the signature. Default 'x-accounts-signature'. */
59
+ signatureHeader?: string;
60
+ /** React to a verified event. Throwing yields a 500 (Accounts may retry). */
61
+ onEvent: (event: AccountsWebhookEvent, raw: string, request: RequestEvent) => void | Promise<void>;
62
+ };
63
+ declare function createAccountsWebhookHandle(opts: AccountsWebhookOptions): Handle;
54
64
 
55
- export { type PlatformLocals, type ServiceKeyHandleOptions, type ServiceKeyLocals, type SvelteKitAuthOptions, createAuthHandle, createServiceKeyHandle };
65
+ export { type AccountsWebhookOptions, type PlatformLocals, type ServiceKeyHandleOptions, type ServiceKeyLocals, type SvelteKitAuthOptions, createAccountsWebhookHandle, createAuthHandle, createServiceKeyHandle };
package/dist/sveltekit.js CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  handleAccountsCallback,
5
5
  identityPassthroughSync,
6
6
  introspectServiceKey,
7
+ logoutFromAccounts,
7
8
  readPlatformSessionCookie,
8
- serviceKeyFromRequest
9
- } from "./chunk-2FSPKJAC.js";
9
+ serviceKeyFromRequest,
10
+ verifyWebhookSignature
11
+ } from "./chunk-QIAL6IDV.js";
10
12
 
11
13
  // src/sveltekit.ts
12
14
  import { createHash } from "crypto";
@@ -55,6 +57,10 @@ function createAuthHandle(opts) {
55
57
  ]);
56
58
  }
57
59
  if (path === logoutPath) {
60
+ if (session.accessToken) {
61
+ const all = event.url.searchParams.get("all") === "1" || event.url.searchParams.get("all") === "true";
62
+ await logoutFromAccounts({ config: opts.config, accessToken: session.accessToken, all });
63
+ }
58
64
  return redirectWithCookies(postLogoutPath, [
59
65
  clearPlatformSessionCookie(cookieOptions),
60
66
  `${STATE_COOKIE}=; ${cookieOptions?.sameSite === "none" ? "Path=/; HttpOnly; SameSite=None; Secure" : "Path=/; HttpOnly; SameSite=Lax"}; Max-Age=0`
@@ -102,8 +108,35 @@ function createServiceKeyHandle(opts) {
102
108
  });
103
109
  };
104
110
  }
111
+ function jsonResponse(status, body) {
112
+ return new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
113
+ }
114
+ function createAccountsWebhookHandle(opts) {
115
+ const path = opts.path ?? "/api/auth/accounts/webhook";
116
+ const sigHeader = opts.signatureHeader ?? "x-accounts-signature";
117
+ return async ({ event, resolve }) => {
118
+ if (event.url.pathname !== path || event.request.method !== "POST") return resolve(event);
119
+ const raw = await event.request.text();
120
+ if (!verifyWebhookSignature(raw, event.request.headers.get(sigHeader), opts.secret)) {
121
+ return jsonResponse(401, { ok: false, error: "Invalid webhook signature." });
122
+ }
123
+ let payload;
124
+ try {
125
+ payload = JSON.parse(raw);
126
+ } catch {
127
+ return jsonResponse(400, { ok: false, error: "Invalid JSON body." });
128
+ }
129
+ try {
130
+ await opts.onEvent({ ...payload, type: String(payload.type ?? "unknown") }, raw, event);
131
+ } catch {
132
+ return jsonResponse(500, { ok: false, error: "Webhook handler failed." });
133
+ }
134
+ return jsonResponse(200, { ok: true });
135
+ };
136
+ }
105
137
  export {
106
138
  clearPlatformSessionCookie,
139
+ createAccountsWebhookHandle,
107
140
  createAuthHandle,
108
141
  createServiceKeyHandle
109
142
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@~lyre/auth",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Shared Axis Accounts auth SDK — framework-agnostic session/identity core (HMAC-signed cookies, accounts login/callback/logout) plus an optional turnkey SvelteKit adapter.",
5
5
  "type": "module",
6
6
  "sideEffects": false,