@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 +19 -35
- package/dist/index.js +47 -33
- package/package.json +2 -2
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:
|
|
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
|
|
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
|
-
*
|
|
135
|
-
*
|
|
128
|
+
* This middleware assumes `requireAuth()` has already:
|
|
129
|
+
* - authenticated the request
|
|
130
|
+
* - populated `req.user` with the authenticated session payload
|
|
136
131
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
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
|
-
*
|
|
143
|
-
*
|
|
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
|
|
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(
|
|
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
|
|
175
|
+
declare function getSeamlessUser(req: Request, authServerUrl: string, cookieName?: string): Promise<any>;
|
|
192
176
|
|
|
193
|
-
export {
|
|
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({
|
|
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({
|
|
441
|
+
if (!payload || !payload.sub) {
|
|
442
|
+
res.status(401).json({
|
|
443
|
+
error: "Invalid or expired session"
|
|
444
|
+
});
|
|
441
445
|
return;
|
|
442
446
|
}
|
|
443
|
-
|
|
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
|
-
|
|
450
|
-
|
|
461
|
+
function requireRole(requiredRoles) {
|
|
462
|
+
const roles = Array.isArray(requiredRoles) ? requiredRoles : [requiredRoles];
|
|
451
463
|
return (req, res, next) => {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
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 {
|
|
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.
|
|
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": "
|
|
40
|
+
"@seamless-auth/core": "beta",
|
|
41
41
|
"cookie-parser": "^1.4.6",
|
|
42
42
|
"jsonwebtoken": "^9.0.3"
|
|
43
43
|
},
|