@seamless-auth/express 0.0.1 → 0.0.2-beta.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
@@ -96,13 +96,12 @@ Everything happens securely between your API and a private Seamless Auth Server.
96
96
 
97
97
  ## Environment Variables
98
98
 
99
- | Variable | Description | Example |
100
- | ----------------------------- | -------------------------------------- | ------------------------- |
101
- | `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
102
- | `SEAMLESS_COOKIE_SIGNING_KEY` | Secret key for signing JWT cookies | `base64:...` |
103
- | `SEAMLESS_SERVICE_TOKEN` | Private key for API → Auth Server JWTs | RSA PEM |
104
- | `SERVICE_JWT_KEYID` | Key ID for JWKS | `service-main` |
105
- | `COOKIE_DOMAIN` | Domain for cookies | `.client.com` |
99
+ | Variable | Description | Example |
100
+ | ----------------------------- | --------------------------------------------- | ------------------------------ |
101
+ | `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
102
+ | `SEAMLESS_COOKIE_SIGNING_KEY` | Secret key for signing JWT cookies | `base64:...` |
103
+ | `SEAMLESS_SERVICE_TOKEN` | Private key for API → Auth Server JWTs | `Obtained via the auth portal` |
104
+ | `FRONTEND_URL` | URL of your website or https://localhost:5001 | `https://mySite.com` |
106
105
 
107
106
  ---
108
107
 
@@ -188,7 +187,7 @@ User shape
188
187
  → sets short-lived pre-auth cookie.
189
188
 
190
189
  2. **Frontend** → `/auth/webauthn/finish`
191
- → API proxies, validates, sets access cookie (`seamless_auth_access`).
190
+ → API proxies, validates, sets access cookie (`seamless-access`).
192
191
 
193
192
  3. **Subsequent API calls** → `/api/...`
194
193
  → `requireAuth()` verifies cookie and attaches user.
@@ -206,9 +205,10 @@ In order to develop with your Seamless Auth server instance, you will need to ha
206
205
  Example env:
207
206
 
208
207
  ```bash
209
- AUTH_SERVER_URL=http://https://<identifier>.seamlessauth.com # Found in the portal
210
- COOKIE_DOMAIN=localhost # Or frontend domain in prod
211
- SEAMLESS_COOKIE_SIGNING_KEY=local-secret-key # Found in the portal
208
+ AUTH_SERVER_URL=https://<identifier>.seamlessauth.com # Found in the portal
209
+ SEAMLESS_SERVICE_TOKEN=32byte-secret # Created and rotated in the portal
210
+ FRONTEND_URL=https://yourSite.com # Must match the value you have for Frontend Domain in the portal (Can be localhost:5001 when application is in demo mode)
211
+ SEAMLESS_COOKIE_SIGNING_KEY=local-secret-key # A key you make for signing your API's distributed cookies
212
212
  ```
213
213
 
214
214
  ---
@@ -217,7 +217,7 @@ SEAMLESS_COOKIE_SIGNING_KEY=local-secret-key # Found in the portal
217
217
 
