@seamless-auth/express 0.0.2-beta.8 → 0.0.2-beta.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
2
- import { JwtPayload } from 'jsonwebtoken';
3
2
 
4
3
  interface SeamlessAuthServerOptions {
5
4
  authServerUrl: string;
@@ -9,7 +8,6 @@ interface SeamlessAuthServerOptions {
9
8
  refreshCookieName?: string;
10
9
  preAuthCookieName?: string;
11
10
  }
12
-
13
11
  /**
14
12
  * Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
15
13
  *
@@ -56,6 +54,10 @@ interface SeamlessAuthServerOptions {
56
54
  */
57
55
  declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Router;
58
56
 
57
+ interface RequireAuthOptions {
58
+ cookieName?: string;
59
+ cookieSecret: string;
60
+ }
59
61
  /**
60
62
  * Express middleware that enforces authentication using Seamless Auth cookies.
61
63
  *
@@ -106,9 +108,6 @@ declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Rout
106
108
  * @returns An Express middleware function that enforces Seamless Auth
107
109
  * authentication on incoming requests.
108
110
  */
109
- interface AuthenticatedRequest extends Request {
110
- user?: JwtPayload;
111
- }
112
111
  interface RequireAuthOptions {
113
112
  cookieName?: string;
114
113
  cookieSecret: string;
@@ -121,32 +120,22 @@ interface RequireAuthOptions {
121
120
  * - This middleware does NOT attempt token refresh.
122
121
  * - Refresh is handled upstream by ensureCookies().
123
122
  */
124
- declare function requireAuth(opts: RequireAuthOptions): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
123
+ declare function requireAuth(opts: RequireAuthOptions): (req: Request, res: Response, next: NextFunction) => void;
125
124
 
126
125
  /**
127
- * Express middleware that enforces role-based authorization for Seamless Auth sessions.
128
- *
129
- * This guard assumes that `requireAuth()` has already validated the request
130
- * and populated `req.user` with the decoded Seamless Auth session payload.
131
- * It then checks whether the user’s roles include the required role (or any
132
- * of several, when an array is provided).
126
+ * Express middleware that enforces role-based authorization for Seamless Auth.
133
127
  *
134
- * If the user possesses the required authorization, the request proceeds.
135
- * Otherwise, the middleware responds with a 403 Forbidden error.
128
+ * This middleware assumes `requireAuth()` has already:
129
+ * - authenticated the request
130
+ * - populated `req.user` with the authenticated session payload
136
131
  *
137
- * ### Responsibilities
138
- * - Validates that `req.user` is present (enforced upstream by `requireAuth`)
139
- * - Ensures the authenticated user includes the specified role(s)
140
- * - Blocks unauthorized access with a standardized JSON 403 response
132
+ * `requireRole` performs **authorization only**. It does not inspect cookies,
133
+ * verify tokens, or read environment variables.
141
134
  *
142
- * ### Parameters
143
- * - **requiredRole** A role (string) or list of roles the user must have.
144
- * If an array is provided, *any* matching role grants access.
145
- * - **cookieName** — Optional name of the access cookie to inspect.
146
- * Defaults to `"seamless-access"`, but typically not needed because
147
- * `requireAuth` is expected to run first.
135
+ * If any of the required roles are present on the user, access is granted.
136
+ * Otherwise, a 403 Forbidden response is returned.
148
137
  *
149
- * ### Example
138
+ * * ### Example
150
139
  * ```ts
151
140
  * // Require a single role
152
141
  * app.get("/admin/users",
@@ -163,13 +152,10 @@ declare function requireAuth(opts: RequireAuthOptions): (req: AuthenticatedReque
163
152
  * requireRole(["admin", "supervisor"]),
164
153
  * updateSettingsHandler
165
154
  * );
166
- * ```
167
155
  *
168
- * @param requiredRole - A role or list of roles required to access the route.
169
- * @param cookieName - Optional access cookie name (defaults to `seamless-access`).
170
- * @returns An Express middleware function enforcing role-based access control.
156
+ * @param requiredRoles - A role or list of roles required to access the route
171
157
  */
172
- declare function requireRole(role: string, cookieName?: string): RequestHandler;
158
+ declare function requireRole(requiredRoles: string | string[]): RequestHandler;
173
159
 
174
160
  interface EnsureCookiesMiddlewareOptions {
175
161
  authServerUrl: string;
@@ -184,10 +170,8 @@ interface EnsureCookiesMiddlewareOptions {
184
170
  audience: string;
185
171
  keyId: string;
186
172
  }
187
- declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request & {
188
- cookiePayload?: any;
189
- }, res: Response, next: NextFunction) => Promise<void>;
173
+ declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
190
174
 
191
- declare function getSeamlessUser<T = any>(req: Request, authServerUrl: string, cookieName?: string): Promise<T | null>;
175
+ declare function getSeamlessUser(req: Request, authServerUrl: string, cookieName?: string): Promise<any>;
192
176
 
193
- export { type SeamlessAuthServerOptions, createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
177
+ export { createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
package/dist/index.js CHANGED
@@ -139,11 +139,11 @@ import { finishLoginHandler } from "@seamless-auth/core/handlers/finishLogin";
139
139
  // src/internal/buildAuthorization.ts
140
140
  import { createServiceToken } from "@seamless-auth/core";
141
141
  function buildServiceAuthorization(req) {
142
- if (!req.cookiePayload?.sub) {
142
+ if (!req.cookiePayload?.sub && !req.user.sub) {
143
143
  return void 0;
144
144
  }
145
145
  const token = createServiceToken({
146
- subject: req.cookiePayload.sub,
146
+ subject: req.cookiePayload?.sub || req.user.sub,
147
147
  issuer: process.env.APP_ORIGIN,
148
148
  audience: process.env.AUTH_SERVER_URL,
149
149
  serviceSecret: process.env.API_SERVICE_TOKEN,
@@ -432,58 +432,72 @@ function requireAuth(opts) {
432
432
  return function(req, res, next) {
433
433
  const token = req.cookies?.[cookieName];
434
434
  if (!token) {
435
- res.status(401).json({ error: "Missing access cookie" });
435
+ res.status(401).json({
436
+ error: "Authentication required"
437
+ });
436
438
  return;
437
439
  }
438
440
  const payload = verifyCookieJwt(token, cookieSecret);
439
- if (!payload) {
440
- res.status(401).json({ error: "Invalid or expired access cookie" });
441
+ if (!payload || !payload.sub) {
442
+ res.status(401).json({
443
+ error: "Invalid or expired session"
444
+ });
441
445
  return;
442
446
  }
443
- req.user = payload;
447
+ const user = {
448
+ sub: payload.sub,
449
+ roles: Array.isArray(payload.roles) ? payload.roles : [],
450
+ email: payload.email,
451
+ phone: payload.phone,
452
+ iat: payload.iat,
453
+ exp: payload.exp
454
+ };
455
+ req.user = user;
444
456
  next();
445
457
  };
446
458
  }
447
459
 
448
460
  // src/middleware/requireRole.ts
449
- import jwt2 from "jsonwebtoken";
450
- function requireRole(role, cookieName = "seamless-access") {
461
+ function requireRole(requiredRoles) {
462
+ const roles = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
451
463
  return (req, res, next) => {
452
- try {
453
- const COOKIE_SECRET = process.env.COOKIE_SIGNING_KEY;
454
- if (!COOKIE_SECRET) {
455
- console.warn(
456
- "[SeamlessAuth] COOKIE_SIGNING_KEY missing \u2014 requireRole will always fail."
457
- );
458
- throw new Error("Missing required env COOKIE_SIGNING_KEY");
459
- }
460
- const token = req.cookies?.[cookieName];
461
- if (!token) {
462
- res.status(401).json({ error: "Missing access cookie" });
463
- return;
464
- }
465
- const payload = jwt2.verify(token, COOKIE_SECRET, {
466
- algorithms: ["HS256"]
464
+ const user = req.user;
465
+ if (!user) {
466
+ res.status(401).json({
467
+ error: "Authentication required"
467
468
  });
468
- if (!payload.roles?.includes(role)) {
469
- res.status(403).json({ error: `Forbidden: ${role} role required` });
470
- return;
471
- }
472
- next();
473
- } catch (err) {
474
- console.error(`[RequireRole] requireRole(${role}) failed:`, err.message);
475
- res.status(401).json({ error: "Invalid or expired access cookie" });
469
+ return;
470
+ }
471
+ if (!Array.isArray(user.roles)) {
472
+ res.status(403).json({
473
+ error: "User has no roles assigned"
474
+ });
475
+ return;
476
476
  }
477
+ const hasRole = roles.some((role) => user.roles.includes(role));
478
+ if (!hasRole) {
479
+ res.status(403).json({
480
+ error: "Insufficient role",
481
+ required: roles,
482
+ actual: user.roles
483
+ });
484
+ return;
485
+ }
486
+ next();
477
487
  };
478
488
  }
479
489
 
480
490
  // src/getSeamlessUser.ts
481
- import { getSeamlessUser as getSeamlessUserCore } from "@seamless-auth/core";
491
+ import {
492
+ getSeamlessUser as getSeamlessUserCore
493
+ } from "@seamless-auth/core";
482
494
  async function getSeamlessUser(req, authServerUrl, cookieName = "seamless-access") {
495
+ const authorization = buildServiceAuthorization(req);
483
496
  return getSeamlessUserCore(req.cookies ?? {}, {
484
497
  authServerUrl,
485
498
  cookieSecret: process.env.COOKIE_SIGNING_KEY,
486
- cookieName
499
+ cookieName,
500
+ authorization
487
501
  });
488
502
  }
489
503
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamless-auth/express",
3
- "version": "0.0.2-beta.8",
3
+ "version": "0.0.2-beta.9",
4
4
  "description": "Express adapter for Seamless Auth passwordless authentication",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "@types/express": ">=4.17.0"
38
38
  },
39
39
  "dependencies": {
40
- "@seamless-auth/core": "^0.1.0",
40
+ "@seamless-auth/core": "beta",
41
41
  "cookie-parser": "^1.4.6",
42
42
  "jsonwebtoken": "^9.0.3"
43
43
  },