@boopkit/cap-users 0.6.0 → 0.8.0
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 +14 -0
- package/capability.json +19 -0
- package/dist/auth-middleware.d.ts +96 -2
- package/dist/auth-middleware.d.ts.map +1 -1
- package/dist/auth-middleware.js +131 -63
- package/dist/auth-middleware.js.map +1 -1
- package/dist/auth-service.d.ts +8 -0
- package/dist/auth-service.d.ts.map +1 -1
- package/dist/auth-service.js +93 -46
- package/dist/auth-service.js.map +1 -1
- package/dist/credential-migration.d.ts +76 -0
- package/dist/credential-migration.d.ts.map +1 -0
- package/dist/credential-migration.js +126 -0
- package/dist/credential-migration.js.map +1 -0
- package/dist/identity-service.d.ts +49 -0
- package/dist/identity-service.d.ts.map +1 -1
- package/dist/identity-service.js +34 -0
- package/dist/identity-service.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/info-schema-tags.d.ts +21 -0
- package/dist/info-schema-tags.d.ts.map +1 -0
- package/dist/info-schema-tags.js +24 -0
- package/dist/info-schema-tags.js.map +1 -0
- package/dist/team-invitation-service.d.ts.map +1 -1
- package/dist/team-invitation-service.js +42 -8
- package/dist/team-invitation-service.js.map +1 -1
- package/package.json +1 -1
- package/scaffold/reference/user-auth.ref.md +48 -4
- package/scaffold/src/scripts/migrate-credentials.ts +69 -0
- package/scaffold/src/types/express.ts +11 -1
package/README.md
CHANGED
|
@@ -132,6 +132,20 @@ router.get('/integrations', requireApiKey(), api.integrations); // an API key, n
|
|
|
132
132
|
|
|
133
133
|
`requireApiKey` accepts the request only when `resolveBearer` resolved it from a key (`req.authStrategy === 'api-key'`), then runs the same `isAuthorized` grant check `requireAuth` does — so an expired per-key grant is refused here even though the key still resolves.
|
|
134
134
|
|
|
135
|
+
**A bearer door of your own.** `resolveBearer` is the API-key instantiation of an exported factory, `createBearerMiddleware(deps, { resolve, strategy, challenge })`. A capability that issues its own kind of token — OAuth access tokens, say — supplies its own `resolve` callback and gets cap-users' bearer handling unchanged: the same header parsing, the same refusal semantics (401 with your challenge, never a fall-through to the session cookie), the same `req.user`/`req.sessionToken` fields downstream code already reads.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { createBearerMiddleware } from '@boopkit/cap-users';
|
|
139
|
+
|
|
140
|
+
const resolveOauth = createBearerMiddleware(deps, {
|
|
141
|
+
resolve: (token) => tokenService.resolve(token), // null if it does not resolve
|
|
142
|
+
strategy: 'oauth', // written to req.authStrategy
|
|
143
|
+
challenge: 'Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Give your path its **own** `strategy` label. `requireApiKey` gates on the `'api-key'` literal specifically, so reusing that label would walk your token through the one gate whose purpose is to demand an independently revocable API key. `req.authStrategy` is typed `string` for exactly this reason, while the gate's comparison stays on the literal.
|
|
148
|
+
|
|
135
149
|
Issue keys with `authService.createApiKey(userId)`, which returns the raw key once. Only `hashToken(apiKey)` is stored in `user_identities`, the same treatment a session cookie gets, so a read of that table hands nobody a working credential. Keys issued before this change were stored raw and will not resolve after it; reissue them.
|
|
136
150
|
|
|
137
151
|
There is deliberately no route, screen, or command for minting a key. Handing out a credential that acts as a user is not a generic operation — who may do it, for whom, whether it needs a second factor or an approval, and what the audit trail looks like are answers only your application has, and a default affordance would answer them for you on the sensitive side. Wire `createApiKey` behind whatever authorization you already trust, and tell the holder at the moment you show it that the key cannot be retrieved again.
|
package/capability.json
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"src/middleware/auth.ts",
|
|
26
26
|
"src/controllers/auth.ts",
|
|
27
27
|
"src/routes/auth.ts",
|
|
28
|
+
"src/scripts/migrate-credentials.ts",
|
|
28
29
|
"views/signup.hbs",
|
|
29
30
|
"views/login.hbs",
|
|
30
31
|
"views/partials/auth-nav.hbs",
|
|
@@ -34,6 +35,12 @@
|
|
|
34
35
|
"skills": [
|
|
35
36
|
"boop-users-user-data-modeling"
|
|
36
37
|
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"credentials:migrate": {
|
|
40
|
+
"cmd": "tsx src/scripts/migrate-credentials.ts",
|
|
41
|
+
"description": "Rehash cleartext credentials in property tables (report only; pass -- --run to apply). ONE-WAY — back up first."
|
|
42
|
+
}
|
|
43
|
+
},
|
|
37
44
|
"hooks": [
|
|
38
45
|
{
|
|
39
46
|
"phase": "imports",
|
|
@@ -107,6 +114,18 @@
|
|
|
107
114
|
"type": "prisma-generate",
|
|
108
115
|
"label": "Generate Prisma client",
|
|
109
116
|
"hint": "npm run db:generate"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"type": "probe",
|
|
120
|
+
"label": "No cleartext authorization tokens",
|
|
121
|
+
"query": "SELECT 1 FROM user_properties WHERE property = 'authorization' AND info_schema <> 'authorization-hashed-01' LIMIT 1",
|
|
122
|
+
"hint": "Some authorization grants predate credential hashing and may hold a raw session token or API key. Run the credential-hashing migration (see reference/user-auth.ref.md)."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"type": "probe",
|
|
126
|
+
"label": "No cleartext invitation tokens",
|
|
127
|
+
"query": "SELECT 1 FROM account_properties WHERE property = 'team_invitations' AND info_schema <> 'team-invitations-hashed-01' LIMIT 1",
|
|
128
|
+
"hint": "Some team invitations predate credential hashing and may hold a raw, redeemable token. Run the credential-hashing migration (see reference/user-auth.ref.md)."
|
|
110
129
|
}
|
|
111
130
|
]
|
|
112
131
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth middleware factories for Express.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* `createAuthMiddleware` returns the app's middleware bundle:
|
|
5
5
|
* - `resolveVisitor` — resolves req.user/sessionToken/visitorToken from auth_id/visitor_id
|
|
6
6
|
* cookies if present. Never creates a user or sets cookies. If no cookie resolves,
|
|
7
7
|
* req.user stays undefined so anonymous traffic does not mint user records.
|
|
@@ -11,9 +11,18 @@
|
|
|
11
11
|
* Mints a user + visitor identity + session token and sets cookies on first call.
|
|
12
12
|
* Idempotent: if resolveVisitor already populated req, returns the existing user.
|
|
13
13
|
* - `requireAuth` — gates routes by trust tier, redirects web / 401s API.
|
|
14
|
+
* - `requireApiKey` — gates routes that must be reached with an API key specifically.
|
|
14
15
|
* - `addAuthContext` — populates res.locals.auth for view templates.
|
|
16
|
+
*
|
|
17
|
+
* `createBearerMiddleware` is the standalone bearer resolver those share. It
|
|
18
|
+
* takes the lookup as an injected callback, so a capability that issues its own
|
|
19
|
+
* kind of token (oauth-server's access tokens) gets cap-users' bearer handling
|
|
20
|
+
* — header parsing, the refusal semantics, the fields written onto the request
|
|
21
|
+
* — without cap-users having to know what an OAuth token is. `resolveBearer` is
|
|
22
|
+
* that factory with `validateApiKey` injected, which is what keeps the two
|
|
23
|
+
* doors from drifting apart.
|
|
15
24
|
*/
|
|
16
|
-
import type { Request, Response, NextFunction } from 'express';
|
|
25
|
+
import type { Request, RequestHandler, Response, NextFunction } from 'express';
|
|
17
26
|
import type { AuthService } from './auth-service.js';
|
|
18
27
|
import type { IdentityService } from './identity-service.js';
|
|
19
28
|
import type { UserService } from './user-service.js';
|
|
@@ -45,6 +54,42 @@ export interface AuthMiddlewareOptions {
|
|
|
45
54
|
*/
|
|
46
55
|
bearerChallenge?: string;
|
|
47
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Options for `createBearerMiddleware` — the three things that differ between
|
|
59
|
+
* one kind of bearer credential and another. Everything else about resolving a
|
|
60
|
+
* `Authorization: Bearer` header is the same for all of them, which is why they
|
|
61
|
+
* share an implementation.
|
|
62
|
+
*/
|
|
63
|
+
export interface BearerMiddlewareOptions {
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a presented token to its principal, or answer that it does not
|
|
66
|
+
* resolve. This is the whole identity domain in one callback: the caller
|
|
67
|
+
* decides which table the token lives in, what verification it needs, and
|
|
68
|
+
* which per-user conditions apply to it.
|
|
69
|
+
*
|
|
70
|
+
* Return `null` (or throw `AuthError`) for "this credential is not good" —
|
|
71
|
+
* both produce the 401. Throw anything else for "I could not tell" — a
|
|
72
|
+
* database being unreachable is not a bad token, and reporting it as one
|
|
73
|
+
* tells a correct client to discard a valid credential.
|
|
74
|
+
*/
|
|
75
|
+
resolve: (token: string) => Promise<{
|
|
76
|
+
user: User<BaseUserInfo>;
|
|
77
|
+
} | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Written to `req.authStrategy` on success — the label a downstream gate uses
|
|
80
|
+
* to tell one kind of credential from another. Use `'api-key'` only for an
|
|
81
|
+
* actual API key: `requireApiKey` gates on that exact literal, and reusing it
|
|
82
|
+
* for another credential walks that credential through the one gate whose
|
|
83
|
+
* purpose is to demand a key.
|
|
84
|
+
*/
|
|
85
|
+
strategy: string;
|
|
86
|
+
/**
|
|
87
|
+
* The `WWW-Authenticate` value sent with the 401. A plain `'Bearer'` for an
|
|
88
|
+
* API key; a resource server that must point clients at its discovery
|
|
89
|
+
* document sends the fuller `Bearer resource_metadata="..."` form.
|
|
90
|
+
*/
|
|
91
|
+
challenge: string;
|
|
92
|
+
}
|
|
48
93
|
/** Shape of res.locals.auth populated by addAuthContext. */
|
|
49
94
|
export interface AuthLocals {
|
|
50
95
|
trustTier: TrustTier | null;
|
|
@@ -61,6 +106,55 @@ export interface AuthMiddleware {
|
|
|
61
106
|
requireApiKey: (requiredTrust?: TrustTier) => (req: Request, res: Response, next: NextFunction) => void;
|
|
62
107
|
addAuthContext: (req: Request, res: Response, next: NextFunction) => void;
|
|
63
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* createBearerMiddleware — resolve `Authorization: Bearer <token>` through a
|
|
111
|
+
* caller-supplied lookup.
|
|
112
|
+
*
|
|
113
|
+
* Three outcomes, and the middle one is the design decision:
|
|
114
|
+
*
|
|
115
|
+
* - No `Authorization` header, or a scheme other than Bearer: pass through
|
|
116
|
+
* with req.user untouched, so this can be mounted on a router that also
|
|
117
|
+
* serves anonymous traffic. Gating is `requireAuth`'s job, not this one's.
|
|
118
|
+
* - A Bearer token that does not resolve: **401**, not silent anonymity. A
|
|
119
|
+
* caller that presented a credential and got it wrong should be told; a
|
|
120
|
+
* broken client that looks like a logged-out one is a bad afternoon.
|
|
121
|
+
* - A Bearer token that resolves: req.user is set to the same principal the
|
|
122
|
+
* cookie path yields, so everything downstream — requireAuth, trust tiers,
|
|
123
|
+
* res.locals.auth — works without knowing which door the request came in.
|
|
124
|
+
* It also records two more fields the cookie path does not:
|
|
125
|
+
* - req.sessionToken = the presented raw token. requireAuth reads BOTH
|
|
126
|
+
* req.user and req.sessionToken and denies if either is missing, so
|
|
127
|
+
* setting only req.user would make the published `resolveBearer` +
|
|
128
|
+
* `requireAuth` pairing deny every bearer request. On the API-key path
|
|
129
|
+
* sessionToken is also the `identityKey` requireAuth hands isAuthorized:
|
|
130
|
+
* `setAuthorization` hashes the key on the way in and `isAuthorized`
|
|
131
|
+
* hashes the presented one before comparing, so recording the RAW key
|
|
132
|
+
* here is what makes that comparison line up, and routing it through
|
|
133
|
+
* isAuthorized restores the per-key grant check ("expire the grant").
|
|
134
|
+
* - req.authStrategy = `options.strategy`, so a gate that must accept only
|
|
135
|
+
* one kind of credential (requireApiKey) can tell them apart. req.user
|
|
136
|
+
* alone cannot: resolveVisitor may have populated it from the cookie.
|
|
137
|
+
*
|
|
138
|
+
* It deliberately does **not** fall back to the session cookie when the token
|
|
139
|
+
* is bad. One of the donor codebases the oauth work drew on did fall back on
|
|
140
|
+
* a shared mount, and specs/oauth-server.spec.md names that as a shape not to
|
|
141
|
+
* lift: it means a request carrying a revoked key can still succeed, on the
|
|
142
|
+
* strength of a cookie the caller was not presenting as its credential. It is
|
|
143
|
+
* also why two bearer middlewares reading the same header is safe here and was
|
|
144
|
+
* not there — both refuse rather than fall through.
|
|
145
|
+
*
|
|
146
|
+
* Only outcomes that mean "this credential is not good" become 401s: a `null`
|
|
147
|
+
* from the resolver, or an `AuthError` thrown by it. Anything else (the
|
|
148
|
+
* database being unreachable, say) goes to next(err) so it is not misreported
|
|
149
|
+
* to the client as an auth failure.
|
|
150
|
+
*
|
|
151
|
+
* `deps` is not read here — resolution is entirely the caller's `resolve`
|
|
152
|
+
* callback — but it is part of the signature so that this factory and
|
|
153
|
+
* `createAuthMiddleware` are wired from the same object, and so a later
|
|
154
|
+
* addition that does need the services is not a breaking change for the
|
|
155
|
+
* capabilities already calling it.
|
|
156
|
+
*/
|
|
157
|
+
export declare function createBearerMiddleware(deps: AuthMiddlewareDeps, options: BearerMiddlewareOptions): RequestHandler;
|
|
64
158
|
/**
|
|
65
159
|
* Create auth middleware functions wired to the auth, identity, and user services.
|
|
66
160
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-middleware.d.ts","sourceRoot":"","sources":["../src/auth-middleware.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth-middleware.d.ts","sourceRoot":"","sources":["../src/auth-middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/auth-middleware.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Auth middleware factories for Express.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* `createAuthMiddleware` returns the app's middleware bundle:
|
|
6
6
|
* - `resolveVisitor` — resolves req.user/sessionToken/visitorToken from auth_id/visitor_id
|
|
7
7
|
* cookies if present. Never creates a user or sets cookies. If no cookie resolves,
|
|
8
8
|
* req.user stays undefined so anonymous traffic does not mint user records.
|
|
@@ -12,9 +12,19 @@
|
|
|
12
12
|
* Mints a user + visitor identity + session token and sets cookies on first call.
|
|
13
13
|
* Idempotent: if resolveVisitor already populated req, returns the existing user.
|
|
14
14
|
* - `requireAuth` — gates routes by trust tier, redirects web / 401s API.
|
|
15
|
+
* - `requireApiKey` — gates routes that must be reached with an API key specifically.
|
|
15
16
|
* - `addAuthContext` — populates res.locals.auth for view templates.
|
|
17
|
+
*
|
|
18
|
+
* `createBearerMiddleware` is the standalone bearer resolver those share. It
|
|
19
|
+
* takes the lookup as an injected callback, so a capability that issues its own
|
|
20
|
+
* kind of token (oauth-server's access tokens) gets cap-users' bearer handling
|
|
21
|
+
* — header parsing, the refusal semantics, the fields written onto the request
|
|
22
|
+
* — without cap-users having to know what an OAuth token is. `resolveBearer` is
|
|
23
|
+
* that factory with `validateApiKey` injected, which is what keeps the two
|
|
24
|
+
* doors from drifting apart.
|
|
16
25
|
*/
|
|
17
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.createBearerMiddleware = createBearerMiddleware;
|
|
18
28
|
exports.createAuthMiddleware = createAuthMiddleware;
|
|
19
29
|
const logger_1 = require("@boopkit/logger");
|
|
20
30
|
const auth_service_js_1 = require("./auth-service.js");
|
|
@@ -22,6 +32,93 @@ const logger = (0, logger_1.createLogger)('users', 'auth');
|
|
|
22
32
|
// ---------- Constants ----------
|
|
23
33
|
const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;
|
|
24
34
|
const TEN_YEARS_MS = 10 * ONE_YEAR_MS;
|
|
35
|
+
// ---------- Bearer factory ----------
|
|
36
|
+
/**
|
|
37
|
+
* createBearerMiddleware — resolve `Authorization: Bearer <token>` through a
|
|
38
|
+
* caller-supplied lookup.
|
|
39
|
+
*
|
|
40
|
+
* Three outcomes, and the middle one is the design decision:
|
|
41
|
+
*
|
|
42
|
+
* - No `Authorization` header, or a scheme other than Bearer: pass through
|
|
43
|
+
* with req.user untouched, so this can be mounted on a router that also
|
|
44
|
+
* serves anonymous traffic. Gating is `requireAuth`'s job, not this one's.
|
|
45
|
+
* - A Bearer token that does not resolve: **401**, not silent anonymity. A
|
|
46
|
+
* caller that presented a credential and got it wrong should be told; a
|
|
47
|
+
* broken client that looks like a logged-out one is a bad afternoon.
|
|
48
|
+
* - A Bearer token that resolves: req.user is set to the same principal the
|
|
49
|
+
* cookie path yields, so everything downstream — requireAuth, trust tiers,
|
|
50
|
+
* res.locals.auth — works without knowing which door the request came in.
|
|
51
|
+
* It also records two more fields the cookie path does not:
|
|
52
|
+
* - req.sessionToken = the presented raw token. requireAuth reads BOTH
|
|
53
|
+
* req.user and req.sessionToken and denies if either is missing, so
|
|
54
|
+
* setting only req.user would make the published `resolveBearer` +
|
|
55
|
+
* `requireAuth` pairing deny every bearer request. On the API-key path
|
|
56
|
+
* sessionToken is also the `identityKey` requireAuth hands isAuthorized:
|
|
57
|
+
* `setAuthorization` hashes the key on the way in and `isAuthorized`
|
|
58
|
+
* hashes the presented one before comparing, so recording the RAW key
|
|
59
|
+
* here is what makes that comparison line up, and routing it through
|
|
60
|
+
* isAuthorized restores the per-key grant check ("expire the grant").
|
|
61
|
+
* - req.authStrategy = `options.strategy`, so a gate that must accept only
|
|
62
|
+
* one kind of credential (requireApiKey) can tell them apart. req.user
|
|
63
|
+
* alone cannot: resolveVisitor may have populated it from the cookie.
|
|
64
|
+
*
|
|
65
|
+
* It deliberately does **not** fall back to the session cookie when the token
|
|
66
|
+
* is bad. One of the donor codebases the oauth work drew on did fall back on
|
|
67
|
+
* a shared mount, and specs/oauth-server.spec.md names that as a shape not to
|
|
68
|
+
* lift: it means a request carrying a revoked key can still succeed, on the
|
|
69
|
+
* strength of a cookie the caller was not presenting as its credential. It is
|
|
70
|
+
* also why two bearer middlewares reading the same header is safe here and was
|
|
71
|
+
* not there — both refuse rather than fall through.
|
|
72
|
+
*
|
|
73
|
+
* Only outcomes that mean "this credential is not good" become 401s: a `null`
|
|
74
|
+
* from the resolver, or an `AuthError` thrown by it. Anything else (the
|
|
75
|
+
* database being unreachable, say) goes to next(err) so it is not misreported
|
|
76
|
+
* to the client as an auth failure.
|
|
77
|
+
*
|
|
78
|
+
* `deps` is not read here — resolution is entirely the caller's `resolve`
|
|
79
|
+
* callback — but it is part of the signature so that this factory and
|
|
80
|
+
* `createAuthMiddleware` are wired from the same object, and so a later
|
|
81
|
+
* addition that does need the services is not a breaking change for the
|
|
82
|
+
* capabilities already calling it.
|
|
83
|
+
*/
|
|
84
|
+
function createBearerMiddleware(deps, options) {
|
|
85
|
+
const { resolve, strategy, challenge } = options;
|
|
86
|
+
void deps;
|
|
87
|
+
return function resolveBearerToken(req, res, next) {
|
|
88
|
+
const header = req.headers?.authorization;
|
|
89
|
+
if (typeof header !== 'string')
|
|
90
|
+
return next();
|
|
91
|
+
const match = /^Bearer +(.+)$/i.exec(header.trim());
|
|
92
|
+
if (!match)
|
|
93
|
+
return next();
|
|
94
|
+
const token = match[1].trim();
|
|
95
|
+
if (token === '')
|
|
96
|
+
return next();
|
|
97
|
+
const refuse = () => {
|
|
98
|
+
logger.warn('bearer token refused', { path: req.path, strategy });
|
|
99
|
+
res.set('WWW-Authenticate', challenge);
|
|
100
|
+
res.status(401).json({ error: 'invalid_token' });
|
|
101
|
+
};
|
|
102
|
+
// Promise.resolve().then wraps a resolver that throws synchronously, so a
|
|
103
|
+
// buggy caller cannot take the process down instead of reaching next(err).
|
|
104
|
+
Promise.resolve()
|
|
105
|
+
.then(() => resolve(token))
|
|
106
|
+
.then((resolved) => {
|
|
107
|
+
if (!resolved)
|
|
108
|
+
return refuse();
|
|
109
|
+
const r = req;
|
|
110
|
+
r.user = resolved.user;
|
|
111
|
+
r.sessionToken = token;
|
|
112
|
+
r.authStrategy = strategy;
|
|
113
|
+
next();
|
|
114
|
+
})
|
|
115
|
+
.catch((err) => {
|
|
116
|
+
if (!(err instanceof auth_service_js_1.AuthError))
|
|
117
|
+
return next(err);
|
|
118
|
+
refuse();
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
}
|
|
25
122
|
// ---------- Factory ----------
|
|
26
123
|
/**
|
|
27
124
|
* Create auth middleware functions wired to the auth, identity, and user services.
|
|
@@ -45,73 +142,38 @@ function createAuthMiddleware(deps, options) {
|
|
|
45
142
|
resolveFromCookies(req).then(() => next()).catch(next);
|
|
46
143
|
}
|
|
47
144
|
/**
|
|
48
|
-
* resolveBearer —
|
|
145
|
+
* resolveBearer — the API-key instantiation of `createBearerMiddleware`.
|
|
49
146
|
*
|
|
50
|
-
*
|
|
147
|
+
* This is not a convenience wrapper around the general factory; it is the
|
|
148
|
+
* whole of the API-key bearer path. The spec's non-negotiable constraint for
|
|
149
|
+
* the OAuth widening was that the API-key path keep behaving exactly as it
|
|
150
|
+
* did, and building it out of the same factory the OAuth path uses makes that
|
|
151
|
+
* true by construction rather than by inspection: there is one bearer
|
|
152
|
+
* implementation, and every existing API-key test exercises it. Two parallel
|
|
153
|
+
* implementations that are supposed to agree is the arrangement that drifts.
|
|
51
154
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* serves anonymous traffic. Gating is `requireAuth`'s job, not this one's.
|
|
55
|
-
* - A Bearer token that does not resolve: **401**, not silent anonymity. A
|
|
56
|
-
* caller that presented a credential and got it wrong should be told; a
|
|
57
|
-
* broken client that looks like a logged-out one is a bad afternoon.
|
|
58
|
-
* - A Bearer token that resolves: req.user is set to the same principal the
|
|
59
|
-
* cookie path yields, so everything downstream — requireAuth, trust tiers,
|
|
60
|
-
* res.locals.auth — works without knowing which door the request came in.
|
|
61
|
-
* It also records two more fields the cookie path does not:
|
|
62
|
-
* - req.sessionToken = the presented raw key. requireAuth reads BOTH
|
|
63
|
-
* req.user and req.sessionToken and denies if either is missing, so
|
|
64
|
-
* setting only req.user would make the published `resolveBearer` +
|
|
65
|
-
* `requireAuth` pairing deny every bearer request. sessionToken is also
|
|
66
|
-
* the `identityKey` requireAuth hands isAuthorized, and createApiKey
|
|
67
|
-
* records the per-key grant under the RAW key — so the presented key is
|
|
68
|
-
* exactly what that call is asking for, and routing it through
|
|
69
|
-
* isAuthorized restores the per-key grant check ("expire the grant") for
|
|
70
|
-
* free. COUPLING: this relies on createApiKey keying the grant on the
|
|
71
|
-
* raw key (auth-service.ts, `setAuthorization(userId, apiKey, ...)`).
|
|
72
|
-
* boop-owbv proposes hashing that field; whichever of the two lands
|
|
73
|
-
* second MUST update the other or API-key authorization breaks silently.
|
|
74
|
-
* - req.authStrategy = 'api-key', so a gate that must accept ONLY a key
|
|
75
|
-
* (requireApiKey) can tell a key principal from a cookie one. req.user
|
|
76
|
-
* alone cannot: resolveVisitor may have populated it from the cookie.
|
|
155
|
+
* The three injected pieces are the whole of what makes this path the
|
|
156
|
+
* API-key one:
|
|
77
157
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
158
|
+
* - `validateApiKey` as the resolver. It does four things an OAuth token
|
|
159
|
+
* would fail three of — the hardcoded `'api-key'` identity domain, a bcrypt
|
|
160
|
+
* check against the user's `credentials` property, an
|
|
161
|
+
* `isMethodAccepted(userId, 'api-key')` check against their `authentication`
|
|
162
|
+
* property, and the lookup itself — which is exactly why the seam is a
|
|
163
|
+
* callback rather than a `domain` option. It signals a bad key by throwing
|
|
164
|
+
* `AuthError`, which the factory treats as a refusal.
|
|
165
|
+
* - `'api-key'` as the strategy label, the literal `requireApiKey` gates on.
|
|
166
|
+
* - `bearerChallenge` as the `WWW-Authenticate` value, so an app can still
|
|
167
|
+
* override the challenge through `createAuthMiddleware`'s options.
|
|
83
168
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* misreported to the client as an auth failure.
|
|
169
|
+
* See `createBearerMiddleware` above for the resolution semantics and for why
|
|
170
|
+
* a bad token refuses rather than falling through to the session cookie.
|
|
87
171
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!match)
|
|
94
|
-
return next();
|
|
95
|
-
const token = match[1].trim();
|
|
96
|
-
if (token === '')
|
|
97
|
-
return next();
|
|
98
|
-
authService
|
|
99
|
-
.validateApiKey(token)
|
|
100
|
-
.then((user) => {
|
|
101
|
-
const r = req;
|
|
102
|
-
r.user = user;
|
|
103
|
-
r.sessionToken = token;
|
|
104
|
-
r.authStrategy = 'api-key';
|
|
105
|
-
next();
|
|
106
|
-
})
|
|
107
|
-
.catch((err) => {
|
|
108
|
-
if (!(err instanceof auth_service_js_1.AuthError))
|
|
109
|
-
return next(err);
|
|
110
|
-
logger.warn('bearer token refused', { path: req.path });
|
|
111
|
-
res.set('WWW-Authenticate', bearerChallenge);
|
|
112
|
-
res.status(401).json({ error: 'invalid_token' });
|
|
113
|
-
});
|
|
114
|
-
}
|
|
172
|
+
const resolveBearer = createBearerMiddleware(deps, {
|
|
173
|
+
resolve: (token) => authService.validateApiKey(token).then((user) => ({ user })),
|
|
174
|
+
strategy: 'api-key',
|
|
175
|
+
challenge: bearerChallenge,
|
|
176
|
+
});
|
|
115
177
|
async function resolveFromCookies(req) {
|
|
116
178
|
let user;
|
|
117
179
|
let sessionToken;
|
|
@@ -221,6 +283,12 @@ function createAuthMiddleware(deps, options) {
|
|
|
221
283
|
const user = req.user;
|
|
222
284
|
const sessionToken = req.sessionToken;
|
|
223
285
|
const authStrategy = req.authStrategy;
|
|
286
|
+
// The comparison stays on the 'api-key' LITERAL even though the field's
|
|
287
|
+
// declared type widened to string. That is the point of the widening:
|
|
288
|
+
// another capability may label its own bearer path 'oauth', and this gate
|
|
289
|
+
// exists to demand an independently revocable API key specifically, so an
|
|
290
|
+
// OAuth access token has to fail here by construction rather than by
|
|
291
|
+
// somebody remembering to check.
|
|
224
292
|
if (!user || !sessionToken || authStrategy !== 'api-key') {
|
|
225
293
|
res.set('WWW-Authenticate', bearerChallenge).status(401).json({ error: 'Unauthorized' });
|
|
226
294
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-middleware.js","sourceRoot":"","sources":["../src/auth-middleware.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"auth-middleware.js","sourceRoot":"","sources":["../src/auth-middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/auth-service.d.ts
CHANGED
|
@@ -104,6 +104,14 @@ export declare function generateToken(partition?: string): string;
|
|
|
104
104
|
* so a fast hash is sufficient (no need for bcrypt).
|
|
105
105
|
*/
|
|
106
106
|
export declare function hashToken(token: string): string;
|
|
107
|
+
/**
|
|
108
|
+
* True when `value` is already a SHA-256 hex digest, so hashing it again would
|
|
109
|
+
* double-hash. Session tokens (dashed base36, ~29 chars) and API keys (43
|
|
110
|
+
* base64url chars) never match this shape, so it distinguishes a raw credential
|
|
111
|
+
* from its stored hash by value shape alone. This is the idempotency test the
|
|
112
|
+
* migration tool and the auth service share.
|
|
113
|
+
*/
|
|
114
|
+
export declare function isHashedToken(value: string): boolean;
|
|
107
115
|
/**
|
|
108
116
|
* Create an auth business service orchestrating signup, login, and
|
|
109
117
|
* session management on top of user data services.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../src/auth-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../src/auth-service.ts"],"names":[],"mappings":"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"}
|