218
218
  ```ts
219
219
  const AUTH_SERVER_URL = process.env.AUTH_SERVER_URL!;
220
- app.use(cors({ origin: "http://localhost:5001", credentials: true }));
220
+ app.use(cors({ origin: "https://localhost:5001", credentials: true }));
221
221
  app.use(express.json());
222
222
  app.use(cookieParser());
223
223
  app.use("/auth", createSeamlessAuthServer({ authServerUrl: AUTH_SERVER_URL }));
@@ -266,3 +266,4 @@ app.get("/api/test", requireAuth(), (req, res) => res.json({ ok: true }));
266
266
 
267
267
  MIT © 2025 Fells Code LLC
268
268
  Part of the **Seamless Auth** ecosystem.
269
+ https://seamlessauth.com
@@ -1,4 +1,48 @@
1
1
  import { Router } from "express";
2
2
  import type { SeamlessAuthServerOptions } from "./types";
3
+ /**
4
+ * Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
5
+ *
6
+ * This helper wires your API backend to a Seamless Auth instance running in
7
+ * "server mode." It automatically forwards login, registration, WebAuthn,
8
+ * logout, token refresh, and session validation routes to the auth server
9
+ * and handles all cookie management required for a seamless login flow.
10
+ *
11
+ * ### Responsibilities
12
+ * - Proxies all `/auth/*` routes to the upstream Seamless Auth server
13
+ * - Manages `access`, `registration`, `pre-auth`, and `refresh` cookies
14
+ * - Normalizes cookie settings for cross-domain or same-domain deployments
15
+ * - Ensures authentication routes behave consistently across environments
16
+ * - Provides shared middleware for auth flows
17
+ *
18
+ * ### Cookie Types
19
+ * - **accessCookie** – long-lived session cookie for authenticated API requests
20
+ * - **registrationCookie** – ephemeral cookie used during registration and OTP/WebAuthn flows
21
+ * - **preAuthCookie** – short-lived cookie used during login initiation
22
+ * - **refreshCookie** – opaque refresh token cookie used to rotate session tokens
23
+ *
24
+ * All cookie names and their domains may be customized via the `opts` parameter.
25
+ *
26
+ * ### Example
27
+ * ```ts
28
+ * app.use("/auth", createSeamlessAuthServer({
29
+ * authServerUrl: "https://identifier.seamlessauth.com",
30
+ * cookieDomain: "mycompany.com",
31
+ * accesscookieName: "sa_access",
32
+ * registrationCookieName: "sa_registration",
33
+ * refreshCookieName: "sa_refresh",
34
+ * }));
35
+ * ```
36
+ *
37
+ * @param opts - Configuration options for the Seamless Auth proxy:
38
+ * - `authServerUrl` — Base URL of your Seamless Auth instance (required)
39
+ * - `cookieDomain` — Domain attribute applied to all auth cookies
40
+ * - `accesscookieName` — Name of the session access cookie
41
+ * - `registrationCookieName` — Name of the ephemeral registration cookie
42
+ * - `refreshCookieName` — Name of the refresh token cookie
43
+ * - `preAuthCookieName` — Name of the cookie used during login initiation
44
+ *
45
+ * @returns An Express `Router` preconfigured with all Seamless Auth routes.
46
+ */
3
47
  export declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Router;
4
48
  //# sourceMappingURL=createServer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAqB,MAAM,EAAE,MAAM,SAAS,CAAC;AAQ7D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAIzD,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,yBAAyB,GAC9B,MAAM,CA6LR"}
