@seamless-auth/core 0.5.1-beta.20260523222121 → 0.5.1

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 CHANGED
@@ -23,6 +23,7 @@ If you are building a custom adapter (Express, Fastify, Next.js, Hono, etc.), th
23
23
  - Core authentication state machine
24
24
  - Cookie validation and refresh logic
25
25
  - Service-token–based API ↔ Auth Server communication
26
+ - Framework-agnostic OAuth provider discovery/start/callback helpers
26
27
  - Stateless, cryptographically verifiable flows
27
28
 
28
29
  This package **does not**:
@@ -87,6 +88,10 @@ Key exports include:
87
88
  - `refreshAccessToken(...)` – rotates expired access sessions
88
89
  - `verifyCookieJwt(...)` – verifies signed cookie payloads
89
90
  - `createServiceToken(...)` – creates short-lived M2M assertions
91
+ - `hasScopedRole(...)` – checks scoped role grants such as `admin:read`
92
+ - `listOAuthProvidersHandler(...)` – retrieves public OAuth provider metadata
93
+ - `startOAuthLoginHandler(...)` – starts an OAuth authorization-code login
94
+ - `finishOAuthLoginHandler(...)` – finishes OAuth login and returns cookie instructions
90
95
 
91
96
  These functions return **descriptive results**, not HTTP responses.
92
97
 
