@boopkit/cap-oauth-server 0.1.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/capability.json +120 -0
- package/dist/authorization-code-service.d.ts +105 -0
- package/dist/authorization-code-service.d.ts.map +1 -0
- package/dist/authorization-code-service.js +142 -0
- package/dist/authorization-code-service.js.map +1 -0
- package/dist/bearer.d.ts +146 -0
- package/dist/bearer.d.ts.map +1 -0
- package/dist/bearer.js +150 -0
- package/dist/bearer.js.map +1 -0
- package/dist/body-field.d.ts +24 -0
- package/dist/body-field.d.ts.map +1 -0
- package/dist/body-field.js +43 -0
- package/dist/body-field.js.map +1 -0
- package/dist/client-registry-service.d.ts +120 -0
- package/dist/client-registry-service.d.ts.map +1 -0
- package/dist/client-registry-service.js +450 -0
- package/dist/client-registry-service.js.map +1 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +157 -0
- package/dist/config.js.map +1 -0
- package/dist/connections-controller.d.ts +106 -0
- package/dist/connections-controller.d.ts.map +1 -0
- package/dist/connections-controller.js +161 -0
- package/dist/connections-controller.js.map +1 -0
- package/dist/consent-controller.d.ts +197 -0
- package/dist/consent-controller.d.ts.map +1 -0
- package/dist/consent-controller.js +532 -0
- package/dist/consent-controller.js.map +1 -0
- package/dist/discovery-controller.d.ts +16 -0
- package/dist/discovery-controller.d.ts.map +1 -0
- package/dist/discovery-controller.js +52 -0
- package/dist/discovery-controller.js.map +1 -0
- package/dist/discovery.d.ts +95 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +101 -0
- package/dist/discovery.js.map +1 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/express.d.ts +66 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +3 -0
- package/dist/express.js.map +1 -0
- package/dist/grant-service.d.ts +102 -0
- package/dist/grant-service.d.ts.map +1 -0
- package/dist/grant-service.js +137 -0
- package/dist/grant-service.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata-document.d.ts +60 -0
- package/dist/metadata-document.d.ts.map +1 -0
- package/dist/metadata-document.js +148 -0
- package/dist/metadata-document.js.map +1 -0
- package/dist/oauth-error.d.ts +45 -0
- package/dist/oauth-error.d.ts.map +1 -0
- package/dist/oauth-error.js +51 -0
- package/dist/oauth-error.js.map +1 -0
- package/dist/pkce.d.ts +29 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +65 -0
- package/dist/pkce.js.map +1 -0
- package/dist/property-array.d.ts +60 -0
- package/dist/property-array.d.ts.map +1 -0
- package/dist/property-array.js +31 -0
- package/dist/property-array.js.map +1 -0
- package/dist/public-id.d.ts +35 -0
- package/dist/public-id.d.ts.map +1 -0
- package/dist/public-id.js +76 -0
- package/dist/public-id.js.map +1 -0
- package/dist/rate-limit.d.ts +56 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +55 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/redirect-uri.d.ts +89 -0
- package/dist/redirect-uri.d.ts.map +1 -0
- package/dist/redirect-uri.js +249 -0
- package/dist/redirect-uri.js.map +1 -0
- package/dist/registration-controller.d.ts +62 -0
- package/dist/registration-controller.d.ts.map +1 -0
- package/dist/registration-controller.js +79 -0
- package/dist/registration-controller.js.map +1 -0
- package/dist/request-client.d.ts +19 -0
- package/dist/request-client.d.ts.map +1 -0
- package/dist/request-client.js +32 -0
- package/dist/request-client.js.map +1 -0
- package/dist/revocation-controller.d.ts +28 -0
- package/dist/revocation-controller.d.ts.map +1 -0
- package/dist/revocation-controller.js +176 -0
- package/dist/revocation-controller.js.map +1 -0
- package/dist/revocation-service.d.ts +113 -0
- package/dist/revocation-service.d.ts.map +1 -0
- package/dist/revocation-service.js +121 -0
- package/dist/revocation-service.js.map +1 -0
- package/dist/token-controller.d.ts +79 -0
- package/dist/token-controller.d.ts.map +1 -0
- package/dist/token-controller.js +207 -0
- package/dist/token-controller.js.map +1 -0
- package/dist/token-service.d.ts +175 -0
- package/dist/token-service.d.ts.map +1 -0
- package/dist/token-service.js +251 -0
- package/dist/token-service.js.map +1 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/user-store.d.ts +110 -0
- package/dist/user-store.d.ts.map +1 -0
- package/dist/user-store.js +40 -0
- package/dist/user-store.js.map +1 -0
- package/package.json +37 -0
- package/scaffold/prisma/schema/oauth-clients.prisma +80 -0
- package/scaffold/reference/oauth-server.ref.md +478 -0
- package/scaffold/src/middleware/oauth-server.ts +227 -0
- package/scaffold/src/scripts/seed-oauth-clients.ts +72 -0
- package/scaffold/src/services/oauth-server.service.ts +101 -0
- package/scaffold/styles/oauth-server.css +345 -0
- package/scaffold/views/oauth-connected-clients.hbs +98 -0
- package/scaffold/views/oauth-consent-error.hbs +22 -0
- package/scaffold/views/oauth-consent.hbs +138 -0
- package/scaffold/views/oauth-disconnect-confirm.hbs +57 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MAX_REGISTERED_CLIENTS = exports.DEFAULT_ABANDONED_CLIENT_TTL_MS = exports.DEFAULT_METADATA_MAX_AGE_MS = exports.DEFAULT_RESPONSE_TYPES = exports.DEFAULT_GRANT_TYPES = void 0;
|
|
4
|
+
exports.validateRedirectUris = validateRedirectUris;
|
|
5
|
+
exports.createClientRegistryService = createClientRegistryService;
|
|
6
|
+
const errors_js_1 = require("./errors.js");
|
|
7
|
+
const metadata_document_js_1 = require("./metadata-document.js");
|
|
8
|
+
const public_id_js_1 = require("./public-id.js");
|
|
9
|
+
const redirect_uri_js_1 = require("./redirect-uri.js");
|
|
10
|
+
/**
|
|
11
|
+
* The client registry — who is asking, and what they are allowed to ask for.
|
|
12
|
+
*
|
|
13
|
+
* One table, one lookup path, three registration modes. A mode says where a
|
|
14
|
+
* client's *authoritative* metadata lives, and almost nothing else:
|
|
15
|
+
*
|
|
16
|
+
* `dynamic` open RFC 7591 registration; we own the metadata
|
|
17
|
+
* `metadata_document` the `client_id` is an https URL; the client owns it
|
|
18
|
+
* `static` an operator wrote the row; only an operator changes it
|
|
19
|
+
*
|
|
20
|
+
* ## What is not here
|
|
21
|
+
*
|
|
22
|
+
* Nothing user- or account-shaped. A client is software; the record tying a
|
|
23
|
+
* client to a *person* is the grant, which lives in cap-users' user properties
|
|
24
|
+
* and belongs to Slice 2. The registry never learns who consented.
|
|
25
|
+
*
|
|
26
|
+
* ## The abuse controls, and why they are fresh work
|
|
27
|
+
*
|
|
28
|
+
* Neither donor has an unauthenticated registration endpoint. Paywalls'
|
|
29
|
+
* `/oauth/register` requires a caller of user type `service-user` and creates a
|
|
30
|
+
* row owned by that caller's account; open registration has no caller and no
|
|
31
|
+
* account, so the gate that made paywalls' endpoint safe has nothing to map
|
|
32
|
+
* onto. What replaces it is here and in `createRegistrationRateLimiter`:
|
|
33
|
+
* `redirect_uris` required and validated at registration, a ceiling on total
|
|
34
|
+
* rows, per-IP rate limiting at the transport, and a prune for rows that never
|
|
35
|
+
* went anywhere.
|
|
36
|
+
*/
|
|
37
|
+
/** Sensible RFC 7591 defaults for a client that asked for nothing specific. */
|
|
38
|
+
exports.DEFAULT_GRANT_TYPES = ['authorization_code', 'refresh_token'];
|
|
39
|
+
exports.DEFAULT_RESPONSE_TYPES = ['code'];
|
|
40
|
+
/** 24 hours. A cached metadata document older than this is re-fetched lazily. */
|
|
41
|
+
exports.DEFAULT_METADATA_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
42
|
+
/** 30 days, per the plan: a dynamic row that never went anywhere is garbage. */
|
|
43
|
+
exports.DEFAULT_ABANDONED_CLIENT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
44
|
+
/** How many rows open registration may create before it starts refusing. */
|
|
45
|
+
exports.DEFAULT_MAX_REGISTERED_CLIENTS = 1000;
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Validation
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
const invalidMetadata = (message) => new errors_js_1.ClientRegistryError('invalid_client_metadata', message);
|
|
50
|
+
const invalidRedirect = (message) => new errors_js_1.ClientRegistryError('invalid_redirect_uri', message);
|
|
51
|
+
/**
|
|
52
|
+
* The redirect-URI rule at *registration*, which is the other half of
|
|
53
|
+
* `matchRedirectUri`. The matcher decides whether a presented URI satisfies a
|
|
54
|
+
* stored one; this decides whether a URI may be stored at all.
|
|
55
|
+
*
|
|
56
|
+
* Both halves read `classifyRedirectUri`, and that is the point: a URI this
|
|
57
|
+
* function stores is by construction a URI the matcher will compare. The three
|
|
58
|
+
* shapes it admits — claimed https, loopback http, and an application's own
|
|
59
|
+
* private-use scheme — and the grounds each is admitted or refused on live in
|
|
60
|
+
* `redirect-uri.ts` next to the matching rule they have to agree with.
|
|
61
|
+
*
|
|
62
|
+
* This is the umbrella spec's "declared-but-unenforced redirect URI lists"
|
|
63
|
+
* defect closed at the point of entry: a list that was never validated going in
|
|
64
|
+
* cannot be enforced coming out, whatever the matcher does.
|
|
65
|
+
*/
|
|
66
|
+
function validateRedirectUris(value) {
|
|
67
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
68
|
+
throw invalidRedirect('redirect_uris is required and must be a non-empty array');
|
|
69
|
+
}
|
|
70
|
+
if (value.length > 20) {
|
|
71
|
+
throw invalidRedirect('redirect_uris holds more entries than any real client needs');
|
|
72
|
+
}
|
|
73
|
+
const uris = [];
|
|
74
|
+
for (const entry of value) {
|
|
75
|
+
if (typeof entry !== 'string' || entry === '') {
|
|
76
|
+
throw invalidRedirect('every redirect_uris entry must be a non-empty string');
|
|
77
|
+
}
|
|
78
|
+
const verdict = (0, redirect_uri_js_1.classifyRedirectUri)(entry);
|
|
79
|
+
if (!verdict.accepted) {
|
|
80
|
+
throw invalidRedirect(`redirect_uris entry ${verdict.reason}: ${entry}`);
|
|
81
|
+
}
|
|
82
|
+
// The raw entry, not the parsed URL's serialization: matching is by exact
|
|
83
|
+
// string, so storing a normalized spelling would silently refuse the value
|
|
84
|
+
// the client will actually present.
|
|
85
|
+
uris.push(entry);
|
|
86
|
+
}
|
|
87
|
+
return uris;
|
|
88
|
+
}
|
|
89
|
+
function optionalString(value, field, maxLength = 2048) {
|
|
90
|
+
if (value === undefined || value === null || value === '')
|
|
91
|
+
return null;
|
|
92
|
+
if (typeof value !== 'string')
|
|
93
|
+
throw invalidMetadata(`${field} must be a string`);
|
|
94
|
+
if (value.length > maxLength)
|
|
95
|
+
throw invalidMetadata(`${field} is longer than ${maxLength}`);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function optionalStringArray(value, field) {
|
|
99
|
+
if (value === undefined || value === null)
|
|
100
|
+
return [];
|
|
101
|
+
if (!Array.isArray(value))
|
|
102
|
+
throw invalidMetadata(`${field} must be an array of strings`);
|
|
103
|
+
return value.map((entry) => {
|
|
104
|
+
if (typeof entry !== 'string')
|
|
105
|
+
throw invalidMetadata(`${field} must be an array of strings`);
|
|
106
|
+
return entry;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** RFC 6749 §3.3: `scope` on the wire is one space-delimited string. */
|
|
110
|
+
function parseScope(value, fallback) {
|
|
111
|
+
if (value === undefined || value === null || value === '')
|
|
112
|
+
return [...fallback];
|
|
113
|
+
if (typeof value !== 'string')
|
|
114
|
+
throw invalidMetadata('scope must be a space-delimited string');
|
|
115
|
+
const scopes = value.split(/\s+/).filter(Boolean);
|
|
116
|
+
return scopes.length > 0 ? scopes : [...fallback];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Only public clients, v1.
|
|
120
|
+
*
|
|
121
|
+
* Every client in the Claude ecosystem is public — a desktop app or a hosted
|
|
122
|
+
* connector that cannot keep a secret — and accepting a value we do not
|
|
123
|
+
* implement would advertise an authentication method the token endpoint will
|
|
124
|
+
* later refuse.
|
|
125
|
+
*/
|
|
126
|
+
function parseAuthMethod(value) {
|
|
127
|
+
if (value === undefined || value === null || value === '')
|
|
128
|
+
return 'none';
|
|
129
|
+
if (value !== 'none') {
|
|
130
|
+
throw invalidMetadata(`token_endpoint_auth_method must be "none"; this server issues only public clients`);
|
|
131
|
+
}
|
|
132
|
+
return 'none';
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Row mapping
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
const asStringArray = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
138
|
+
const asDate = (value) => value instanceof Date ? value : new Date(value);
|
|
139
|
+
function toClient(row) {
|
|
140
|
+
return {
|
|
141
|
+
id: row.id,
|
|
142
|
+
publicId: row.publicId,
|
|
143
|
+
clientId: row.clientId,
|
|
144
|
+
mode: row.mode,
|
|
145
|
+
status: row.status === 'suspended' ? 'suspended' : 'enabled',
|
|
146
|
+
name: row.name ?? null,
|
|
147
|
+
description: row.description ?? null,
|
|
148
|
+
logoUri: row.logoUri ?? null,
|
|
149
|
+
clientUri: row.clientUri ?? null,
|
|
150
|
+
contacts: asStringArray(row.contacts),
|
|
151
|
+
softwareId: row.softwareId ?? null,
|
|
152
|
+
softwareVersion: row.softwareVersion ?? null,
|
|
153
|
+
policyUri: row.policyUri ?? null,
|
|
154
|
+
tosUri: row.tosUri ?? null,
|
|
155
|
+
redirectUris: asStringArray(row.redirectUris),
|
|
156
|
+
grantTypes: asStringArray(row.grantTypes),
|
|
157
|
+
responseTypes: asStringArray(row.responseTypes),
|
|
158
|
+
scopes: asStringArray(row.scopes),
|
|
159
|
+
tokenEndpointAuthMethod: row.tokenEndpointAuthMethod ?? 'none',
|
|
160
|
+
metadataUri: row.metadataUri ?? null,
|
|
161
|
+
metadataFetchedAt: row.metadataFetchedAt == null ? null : asDate(row.metadataFetchedAt),
|
|
162
|
+
createdAt: asDate(row.createdAt),
|
|
163
|
+
updatedAt: asDate(row.updatedAt),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// The service
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
function createClientRegistryService(model, options = {}) {
|
|
170
|
+
const now = options.now ?? (() => new Date());
|
|
171
|
+
const defaultScopes = options.defaultScopes ?? [];
|
|
172
|
+
const maxRegisteredClients = options.maxRegisteredClients ?? exports.DEFAULT_MAX_REGISTERED_CLIENTS;
|
|
173
|
+
const metadataMaxAgeMs = options.metadataMaxAgeMs ?? exports.DEFAULT_METADATA_MAX_AGE_MS;
|
|
174
|
+
const fetchLimits = {
|
|
175
|
+
timeoutMs: options.metadataTimeoutMs ?? metadata_document_js_1.DEFAULT_METADATA_TIMEOUT_MS,
|
|
176
|
+
maxBytes: options.metadataMaxBytes ?? metadata_document_js_1.DEFAULT_METADATA_MAX_BYTES,
|
|
177
|
+
maxRedirects: options.metadataMaxRedirects ?? metadata_document_js_1.DEFAULT_METADATA_MAX_REDIRECTS,
|
|
178
|
+
};
|
|
179
|
+
const service = {
|
|
180
|
+
async registerDynamic(input) {
|
|
181
|
+
const redirectUris = validateRedirectUris(input.redirect_uris);
|
|
182
|
+
if (Number.isFinite(maxRegisteredClients)) {
|
|
183
|
+
const registered = await model.count({ where: { mode: 'dynamic' } });
|
|
184
|
+
if (registered >= maxRegisteredClients) {
|
|
185
|
+
throw new errors_js_1.ClientRegistryError('boop_registration_capacity', 'the client registry has reached its configured ceiling');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const publicId = (0, public_id_js_1.generateClientPublicId)();
|
|
189
|
+
const row = await model.create({
|
|
190
|
+
data: {
|
|
191
|
+
publicId,
|
|
192
|
+
// For a dynamic client the `client_id` *is* the `publicId`. That is
|
|
193
|
+
// what makes `<client_id>.<token>` and `<publicId>.<token>` the same
|
|
194
|
+
// string for two of the three modes, and why only CIMD needs the
|
|
195
|
+
// indirection at all.
|
|
196
|
+
clientId: publicId,
|
|
197
|
+
mode: 'dynamic',
|
|
198
|
+
status: 'enabled',
|
|
199
|
+
name: optionalString(input.client_name, 'client_name', 255),
|
|
200
|
+
description: null,
|
|
201
|
+
logoUri: optionalString(input.logo_uri, 'logo_uri'),
|
|
202
|
+
clientUri: optionalString(input.client_uri, 'client_uri'),
|
|
203
|
+
contacts: optionalStringArray(input.contacts, 'contacts'),
|
|
204
|
+
softwareId: optionalString(input.software_id, 'software_id', 255),
|
|
205
|
+
softwareVersion: optionalString(input.software_version, 'software_version', 255),
|
|
206
|
+
policyUri: optionalString(input.policy_uri, 'policy_uri'),
|
|
207
|
+
tosUri: optionalString(input.tos_uri, 'tos_uri'),
|
|
208
|
+
redirectUris,
|
|
209
|
+
grantTypes: normaliseGrantTypes(input.grant_types),
|
|
210
|
+
responseTypes: normaliseResponseTypes(input.response_types),
|
|
211
|
+
scopes: parseScope(input.scope, defaultScopes),
|
|
212
|
+
tokenEndpointAuthMethod: parseAuthMethod(input.token_endpoint_auth_method),
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
return toClient(row);
|
|
216
|
+
},
|
|
217
|
+
async resolveMetadataDocument(clientId) {
|
|
218
|
+
if (!(0, metadata_document_js_1.isValidMetadataUri)(clientId)) {
|
|
219
|
+
throw new errors_js_1.ClientRegistryError('boop_metadata_document_unusable', `client_id is not a usable https metadata document URL: ${clientId}`);
|
|
220
|
+
}
|
|
221
|
+
const fetchImpl = options.fetch;
|
|
222
|
+
if (!fetchImpl) {
|
|
223
|
+
throw new errors_js_1.ClientRegistryError('boop_metadata_document_unusable', 'this registry was built without an HTTP boundary, so it cannot fetch a metadata document');
|
|
224
|
+
}
|
|
225
|
+
const existing = await model.findUnique({ where: { clientId } });
|
|
226
|
+
// No cached copy: a first fetch that fails *does* fail the request,
|
|
227
|
+
// because there is nothing to fall back to and consent would otherwise
|
|
228
|
+
// show a nameless client.
|
|
229
|
+
if (!existing) {
|
|
230
|
+
const doc = await (0, metadata_document_js_1.fetchMetadataDocument)(clientId, fetchImpl, fetchLimits);
|
|
231
|
+
const fields = readMetadataDocument(doc, defaultScopes);
|
|
232
|
+
const row = await model.create({
|
|
233
|
+
data: {
|
|
234
|
+
publicId: (0, public_id_js_1.generateClientPublicId)(),
|
|
235
|
+
clientId,
|
|
236
|
+
mode: 'metadata_document',
|
|
237
|
+
status: 'enabled',
|
|
238
|
+
metadataUri: clientId,
|
|
239
|
+
metadataFetchedAt: now(),
|
|
240
|
+
description: null,
|
|
241
|
+
...fields,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
return toClient(row);
|
|
245
|
+
}
|
|
246
|
+
const cached = toClient(existing);
|
|
247
|
+
if (cached.mode !== 'metadata_document') {
|
|
248
|
+
// A `static` or `dynamic` row already owns this `client_id`. Refetching
|
|
249
|
+
// over it would let whoever controls the URL rewrite a row an operator
|
|
250
|
+
// wrote.
|
|
251
|
+
return cached;
|
|
252
|
+
}
|
|
253
|
+
const fetchedAt = cached.metadataFetchedAt;
|
|
254
|
+
const age = fetchedAt ? now().getTime() - fetchedAt.getTime() : Number.POSITIVE_INFINITY;
|
|
255
|
+
if (age < metadataMaxAgeMs)
|
|
256
|
+
return cached;
|
|
257
|
+
// Stale. Re-fetch lazily — and **fall back to the cached copy when the
|
|
258
|
+
// re-fetch fails**. A client whose document is briefly unreachable should
|
|
259
|
+
// not break consent for users who already trust it, and the cached copy
|
|
260
|
+
// is metadata we validated ourselves the last time it was reachable.
|
|
261
|
+
try {
|
|
262
|
+
const doc = await (0, metadata_document_js_1.fetchMetadataDocument)(cached.metadataUri ?? clientId, fetchImpl, fetchLimits);
|
|
263
|
+
const fields = readMetadataDocument(doc, defaultScopes);
|
|
264
|
+
const row = await model.update({
|
|
265
|
+
where: { clientId },
|
|
266
|
+
data: { ...fields, metadataFetchedAt: now() },
|
|
267
|
+
});
|
|
268
|
+
return toClient(row);
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
options.onMetadataRefreshFailed?.(clientId, err);
|
|
272
|
+
return cached;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
async registerStaticClient(input) {
|
|
276
|
+
const redirectUris = validateRedirectUris(input.redirectUris);
|
|
277
|
+
if (!input.clientId)
|
|
278
|
+
throw invalidMetadata('a static client needs a clientId');
|
|
279
|
+
if (!input.name)
|
|
280
|
+
throw invalidMetadata('a static client needs a name for the consent screen');
|
|
281
|
+
const data = {
|
|
282
|
+
mode: 'static',
|
|
283
|
+
status: 'enabled',
|
|
284
|
+
name: input.name,
|
|
285
|
+
description: optionalString(input.description, 'description'),
|
|
286
|
+
logoUri: optionalString(input.logoUri, 'logoUri'),
|
|
287
|
+
clientUri: optionalString(input.clientUri, 'clientUri'),
|
|
288
|
+
contacts: input.contacts ?? [],
|
|
289
|
+
softwareId: optionalString(input.softwareId, 'softwareId', 255),
|
|
290
|
+
softwareVersion: optionalString(input.softwareVersion, 'softwareVersion', 255),
|
|
291
|
+
policyUri: optionalString(input.policyUri, 'policyUri'),
|
|
292
|
+
tosUri: optionalString(input.tosUri, 'tosUri'),
|
|
293
|
+
redirectUris,
|
|
294
|
+
grantTypes: input.grantTypes ?? [...exports.DEFAULT_GRANT_TYPES],
|
|
295
|
+
responseTypes: input.responseTypes ?? [...exports.DEFAULT_RESPONSE_TYPES],
|
|
296
|
+
scopes: input.scopes ?? [...defaultScopes],
|
|
297
|
+
tokenEndpointAuthMethod: 'none',
|
|
298
|
+
};
|
|
299
|
+
// Idempotent, because a seed script must be safe to run twice
|
|
300
|
+
// (boop-database-prisma, "Seed Scripts"). The `publicId` is minted only
|
|
301
|
+
// on the first write: re-seeding must not change the token prefix that
|
|
302
|
+
// every live token for this client already carries.
|
|
303
|
+
const existing = await model.findUnique({ where: { clientId: input.clientId } });
|
|
304
|
+
if (existing) {
|
|
305
|
+
const row = await model.update({ where: { clientId: input.clientId }, data });
|
|
306
|
+
return toClient(row);
|
|
307
|
+
}
|
|
308
|
+
const row = await model.create({
|
|
309
|
+
data: { publicId: (0, public_id_js_1.generateClientPublicId)(), clientId: input.clientId, ...data },
|
|
310
|
+
});
|
|
311
|
+
return toClient(row);
|
|
312
|
+
},
|
|
313
|
+
async findByClientId(clientId) {
|
|
314
|
+
if (typeof clientId !== 'string' || clientId === '')
|
|
315
|
+
return null;
|
|
316
|
+
const row = await model.findUnique({ where: { clientId } });
|
|
317
|
+
return row ? toClient(row) : null;
|
|
318
|
+
},
|
|
319
|
+
async findByPublicId(publicId) {
|
|
320
|
+
if (typeof publicId !== 'string' || publicId === '')
|
|
321
|
+
return null;
|
|
322
|
+
const row = await model.findUnique({ where: { publicId } });
|
|
323
|
+
return row ? toClient(row) : null;
|
|
324
|
+
},
|
|
325
|
+
async suspend(clientId) {
|
|
326
|
+
await requireClient(model, clientId);
|
|
327
|
+
const row = await model.update({ where: { clientId }, data: { status: 'suspended' } });
|
|
328
|
+
return toClient(row);
|
|
329
|
+
},
|
|
330
|
+
async unsuspend(clientId) {
|
|
331
|
+
await requireClient(model, clientId);
|
|
332
|
+
const row = await model.update({ where: { clientId }, data: { status: 'enabled' } });
|
|
333
|
+
return toClient(row);
|
|
334
|
+
},
|
|
335
|
+
async updateRegistration(clientId, input) {
|
|
336
|
+
const client = await requireClient(model, clientId);
|
|
337
|
+
refuseStatic(client, 'modified');
|
|
338
|
+
const data = {};
|
|
339
|
+
if (input.redirect_uris !== undefined) {
|
|
340
|
+
data.redirectUris = validateRedirectUris(input.redirect_uris);
|
|
341
|
+
}
|
|
342
|
+
if (input.client_name !== undefined) {
|
|
343
|
+
data.name = optionalString(input.client_name, 'client_name', 255);
|
|
344
|
+
}
|
|
345
|
+
if (input.client_uri !== undefined)
|
|
346
|
+
data.clientUri = optionalString(input.client_uri, 'client_uri');
|
|
347
|
+
if (input.logo_uri !== undefined)
|
|
348
|
+
data.logoUri = optionalString(input.logo_uri, 'logo_uri');
|
|
349
|
+
if (input.contacts !== undefined)
|
|
350
|
+
data.contacts = optionalStringArray(input.contacts, 'contacts');
|
|
351
|
+
if (input.policy_uri !== undefined)
|
|
352
|
+
data.policyUri = optionalString(input.policy_uri, 'policy_uri');
|
|
353
|
+
if (input.tos_uri !== undefined)
|
|
354
|
+
data.tosUri = optionalString(input.tos_uri, 'tos_uri');
|
|
355
|
+
if (input.scope !== undefined)
|
|
356
|
+
data.scopes = parseScope(input.scope, defaultScopes);
|
|
357
|
+
const row = await model.update({ where: { clientId }, data });
|
|
358
|
+
return toClient(row);
|
|
359
|
+
},
|
|
360
|
+
async deleteRegistration(clientId) {
|
|
361
|
+
const client = await requireClient(model, clientId);
|
|
362
|
+
refuseStatic(client, 'deleted');
|
|
363
|
+
await model.delete({ where: { clientId } });
|
|
364
|
+
},
|
|
365
|
+
async countRegistered() {
|
|
366
|
+
return model.count({ where: { mode: 'dynamic' } });
|
|
367
|
+
},
|
|
368
|
+
async pruneAbandonedClients(pruneOptions = {}) {
|
|
369
|
+
const before = pruneOptions.before ?? new Date(now().getTime() - exports.DEFAULT_ABANDONED_CLIENT_TTL_MS);
|
|
370
|
+
const isInUse = pruneOptions.isInUse ?? (async () => false);
|
|
371
|
+
const rows = await model.findMany({
|
|
372
|
+
where: { mode: 'dynamic', createdAt: { lt: before } },
|
|
373
|
+
});
|
|
374
|
+
const removed = [];
|
|
375
|
+
for (const row of rows) {
|
|
376
|
+
const client = toClient(row);
|
|
377
|
+
if (await isInUse(client))
|
|
378
|
+
continue;
|
|
379
|
+
await model.delete({ where: { clientId: client.clientId } });
|
|
380
|
+
removed.push(client.clientId);
|
|
381
|
+
}
|
|
382
|
+
return removed;
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
return service;
|
|
386
|
+
}
|
|
387
|
+
async function requireClient(model, clientId) {
|
|
388
|
+
const row = await model.findUnique({ where: { clientId } });
|
|
389
|
+
if (!row)
|
|
390
|
+
throw new errors_js_1.ClientRegistryError('invalid_client', `no such client: ${clientId}`);
|
|
391
|
+
return toClient(row);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* A `static` row belongs to the operator who wrote it.
|
|
395
|
+
*
|
|
396
|
+
* This is a check in the service rather than a convention anyone remembers,
|
|
397
|
+
* because the thing it prevents — an unauthenticated registration surface
|
|
398
|
+
* rewriting the redirect URIs of a client the operator vouched for — is exactly
|
|
399
|
+
* the kind of hole a convention leaves open the first time someone adds a
|
|
400
|
+
* route.
|
|
401
|
+
*/
|
|
402
|
+
function refuseStatic(client, verb) {
|
|
403
|
+
if (client.mode === 'static') {
|
|
404
|
+
throw new errors_js_1.ClientRegistryError('boop_static_client_immutable', `${client.clientId} is a static client and cannot be ${verb} through the registration surface`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/** Grant types this server implements, filtered to what the client asked for. */
|
|
408
|
+
function normaliseGrantTypes(value) {
|
|
409
|
+
const requested = optionalStringArray(value, 'grant_types');
|
|
410
|
+
if (requested.length === 0)
|
|
411
|
+
return [...exports.DEFAULT_GRANT_TYPES];
|
|
412
|
+
const unsupported = requested.filter((g) => !exports.DEFAULT_GRANT_TYPES.includes(g));
|
|
413
|
+
if (unsupported.length > 0) {
|
|
414
|
+
throw invalidMetadata(`unsupported grant_types: ${unsupported.join(', ')}`);
|
|
415
|
+
}
|
|
416
|
+
return requested;
|
|
417
|
+
}
|
|
418
|
+
function normaliseResponseTypes(value) {
|
|
419
|
+
const requested = optionalStringArray(value, 'response_types');
|
|
420
|
+
if (requested.length === 0)
|
|
421
|
+
return [...exports.DEFAULT_RESPONSE_TYPES];
|
|
422
|
+
const unsupported = requested.filter((r) => !exports.DEFAULT_RESPONSE_TYPES.includes(r));
|
|
423
|
+
if (unsupported.length > 0) {
|
|
424
|
+
throw invalidMetadata(`unsupported response_types: ${unsupported.join(', ')}`);
|
|
425
|
+
}
|
|
426
|
+
return requested;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Read a fetched document into row fields.
|
|
430
|
+
*
|
|
431
|
+
* Everything on the document is a claim by whoever owns the URL, so the same
|
|
432
|
+
* validation the registration endpoint applies runs here too — in particular
|
|
433
|
+
* `redirect_uris`, which is the field an attacker would want to control.
|
|
434
|
+
*/
|
|
435
|
+
function readMetadataDocument(doc, defaultScopes) {
|
|
436
|
+
return {
|
|
437
|
+
name: optionalString(doc.client_name, 'client_name', 255),
|
|
438
|
+
logoUri: optionalString(doc.logo_uri, 'logo_uri'),
|
|
439
|
+
clientUri: optionalString(doc.client_uri, 'client_uri'),
|
|
440
|
+
contacts: optionalStringArray(doc.contacts, 'contacts'),
|
|
441
|
+
policyUri: optionalString(doc.policy_uri, 'policy_uri'),
|
|
442
|
+
tosUri: optionalString(doc.tos_uri, 'tos_uri'),
|
|
443
|
+
redirectUris: validateRedirectUris(doc.redirect_uris),
|
|
444
|
+
grantTypes: normaliseGrantTypes(doc.grant_types),
|
|
445
|
+
responseTypes: normaliseResponseTypes(doc.response_types),
|
|
446
|
+
scopes: parseScope(doc.scope, defaultScopes),
|
|
447
|
+
tokenEndpointAuthMethod: parseAuthMethod(doc.token_endpoint_auth_method),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
//# sourceMappingURL=client-registry-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-registry-service.js","sourceRoot":"","sources":["../src/client-registry-service.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type DiscoveryConfig } from './discovery.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reading the capability's configuration out of the environment.
|
|
4
|
+
*
|
|
5
|
+
* ## Why the environment, and why here
|
|
6
|
+
*
|
|
7
|
+
* boop has no feature-flag system — the findings grepped every capability's
|
|
8
|
+
* `src/` and `scaffold/src/` for one and came back empty — so the `env` array in
|
|
9
|
+
* `capability.json` is the mechanism boop actually ships: it appends the
|
|
10
|
+
* declared variables to `.env` and records them in `.boop/config.json`.
|
|
11
|
+
*
|
|
12
|
+
* The parsing lives in the package rather than in the scaffold binding file for
|
|
13
|
+
* one reason: a value that is clamped, defaulted or warned about is behaviour,
|
|
14
|
+
* and behaviour in a scaffold file is behaviour that cannot be tested here and
|
|
15
|
+
* is copied into an application once and then never updated.
|
|
16
|
+
*
|
|
17
|
+
* ## Read once, at construction
|
|
18
|
+
*
|
|
19
|
+
* Every value here is read at boot, not per request, so a running process has
|
|
20
|
+
* one answer and a clamp warning fires once rather than on every token.
|
|
21
|
+
*/
|
|
22
|
+
/** RFC 6749 has no opinion; these are Decision 1's numbers. */
|
|
23
|
+
export declare const DEFAULT_ACCESS_TOKEN_TTL_SECONDS = 900;
|
|
24
|
+
export declare const DEFAULT_REFRESH_TOKEN_TTL_SECONDS = 7776000;
|
|
25
|
+
/**
|
|
26
|
+
* Clamps, not rejections.
|
|
27
|
+
*
|
|
28
|
+
* Above an hour the blast-radius argument for a short access token evaporates
|
|
29
|
+
* and we would be shipping a footgun as configuration; below a minute nothing
|
|
30
|
+
* works. But an application should not fail to boot over a token lifetime, so
|
|
31
|
+
* an out-of-range value is clamped and named in a warning.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ACCESS_TOKEN_TTL_BOUNDS: {
|
|
34
|
+
readonly min: 60;
|
|
35
|
+
readonly max: 3600;
|
|
36
|
+
};
|
|
37
|
+
export declare const REFRESH_TOKEN_TTL_BOUNDS: {
|
|
38
|
+
readonly min: 3600;
|
|
39
|
+
readonly max: 31536000;
|
|
40
|
+
};
|
|
41
|
+
export interface ServerConfig extends DiscoveryConfig {
|
|
42
|
+
accessTokenTtlSeconds: number;
|
|
43
|
+
refreshTokenTtlSeconds: number;
|
|
44
|
+
maxRegisteredClients: number;
|
|
45
|
+
registrationRateLimit: number;
|
|
46
|
+
registrationRateWindowMs: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ReadConfigOptions {
|
|
49
|
+
/** Where a clamp or a fallback is announced. Defaults to `console.warn`. */
|
|
50
|
+
warn?(message: string): void;
|
|
51
|
+
}
|
|
52
|
+
type Env = Record<string, string | undefined>;
|
|
53
|
+
export declare class OauthServerConfigError extends Error {
|
|
54
|
+
constructor(message: string);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bring a lifetime inside its documented bounds, naming the value and the bound
|
|
58
|
+
* if it was out of range.
|
|
59
|
+
*
|
|
60
|
+
* Exported because the *number* is clamped in two places for one reason: this
|
|
61
|
+
* module clamps what the environment said, and the token service clamps what it
|
|
62
|
+
* was constructed with. Those are not the same event — an application that
|
|
63
|
+
* builds the token service from a literal rather than from `readServerConfig`
|
|
64
|
+
* bypasses the first entirely — and a clamp that only one of them applies is a
|
|
65
|
+
* clamp a caller can walk around. One function, called twice, cannot disagree
|
|
66
|
+
* with itself about where the bounds are.
|
|
67
|
+
*/
|
|
68
|
+
export declare function clampTtlSeconds(value: number, bounds: {
|
|
69
|
+
min: number;
|
|
70
|
+
max: number;
|
|
71
|
+
}, name: string, warn: (message: string) => void): number;
|
|
72
|
+
/**
|
|
73
|
+
* Build the whole capability's configuration from an environment.
|
|
74
|
+
*
|
|
75
|
+
* Throws only for `OAUTH_ISSUER_URL`, which is `required: "boot"` in the
|
|
76
|
+
* manifest: every endpoint this capability serves is an absolute URL built from
|
|
77
|
+
* it, and a wrong issuer produces discovery documents that send clients
|
|
78
|
+
* somewhere else. That is worth refusing to boot over; a token lifetime is not.
|
|
79
|
+
*/
|
|
80
|
+
export declare function readServerConfig(env: Env, options?: ReadConfigOptions): ServerConfig;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAMhF;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,iCAAiC,UAAY,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;CAAmC,CAAC;AACxE,eAAO,MAAM,wBAAwB;;;CAA2C,CAAC;AAEjF,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,MAAM,CAcR;AAuCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,iBAAsB,GAAG,YAAY,CAgFxF"}
|