1
+ {"version":3,"file":"createServer.d.ts","sourceRoot":"","sources":["../src/createServer.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAqB,MAAM,EAAE,MAAM,SAAS,CAAC;AAQ7D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,yBAAyB,GAC9B,MAAM,CA0LR"}
@@ -1,9 +1,53 @@
1
1
  import express from "express";
2
2
  import cookieParser from "cookie-parser";
3
- import { setSessionCookie, clearAllCookies, clearSessionCookie, } from "./internal/cookie";
4
- import { authFetch } from "./internal/authFetch";
5
- import { createEnsureCookiesMiddleware } from "./middleware/ensureCookies";
6
- import { verifySignedAuthResponse } from "./internal/verifySignedAuthResponse";
3
+ import { setSessionCookie, clearAllCookies, clearSessionCookie, } from './internal/cookie.js';
4
+ import { authFetch } from './internal/authFetch.js';
5
+ import { createEnsureCookiesMiddleware } from './middleware/ensureCookies.js';
6
+ import { verifySignedAuthResponse } from './internal/verifySignedAuthResponse.js';
7
+ /**
8
+ * Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
9
+ *
10
+ * This helper wires your API backend to a Seamless Auth instance running in
11
+ * "server mode." It automatically forwards login, registration, WebAuthn,
12
+ * logout, token refresh, and session validation routes to the auth server
13
+ * and handles all cookie management required for a seamless login flow.
14
+ *
15
+ * ### Responsibilities
16
+ * - Proxies all `/auth/*` routes to the upstream Seamless Auth server
17
+ * - Manages `access`, `registration`, `pre-auth`, and `refresh` cookies
18
+ * - Normalizes cookie settings for cross-domain or same-domain deployments
19
+ * - Ensures authentication routes behave consistently across environments
20
+ * - Provides shared middleware for auth flows
21
+ *
22
+ * ### Cookie Types
23
+ * - **accessCookie** – long-lived session cookie for authenticated API requests
24
+ * - **registrationCookie** – ephemeral cookie used during registration and OTP/WebAuthn flows
25
+ * - **preAuthCookie** – short-lived cookie used during login initiation
26
+ * - **refreshCookie** – opaque refresh token cookie used to rotate session tokens
27
+ *
28
+ * All cookie names and their domains may be customized via the `opts` parameter.
29
+ *
30
+ * ### Example
31
+ * ```ts
32
+ * app.use("/auth", createSeamlessAuthServer({
33
+ * authServerUrl: "https://identifier.seamlessauth.com",
34
+ * cookieDomain: "mycompany.com",
35
+ * accesscookieName: "sa_access",
36
+ * registrationCookieName: "sa_registration",
37
+ * refreshCookieName: "sa_refresh",
38
+ * }));
39
+ * ```
40
+ *
41
+ * @param opts - Configuration options for the Seamless Auth proxy:
42
+ * - `authServerUrl` — Base URL of your Seamless Auth instance (required)
43
+ * - `cookieDomain` — Domain attribute applied to all auth cookies
44
+ * - `accesscookieName` — Name of the session access cookie
45
+ * - `registrationCookieName` — Name of the ephemeral registration cookie
46
+ * - `refreshCookieName` — Name of the refresh token cookie
47
+ * - `preAuthCookieName` — Name of the cookie used during login initiation
48
+ *
49
+ * @returns An Express `Router` preconfigured with all Seamless Auth routes.
50
+ */
7
51
  export function createSeamlessAuthServer(opts) {
8
52
  const r = express.Router();
9
53
  r.use(express.json());
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createSeamlessAuthServer } from "./createServer";
2
- export { requireAuth } from "./middleware/requireAuth";
3
- export { requireRole } from "./middleware/requireRole";
4
- export { getSeamlessUser } from "./internal/getSeamlessUser";
1
+ import { createSeamlessAuthServer } from './createServer.js';
2
+ export { requireAuth } from './middleware/requireAuth.js';
3
+ export { requireRole } from './middleware/requireRole.js';
4
+ export { getSeamlessUser } from './internal/getSeamlessUser.js';
5
5
  export default createSeamlessAuthServer;
@@ -1,6 +1,6 @@
1
1
  import { CookieRequest } from "../middleware/ensureCookies";
2
2
  export interface AuthFetchOptions {
3
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
3
+ method?: "GET" | "POST" | "PUT" | "DELETE";
4
4
  body?: any;
5
5
  cookies?: string[];
6
6
  headers?: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"authFetch.d.ts","sourceRoot":"","sources":["../../src/internal/authFetch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,MAAM,EACX,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,OAAY,EAAE,GAAE,gBAAqB,qBAuDxE"}
1
+ {"version":3,"file":"authFetch.d.ts","sourceRoot":"","sources":["../../src/internal/authFetch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,MAAM,EACX,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,OAAY,EAAE,GAAE,gBAAqB,qBAiDxE"}
@@ -4,30 +4,26 @@ export async function authFetch(req, url, { method = "POST", body, cookies, head
4
4
  if (!serviceKey) {
5
5
  throw new Error("Cannot sign service token. Missing SEAMLESS_SERVICE_TOKEN");
6
6
  }
7
- console.debug("[SeamlessAuth] Performing authentication fetch to Auth server");
8
7
  // -------------------------------
9
8
  // Issue short-lived machine token
10
9
  // -------------------------------
11
10
  const token = jwt.sign({
12
- // Minimal, safe fields
13
11
  iss: process.env.FRONTEND_URL,
14
12
  aud: process.env.AUTH_SERVER_URL,
15
13
  sub: req.cookiePayload?.sub,
16
14
  roles: req.cookiePayload?.roles ?? [],
17
15
  iat: Math.floor(Date.now() / 1000),
18
16
  }, serviceKey, {
19
- expiresIn: "60s", // Short-lived = safer
17
+ expiresIn: "60s", // Short-lived
20
18
  algorithm: "HS256", // HMAC-based
21
- keyid: "dev-main", // For future rotation
22
19
  });
23
20
  const finalHeaders = {
24
21
  ...(method !== "GET" && { "Content-Type": "application/json" }),
25
22
  ...(cookies ? { Cookie: cookies.join("; ") } : {}),
26
- Authorization: `Bearer ${serviceKey}`,
23
+ Authorization: `Bearer ${token}`,
27
24
  ...headers,
28
25
  };
29
26
  let finalUrl = url;
30
- console.debug("[SeamlessAuth] URL ...", finalUrl);
31
27
  if (method === "GET" && body && typeof body === "object") {
32
28
  const qs = new URLSearchParams(body).toString();
33
29
  finalUrl += url.includes("?") ? `&${qs}` : `?${qs}`;
@@ -1,11 +1,51 @@
1
1
  import { CookieRequest } from "../middleware/ensureCookies.js";
2
2
  /**
3
- * Retrieves the Seamless Auth user information by calling the auth server's introspection endpoint.
4
- * Requires the sa_session (or custom) cookie to be present on the request.
3
+ * Retrieves the authenticated Seamless Auth user for a request by calling
4
+ * the upstream Seamless Auth Server’s introspection endpoint.
5
5
  *
6
- * @param req Express request object
7
- * @param authServerUrl Base URL of the client's auth server
8
- * @returns The user data object if valid, or null if invalid/unauthenticated
6
+ * This helper is used when server-side code needs the fully hydrated
7
+ * Seamless Auth user object (including roles, metadata, and profile fields),
8
+ * not just the JWT payload extracted from cookies.
9
+ *
10
+ * Unlike `requireAuth`, this helper does **not** enforce authentication.
11
+ * It simply returns:
12
+ * - The resolved user object (if the session is valid)
13
+ * - `null` if the session is invalid, expired, or missing
14
+ *
15
+ * ### Responsibilities
16
+ * - Extracts the access cookie (or refresh cookie when needed)
17
+ * - Calls the Seamless Auth Server’s `/internal/session/introspect` endpoint
18
+ * - Validates whether the session is active
19
+ * - Returns the user object or `null` without throwing
20
+ *
21
+ * ### Use Cases
22
+ * - Fetching the current user in internal APIs
23
+ * - Enriching backend requests with server-authoritative user information
24
+ * - Logging, analytics, auditing
25
+ * - Optional-auth routes that behave differently for signed-in users
26
+ *
27
+ * ### Example
28
+ * ```ts
29
+ * app.get("/portal/me", async (req, res) => {
30
+ * const user = await getSeamlessUser(req, process.env.SA_AUTH_SERVER_URL);
31
+ *
32
+ * if (!user) {
33
+ * return res.json({ user: null });
34
+ * }
35
+ *
36
+ * return res.json({ user });
37
+ * });
38
+ * ```
39
+ *
40
+ * ### Returns
41
+ * - A full Seamless Auth user object (if active)
42
+ * - `null` if not authenticated or session expired
43
+ *
44
+ * @param req - The Express request object containing auth cookies.
45
+ * @param authServerUrl - Base URL of the Seamless Auth instance to introspect against.
46
+ * @param cookieName - Name of the access cookie storing the session JWT (`"seamless-access"` by default).
47
+ *
48
+ * @returns The authenticated user object, or `null` if the session is inactive.
9
49
  */
10
50
  export declare function getSeamlessUser<T = any>(req: CookieRequest, authServerUrl: string, cookieName?: string): Promise<T | null>;
11
51
  //# sourceMappingURL=getSeamlessUser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getSeamlessUser.d.ts","sourceRoot":"","sources":["../../src/internal/getSeamlessUser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,GAAG,EAC3C,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,UAAU,GAAE,MAA+B,GAC1C,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBnB"}
1
+ {"version":3,"file":"getSeamlessUser.d.ts","sourceRoot":"","sources":["../../src/internal/getSeamlessUser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,GAAG,EAC3C,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,MAAM,EACrB,UAAU,GAAE,MAA0B,GACrC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAwBnB"}
@@ -1,14 +1,54 @@
1
1
  import { authFetch } from "./authFetch.js";
2
2
  import { verifyCookieJwt } from "./verifyCookieJwt.js";
3
3
  /**
4
- * Retrieves the Seamless Auth user information by calling the auth server's introspection endpoint.
5
- * Requires the sa_session (or custom) cookie to be present on the request.
4
+ * Retrieves the authenticated Seamless Auth user for a request by calling
5
+ * the upstream Seamless Auth Server’s introspection endpoint.
6
6
  *
7
- * @param req Express request object
8
- * @param authServerUrl Base URL of the client's auth server
9
- * @returns The user data object if valid, or null if invalid/unauthenticated
7
+ * This helper is used when server-side code needs the fully hydrated
8
+ * Seamless Auth user object (including roles, metadata, and profile fields),
9
+ * not just the JWT payload extracted from cookies.
10
+ *
11
+ * Unlike `requireAuth`, this helper does **not** enforce authentication.
12
+ * It simply returns:
13
+ * - The resolved user object (if the session is valid)
14
+ * - `null` if the session is invalid, expired, or missing
15
+ *
16
+ * ### Responsibilities
17
+ * - Extracts the access cookie (or refresh cookie when needed)
18
+ * - Calls the Seamless Auth Server’s `/internal/session/introspect` endpoint
19
+ * - Validates whether the session is active
20
+ * - Returns the user object or `null` without throwing
21
+ *
22
+ * ### Use Cases
23
+ * - Fetching the current user in internal APIs
24
+ * - Enriching backend requests with server-authoritative user information
25
+ * - Logging, analytics, auditing
26
+ * - Optional-auth routes that behave differently for signed-in users
27
+ *
28
+ * ### Example
29
+ * ```ts
30
+ * app.get("/portal/me", async (req, res) => {
31
+ * const user = await getSeamlessUser(req, process.env.SA_AUTH_SERVER_URL);
32
+ *
33
+ * if (!user) {
34
+ * return res.json({ user: null });
35
+ * }
36
+ *
37
+ * return res.json({ user });
38
+ * });
39
+ * ```
40
+ *
41
+ * ### Returns
42
+ * - A full Seamless Auth user object (if active)
43
+ * - `null` if not authenticated or session expired
44
+ *
45
+ * @param req - The Express request object containing auth cookies.
46
+ * @param authServerUrl - Base URL of the Seamless Auth instance to introspect against.
47
+ * @param cookieName - Name of the access cookie storing the session JWT (`"seamless-access"` by default).
48
+ *
49
+ * @returns The authenticated user object, or `null` if the session is inactive.
10
50
  */
11
- export async function getSeamlessUser(req, authServerUrl, cookieName = "seamless-auth-access") {
51
+ export async function getSeamlessUser(req, authServerUrl, cookieName = "seamless-access") {
12
52
  try {
13
53
  const payload = verifyCookieJwt(req.cookies[cookieName]);
14
54
  if (!payload) {
@@ -1 +1 @@
1
- {"version":3,"file":"ensureCookies.d.ts","sourceRoot":"","sources":["../../src/middleware/ensureCookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI1C,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,aAAa,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,yBAAyB,IA4BzE,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,MAAM,YAAY,EAClB,qBAAiB,wDA2FpB"}
1
+ {"version":3,"file":"ensureCookies.d.ts","sourceRoot":"","sources":["../../src/middleware/ensureCookies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAI1C,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,aAAa,CAAC,EAAE,UAAU,CAAC;CAC5B;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,yBAAyB,IA4BzE,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,MAAM,YAAY,EAClB,qBAAiB,wDAqFpB"}
@@ -32,12 +32,6 @@ export function createEnsureCookiesMiddleware(opts) {
32
32
  const AUTH_SERVER_URL = process.env.AUTH_SERVER;
33
33
  const cookieValue = req.cookies?.[name];
34
34
  const refreshCookieValue = req.cookies?.[opts.refreshCookieName];
35
- //
36
- // --- NEW REFRESH-AWARE LOGIC ---
37
- //
38
- // If required cookie is missing BUT refresh cookie exists,
39
- // allow request to proceed. requireAuth() will perform refresh.
40
- //
41
35
  if (required && !cookieValue) {
42
36
  if (refreshCookieValue) {
43
37
  console.log("[SeamlessAuth] Access token expired — attempting refresh");
@@ -61,7 +55,7 @@ export function createEnsureCookiesMiddleware(opts) {
61
55
  };
62
56
  return next();
63
57
  }
64
- // No required cookie AND no refresh cookie → hard fail
58
+ // No required cookie AND no refresh cookie
65
59
  return res.status(400).json({
66
60
  error: `Missing required cookie "${name}" for route ${req.path}`,
67
61
  hint: "Did you forget to call /auth/login/start first?",
@@ -1,9 +1,53 @@
1
1
  import { Request, Response, NextFunction } from "express";
2
2
  /**
3
- * Express middleware that verifies a Seamless Auth access cookie.
4
- * - Reads and verifies signed cookie JWT
5
- * - Attaches decoded payload to req.user
6
- * - Returns 401 if missing/invalid/expired
3
+ * Express middleware that enforces authentication using Seamless Auth cookies.
4
+ *
5
+ * This guard verifies the signed access cookie generated by the Seamless Auth
6
+ * server. If the access cookie is valid and unexpired, the decoded session
7
+ * payload is attached to `req.user` and the request proceeds.
8
+ *
9
+ * If the access cookie is expired or missing *but* a valid refresh cookie is
10
+ * present, the middleware automatically attempts a silent token refresh using
11
+ * the Seamless Auth server. When successful, new session cookies are issued and
12
+ * the request continues with an updated `req.user`.
13
+ *
14
+ * If neither the access token nor refresh token can validate the session,
15
+ * the middleware returns a 401 Unauthorized error and prevents further
16
+ * route execution.
17
+ *
18
+ * ### Responsibilities
19
+ * - Validates the Seamless Auth session access cookie
20
+ * - Attempts refresh-token–based session renewal when necessary
21
+ * - Populates `req.user` with the verified session payload
22
+ * - Handles all cookie rewriting during refresh flows
23
+ * - Acts as a request-level authentication guard for API routes
24
+ *
25
+ * ### Cookie Parameters
26
+ * - **cookieName** — Name of the access cookie that holds the signed session JWT
27
+ * - **refreshCookieName** — Name of the refresh cookie used for silent token refresh
28
+ * - **cookieDomain** — Domain or path value applied to issued cookies
29
+ *
30
+ * ### Example
31
+ * ```ts
32
+ * // Protect a route
33
+ * app.get("/api/me", requireAuth(), (req, res) => {
34
+ * res.json({ user: req.user });
35
+ * });
36
+ *
37
+ * // Custom cookie names (if your Seamless Auth server uses overrides)
38
+ * app.use(
39
+ * "/internal",
40
+ * requireAuth("sa_access", "sa_refresh", "mycompany.com"),
41
+ * internalRouter
42
+ * );
43
+ * ```
44
+ *
45
+ * @param cookieName - The access cookie name. Defaults to `"seamless-access"`.
46
+ * @param refreshCookieName - The refresh cookie name used for session rotation. Defaults to `"seamless-refresh"`.
47
+ * @param cookieDomain - Domain or path used when rewriting cookies. Defaults to `"/"`.
48
+ *
49
+ * @returns An Express middleware function that enforces Seamless Auth
50
+ * authentication on incoming requests.
7
51
  */
8
52
  export declare function requireAuth(cookieName?: string, refreshCookieName?: string, cookieDomain?: string): (req: Request, res: Response, next: NextFunction) => Promise<void>;
9
53
  //# sourceMappingURL=requireAuth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM1D;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,UAAU,SAAyB,EACnC,iBAAiB,SAA0B,EAC3C,YAAY,SAAM,IAahB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CA+EjB"}
1
+ {"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/middleware/requireAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAgB,WAAW,CACzB,UAAU,SAAoB,EAC9B,iBAAiB,SAAqB,EACtC,YAAY,SAAM,IAahB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,CA+EjB"}
@@ -2,18 +2,62 @@ import jwt from "jsonwebtoken";
2
2
  import { refreshAccessToken } from "../internal/refreshAccessToken.js";
3
3
  import { setSessionCookie } from "../internal/cookie.js";
4
4
  /**
5
- * Express middleware that verifies a Seamless Auth access cookie.
6
- * - Reads and verifies signed cookie JWT
7
- * - Attaches decoded payload to req.user
8
- * - Returns 401 if missing/invalid/expired
5
+ * Express middleware that enforces authentication using Seamless Auth cookies.
6
+ *
7
+ * This guard verifies the signed access cookie generated by the Seamless Auth
8
+ * server. If the access cookie is valid and unexpired, the decoded session
9
+ * payload is attached to `req.user` and the request proceeds.
10
+ *
11
+ * If the access cookie is expired or missing *but* a valid refresh cookie is
12
+ * present, the middleware automatically attempts a silent token refresh using
13
+ * the Seamless Auth server. When successful, new session cookies are issued and
14
+ * the request continues with an updated `req.user`.
15
+ *
16
+ * If neither the access token nor refresh token can validate the session,
17
+ * the middleware returns a 401 Unauthorized error and prevents further
18
+ * route execution.
19
+ *
20
+ * ### Responsibilities
21
+ * - Validates the Seamless Auth session access cookie
22
+ * - Attempts refresh-token–based session renewal when necessary
23
+ * - Populates `req.user` with the verified session payload
24
+ * - Handles all cookie rewriting during refresh flows
25
+ * - Acts as a request-level authentication guard for API routes
26
+ *
27
+ * ### Cookie Parameters
28
+ * - **cookieName** — Name of the access cookie that holds the signed session JWT
29
+ * - **refreshCookieName** — Name of the refresh cookie used for silent token refresh
30
+ * - **cookieDomain** — Domain or path value applied to issued cookies
31
+ *
32
+ * ### Example
33
+ * ```ts
34
+ * // Protect a route
35
+ * app.get("/api/me", requireAuth(), (req, res) => {
36
+ * res.json({ user: req.user });
37
+ * });
38
+ *
39
+ * // Custom cookie names (if your Seamless Auth server uses overrides)
40
+ * app.use(
41
+ * "/internal",
42
+ * requireAuth("sa_access", "sa_refresh", "mycompany.com"),
43
+ * internalRouter
44
+ * );
45
+ * ```
46
+ *
47
+ * @param cookieName - The access cookie name. Defaults to `"seamless-access"`.
48
+ * @param refreshCookieName - The refresh cookie name used for session rotation. Defaults to `"seamless-refresh"`.
49
+ * @param cookieDomain - Domain or path used when rewriting cookies. Defaults to `"/"`.
50
+ *
51
+ * @returns An Express middleware function that enforces Seamless Auth
52
+ * authentication on incoming requests.
9
53
  */
10
- export function requireAuth(cookieName = "seamless-auth-access", refreshCookieName = "seamless-auth-refresh", cookieDomain = "/") {
54
+ export function requireAuth(cookieName = "seamless-access", refreshCookieName = "seamless-refresh", cookieDomain = "/") {
11
55
  const COOKIE_SECRET = process.env.SEAMLESS_COOKIE_SIGNING_KEY;
12
56
  if (!COOKIE_SECRET) {
13
57
  console.warn("[SeamlessAuth] SEAMLESS_COOKIE_SIGNING_KEY missing — requireAuth will always fail.");
14
58
  throw new Error("Missing required env SEAMLESS_COOKIE_SIGNING_KEY");
15
59
  }
16
- const AUTH_SERVER_URL = process.env.AUTH_SERVER;
60
+ const AUTH_SERVER_URL = process.env.AUTH_SERVER_URL;
17
61
  return async (req, res, next) => {
18
62
  try {
19
63
  if (!COOKIE_SECRET) {
@@ -1,9 +1,49 @@
1
1
  import { RequestHandler } from "express";
2
2
  /**
3
- * Express middleware to enforce a required role from Seamless Auth cookie JWT.
3
+ * Express middleware that enforces role-based authorization for Seamless Auth sessions.
4
4
  *
5
- * @param role Role name to require (e.g. 'admin')
6
- * @param cookieName Cookie name containing JWT (default: 'sa_session')
5
+ * This guard assumes that `requireAuth()` has already validated the request
6
+ * and populated `req.user` with the decoded Seamless Auth session payload.
7
+ * It then checks whether the user’s roles include the required role (or any
8
+ * of several, when an array is provided).
9
+ *
10
+ * If the user possesses the required authorization, the request proceeds.
11
+ * Otherwise, the middleware responds with a 403 Forbidden error.
12
+ *
13
+ * ### Responsibilities
14
+ * - Validates that `req.user` is present (enforced upstream by `requireAuth`)
15
+ * - Ensures the authenticated user includes the specified role(s)
16
+ * - Blocks unauthorized access with a standardized JSON 403 response
17
+ *
18
+ * ### Parameters
19
+ * - **requiredRole** — A role (string) or list of roles the user must have.
20
+ * If an array is provided, *any* matching role grants access.
21
+ * - **cookieName** — Optional name of the access cookie to inspect.
22
+ * Defaults to `"seamless-access"`, but typically not needed because
23
+ * `requireAuth` is expected to run first.
24
+ *
25
+ * ### Example
26
+ * ```ts
27
+ * // Require a single role
28
+ * app.get("/admin/users",
29
+ * requireAuth(),
30
+ * requireRole("admin"),
31
+ * (req, res) => {
32
+ * res.send("Welcome admin!");
33
+ * }
34
+ * );
35
+ *
36
+ * // Allow any of multiple roles
37
+ * app.post("/settings",
38
+ * requireAuth(),
39
+ * requireRole(["admin", "supervisor"]),
40
+ * updateSettingsHandler
41
+ * );
42
+ * ```
43
+ *
44
+ * @param requiredRole - A role or list of roles required to access the route.
45
+ * @param cookieName - Optional access cookie name (defaults to `seamless-access`).
46
+ * @returns An Express middleware function enforcing role-based access control.
7
47
  */
8
48
  export declare function requireRole(role: string, cookieName?: string): RequestHandler;
9
49
  //# sourceMappingURL=requireRole.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"requireRole.d.ts","sourceRoot":"","sources":["../../src/middleware/requireRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAG1E;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,SAAoB,GAC7B,cAAc,CAiChB"}
1
+ {"version":3,"file":"requireRole.d.ts","sourceRoot":"","sources":["../../src/middleware/requireRole.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAG1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,SAAoB,GAC7B,cAAc,CAiChB"}
@@ -1,9 +1,49 @@
1
1
  import jwt from "jsonwebtoken";
2
2
  /**
3
- * Express middleware to enforce a required role from Seamless Auth cookie JWT.
3
+ * Express middleware that enforces role-based authorization for Seamless Auth sessions.
4
4
  *
5
- * @param role Role name to require (e.g. 'admin')
6
- * @param cookieName Cookie name containing JWT (default: 'sa_session')
5
+ * This guard assumes that `requireAuth()` has already validated the request
6
+ * and populated `req.user` with the decoded Seamless Auth session payload.
7
+ * It then checks whether the user’s roles include the required role (or any
8
+ * of several, when an array is provided).
9
+ *
10
+ * If the user possesses the required authorization, the request proceeds.
11
+ * Otherwise, the middleware responds with a 403 Forbidden error.
12
+ *
13
+ * ### Responsibilities
14
+ * - Validates that `req.user` is present (enforced upstream by `requireAuth`)
15
+ * - Ensures the authenticated user includes the specified role(s)
16
+ * - Blocks unauthorized access with a standardized JSON 403 response
17
+ *
18
+ * ### Parameters
19
+ * - **requiredRole** — A role (string) or list of roles the user must have.
20
+ * If an array is provided, *any* matching role grants access.
21
+ * - **cookieName** — Optional name of the access cookie to inspect.
22
+ * Defaults to `"seamless-access"`, but typically not needed because
23
+ * `requireAuth` is expected to run first.
24
+ *
25
+ * ### Example
26
+ * ```ts
27
+ * // Require a single role
28
+ * app.get("/admin/users",
29
+ * requireAuth(),
30
+ * requireRole("admin"),
31
+ * (req, res) => {
32
+ * res.send("Welcome admin!");
33
+ * }
34
+ * );
35
+ *
36
+ * // Allow any of multiple roles
37
+ * app.post("/settings",
38
+ * requireAuth(),
39
+ * requireRole(["admin", "supervisor"]),
40
+ * updateSettingsHandler
41
+ * );
42
+ * ```
43
+ *
44
+ * @param requiredRole - A role or list of roles required to access the route.
45
+ * @param cookieName - Optional access cookie name (defaults to `seamless-access`).
46
+ * @returns An Express middleware function enforcing role-based access control.
7
47
  */
8
48
  export function requireRole(role, cookieName = "seamless-access") {
9
49
  return (req, res, next) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamless-auth/express",
3
- "version": "0.0.1",
3
+ "version": "0.0.2-beta.1",
4
4
  "description": "Express adapter for Seamless Auth passwordless authentication",
5
5
  "license": "MIT",
6
6
  "type": "module",