@@ -121,6 +126,70 @@ if (result.type === "error") {
121
126
 
122
127
  ---
123
128
 
129
+ ## OAuth Helper Flow
130
+
131
+ The core OAuth helpers are designed for framework adapters. They do not redirect, set cookies, or
132
+ read secrets. They proxy to the Seamless Auth API and return plain result objects that your adapter
133
+ turns into HTTP responses.
134
+
135
+ ```ts
136
+ const providers = await listOAuthProvidersHandler({
137
+ authServerUrl: "https://auth.example.com",
138
+ });
139
+
140
+ const started = await startOAuthLoginHandler(
141
+ {
142
+ providerId: "google",
143
+ body: {
144
+ redirectUri: "https://app.example.com/oauth/callback",
145
+ returnTo: "https://app.example.com/dashboard",
146
+ },
147
+ },
148
+ { authServerUrl: "https://auth.example.com" },
149
+ );
150
+
151
+ const finished = await finishOAuthLoginHandler(
152
+ {
153
+ providerId: "google",
154
+ body: {
155
+ code: "provider-code",
156
+ state: "signed-state-from-start",
157
+ },
158
+ },
159
+ {
160
+ authServerUrl: "https://auth.example.com",
161
+ accessCookieName: "seamless-access",
162
+ refreshCookieName: "seamless-refresh",
163
+ },
164
+ );
165
+
166
+ if (finished.setCookies) {
167
+ // adapter signs and applies access/refresh cookies
168
+ }
169
+ ```
170
+
171
+ The Seamless Auth API handles state validation, provider token exchange, userinfo lookup, and
172
+ provider identity linking. Core and adapter code never store provider access tokens.
173
+
174
+ ---
175
+
176
+ ## Scoped Roles
177
+
178
+ Core exports `hasScopedRole` and `roleGrantsAccess` for framework adapters and custom servers that
179
+ need the same authorization semantics as `@seamless-auth/express`.
180
+
181
+ ```ts
182
+ import { hasScopedRole } from "@seamless-auth/core";
183
+
184
+ hasScopedRole(["admin:write"], "admin:read"); // true
185
+ hasScopedRole(["admin:read"], "admin:write"); // false
186
+ ```
187
+
188
+ Plain roles remain backwards compatible. `admin` grants `admin:read` and `admin:write`, while
189
+ `admin:read` does not satisfy a plain `admin` check.
190
+
191
+ ---
192
+
124
193
  ## Testing
125
194
 
126
195
  All logic in this package is tested against compiled output (`dist/`),
@@ -0,0 +1,39 @@
1
+ import type { CookiePayload } from "../ensureCookies.js";
2
+ export interface OAuthHandlerOptions {
3
+ authServerUrl: string;
4
+ cookieDomain?: string;
5
+ accessCookieName: string;
6
+ refreshCookieName: string;
7
+ }
8
+ export interface OAuthRequestInput {
9
+ providerId?: string;
10
+ body?: unknown;
11
+ forwardedClientIp?: string;
12
+ }
13
+ export interface OAuthHandlerResult {
14
+ status: number;
15
+ body?: unknown;
16
+ error?: unknown;
17
+ setCookies?: {
18
+ name: string;
19
+ value: CookiePayload;
20
+ ttl: number;
21
+ domain?: string;
22
+ }[];
23
+ }
24
+ export declare function listOAuthProvidersHandler(opts: Pick<OAuthHandlerOptions, "authServerUrl">): Promise<{
25
+ body: any;
26
+ status: number;
27
+ } | {
28
+ error: any;
29
+ status: number;
30
+ }>;
31
+ export declare function startOAuthLoginHandler(input: OAuthRequestInput, opts: Pick<OAuthHandlerOptions, "authServerUrl">): Promise<{
32
+ body: any;
33
+ status: number;
34
+ } | {
35
+ error: any;
36
+ status: number;
37
+ }>;
38
+ export declare function finishOAuthLoginHandler(input: OAuthRequestInput, opts: OAuthHandlerOptions): Promise<OAuthHandlerResult>;
39
+ //# sourceMappingURL=oauthHandlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthHandlers.d.ts","sourceRoot":"","sources":["../../src/handlers/oauthHandlers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,aAAa,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;CACL;AAED,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC;;;;;;GAYjD;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC;;;;;;GAiBjD;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAiE7B"}
@@ -0,0 +1,77 @@
1
+ import { authFetch } from "../authFetch.js";
2
+ import { verifySignedAuthResponse } from "../verifySignedAuthResponse.js";
3
+ export async function listOAuthProvidersHandler(opts) {
4
+ const up = await authFetch(`${opts.authServerUrl}/oauth/providers`, {
5
+ method: "GET",
6
+ });
7
+ const data = await up.json();
8
+ return {
9
+ status: up.status,
10
+ ...(up.ok ? { body: data } : { error: data }),
11
+ };
12
+ }
13
+ export async function startOAuthLoginHandler(input, opts) {
14
+ const up = await authFetch(`${opts.authServerUrl}/oauth/${encodeURIComponent(input.providerId)}/start`, {
15
+ method: "POST",
16
+ body: input.body,
17
+ forwardedClientIp: input.forwardedClientIp,
18
+ });
19
+ const data = await up.json();
20
+ return {
21
+ status: up.status,
22
+ ...(up.ok ? { body: data } : { error: data }),
23
+ };
24
+ }
25
+ export async function finishOAuthLoginHandler(input, opts) {
26
+ const up = await authFetch(`${opts.authServerUrl}/oauth/${encodeURIComponent(input.providerId)}/callback`, {
27
+ method: "POST",
28
+ body: input.body,
29
+ forwardedClientIp: input.forwardedClientIp,
30
+ });
31
+ const data = await up.json();
32
+ if (!up.ok) {
33
+ return {
34
+ status: up.status,
35
+ error: data,
36
+ };
37
+ }
38
+ const verifiedAccessToken = await verifySignedAuthResponse(data.token, opts.authServerUrl);
39
+ if (!verifiedAccessToken) {
40
+ throw new Error("Invalid signed response from Auth Server");
41
+ }
42
+ if (verifiedAccessToken.sub !== data.sub) {
43
+ throw new Error("Signature mismatch with data payload");
44
+ }
45
+ const sessionId = typeof verifiedAccessToken.sid === "string"
46
+ ? verifiedAccessToken.sid
47
+ : undefined;
48
+ return {
49
+ status: up.status,
50
+ body: data,
51
+ setCookies: [
52
+ {
53
+ name: opts.accessCookieName,
54
+ value: {
55
+ sub: data.sub,
56
+ ...(sessionId === undefined ? {} : { sessionId }),
57
+ roles: data.roles,
58
+ email: data.email,
59
+ phone: data.phone,
60
+ organizationId: data.organizationId ?? null,
61
+ },
62
+ ttl: data.ttl,
63
+ domain: opts.cookieDomain,
64
+ },
65
+ {
66
+ name: opts.refreshCookieName,
67
+ value: {
68
+ sub: data.sub,
69
+ refreshToken: data.refreshToken,
70
+ },
71
+ ttl: data.refreshTtl,
72
+ domain: opts.cookieDomain,
73
+ },
74
+ ],
75
+ };
76
+ }
77
+ //# sourceMappingURL=oauthHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthHandlers.js","sourceRoot":"","sources":["../../src/handlers/oauthHandlers.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./verifyCookieJwt.js";
5
5
  export * from "./refreshAccessToken.js";
6
6
  export * from "./getSeamlessUser.js";
7
7
  export * from "./createServiceToken.js";
8
+ export * from "./scopedRoles.js";
8
9
  export * from "./handlers/login.js";
9
10
  export * from "./handlers/finishLogin.js";
10
11
  export * from "./handlers/register.js";
@@ -17,4 +18,5 @@ export * from "./handlers/verifyMagicLinkHandler.js";
17
18
  export * from "./handlers/requestMagicLinkHandler.js";
18
19
  export * from "./handlers/pollMagicLinkConfirmationHandler.js";
19
20
  export * from "./handlers/switchOrganizationHandler.js";
21
+ export * from "./handlers/oauthHandlers.js";
20
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAExC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,yCAAyC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AAEjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,yCAAyC,CAAC;AACxD,cAAc,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export * from "./verifyCookieJwt.js";
5
5
  export * from "./refreshAccessToken.js";
6
6
  export * from "./getSeamlessUser.js";
7
7
  export * from "./createServiceToken.js";
8
+ export * from "./scopedRoles.js";
8
9
  export * from "./handlers/login.js";
9
10
  export * from "./handlers/finishLogin.js";
10
11
  export * from "./handlers/register.js";
@@ -17,4 +18,5 @@ export * from "./handlers/verifyMagicLinkHandler.js";
17
18
  export * from "./handlers/requestMagicLinkHandler.js";
18
19
  export * from "./handlers/pollMagicLinkConfirmationHandler.js";
19
20
  export * from "./handlers/switchOrganizationHandler.js";
21
+ export * from "./handlers/oauthHandlers.js";
20
22
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AAExC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,yCAAyC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AAEjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,yCAAyC,CAAC;AACxD,cAAc,6BAA6B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function roleGrantsAccess(grantedRole: string, requiredRole: string): boolean;
2
+ export declare function hasScopedRole(grantedRoles: unknown, requiredRoles: string | string[]): boolean;
3
+ //# sourceMappingURL=scopedRoles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopedRoles.d.ts","sourceRoot":"","sources":["../src/scopedRoles.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAsCnF;AAED,wBAAgB,aAAa,CAC3B,YAAY,EAAE,OAAO,EACrB,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,GAC/B,OAAO,CAaT"}
@@ -0,0 +1,39 @@
1
+ export function roleGrantsAccess(grantedRole, requiredRole) {
2
+ const granted = grantedRole.trim();
3
+ const required = requiredRole.trim();
4
+ if (!granted || !required) {
5
+ return false;
6
+ }
7
+ if (granted === required) {
8
+ return true;
9
+ }
10
+ if (granted.endsWith(":*")) {
11
+ const prefix = granted.slice(0, -2);
12
+ return required === prefix || required.startsWith(`${prefix}:`);
13
+ }
14
+ if (!required.includes(":")) {
15
+ return false;
16
+ }
17
+ if (!granted.includes(":")) {
18
+ return required.startsWith(`${granted}:`);
19
+ }
20
+ const grantedParts = granted.split(":");
21
+ const requiredParts = required.split(":");
22
+ const grantedAction = grantedParts.at(-1);
23
+ const requiredAction = requiredParts.at(-1);
24
+ const grantedPrefix = grantedParts.slice(0, -1);
25
+ const requiredPrefix = requiredParts.slice(0, -1);
26
+ return (grantedAction === "write" &&
27
+ requiredAction === "read" &&
28
+ grantedPrefix.length === requiredPrefix.length &&
29
+ grantedPrefix.every((part, index) => part === requiredPrefix[index]));
30
+ }
31
+ export function hasScopedRole(grantedRoles, requiredRoles) {
32
+ if (!Array.isArray(grantedRoles)) {
33
+ return false;
34
+ }
35
+ const granted = grantedRoles.filter((role) => typeof role === "string");
36
+ const required = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
37
+ return required.some((requiredRole) => granted.some((grantedRole) => roleGrantsAccess(grantedRole, requiredRole)));
38
+ }
39
+ //# sourceMappingURL=scopedRoles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopedRoles.js","sourceRoot":"","sources":["../src/scopedRoles.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,YAAoB;IACxE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;IAErC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,cAAc,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAElD,OAAO,CACL,aAAa,KAAK,OAAO;QACzB,cAAc,KAAK,MAAM;QACzB,aAAa,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM;QAC9C,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,cAAc,CAAC,KAAK,CAAC,CAAC,CACrE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,YAAqB,EACrB,aAAgC;IAEhC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CACjC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CACnD,CAAC;IACF,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAEhF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CACpC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAC3E,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamless-auth/core",
3
- "version": "0.5.1-beta.20260523222121",
3
+ "version": "0.5.1",
4
4
  "description": "Framework-agnostic core authentication logic for SeamlessAuth",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "Fells Code, LLC",