@eetr/eetr-auth-client 0.3.1 → 0.5.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 eetr-ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # @eetr/eetr-auth-client
2
+
3
+ TypeScript client library for the [eetr-auth](https://github.com/eetr-ai/eetr-auth) OAuth 2.1 / OIDC server.
4
+
5
+ It wraps the server's token, introspection, UserInfo, admin, and passkey-management
6
+ endpoints, plus helpers for OIDC discovery and JWT verification. Everything is
7
+ `fetch`-based and ships with full type definitions.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @eetr/eetr-auth-client
13
+ ```
14
+
15
+ ```bash
16
+ pnpm add @eetr/eetr-auth-client
17
+ # or
18
+ yarn add @eetr/eetr-auth-client
19
+ ```
20
+
21
+ **Requirements:** Node.js 18+ (the library relies on the global `fetch`; `decodeJwtPayload`
22
+ also uses Node's `Buffer`). `jose` is a runtime dependency used for JWT verification.
23
+
24
+ The package is ESM-only (`"type": "module"`) and exports both JavaScript and `.d.ts` types.
25
+
26
+ ## Quick start
27
+
28
+ ```ts
29
+ import {
30
+ fetchOIDCDiscovery,
31
+ exchangeToken,
32
+ validateJwt,
33
+ getUserInfo,
34
+ } from "@eetr/eetr-auth-client";
35
+
36
+ const ISSUER = "https://auth.example.com";
37
+
38
+ // 1. Discover endpoints
39
+ const discovery = await fetchOIDCDiscovery(ISSUER);
40
+
41
+ // 2. Exchange an authorization code for tokens (PKCE)
42
+ const tokens = await exchangeToken(
43
+ {
44
+ grantType: "authorization_code",
45
+ clientId: "my-client",
46
+ code: authorizationCode,
47
+ redirectUri: "https://app.example.com/callback",
48
+ codeVerifier,
49
+ },
50
+ { tokenEndpoint: discovery.token_endpoint }
51
+ );
52
+
53
+ // 3. Verify the access/ID token against the server's JWKS
54
+ const payload = await validateJwt(tokens.access_token, discovery.jwks_uri, {
55
+ issuer: ISSUER,
56
+ audience: "my-client",
57
+ });
58
+
59
+ // 4. Fetch the user's profile
60
+ const user = await getUserInfo(tokens.access_token, discovery.userinfo_endpoint);
61
+ ```
62
+
63
+ ## API reference
64
+
65
+ ### Discovery
66
+
67
+ ```ts
68
+ fetchOIDCDiscovery(issuerUrl: string): Promise<OIDCDiscovery>
69
+ fetchOAuthMetadata(issuerUrl: string): Promise<OAuthServerMetadata>
70
+ ```
71
+
72
+ Fetch the server's `/.well-known/openid-configuration` or
73
+ `/.well-known/oauth-authorization-server` metadata. Use the returned
74
+ `token_endpoint`, `jwks_uri`, `userinfo_endpoint`, etc. to configure the rest of
75
+ the client rather than hard-coding paths.
76
+
77
+ ### Authorization URL
78
+
79
+ ```ts
80
+ buildAuthorizationUrl(authorizationEndpoint: string, params: AuthorizationUrlParams): string
81
+ ```
82
+
83
+ Builds the authorization-request URL for the `authorization_code` + PKCE flow.
84
+ `code_challenge` is required (the server only accepts `S256`). Pass `nonce` to
85
+ request an OIDC `nonce` that the server binds into the issued `id_token`, then
86
+ verify it on the returned token with [`validateIdToken`](#jwt-verification).
87
+
88
+ ```ts
89
+ const url = buildAuthorizationUrl(discovery.authorization_endpoint, {
90
+ clientId: "my-client",
91
+ redirectUri: "https://app.example.com/callback",
92
+ codeChallenge, // base64url SHA-256 of your PKCE verifier
93
+ scope: "openid profile email",
94
+ state,
95
+ nonce,
96
+ });
97
+ ```
98
+
99
+ Prefer the `OIDCScope` constants and the `scopes` array so a typo can't silently
100
+ drop `openid` (which would make `/userinfo` return `403 insufficient_scope`). `scope`
101
+ and `scopes` are merged and de-duplicated, so either or both work:
102
+
103
+ ```ts
104
+ import { OIDCScope, STANDARD_OIDC_SCOPES } from "@eetr/eetr-auth-client";
105
+
106
+ const url = buildAuthorizationUrl(discovery.authorization_endpoint, {
107
+ clientId: "my-client",
108
+ redirectUri: "https://app.example.com/callback",
109
+ codeChallenge,
110
+ scopes: [OIDCScope.OpenId, OIDCScope.Profile, OIDCScope.Email], // or STANDARD_OIDC_SCOPES
111
+ state,
112
+ nonce,
113
+ });
114
+ ```
115
+
116
+ > The client must be **granted** these scopes by an admin, and `openid`/`profile`/`email`
117
+ > are seeded on the server. Requesting a scope the client wasn't granted fails with
118
+ > `invalid_scope`.
119
+
120
+ ### Token exchange
121
+
122
+ ```ts
123
+ exchangeToken(params: ExchangeTokenParams, config: ExchangeTokenConfig): Promise<TokenResponse>
124
+ ```
125
+
126
+ Performs an OAuth token request. `grantType` is one of `"authorization_code"`,
127
+ `"client_credentials"`, or `"refresh_token"`; supply the fields relevant to the grant.
128
+ When the `openid` scope was granted on an `authorization_code` exchange, the
129
+ `TokenResponse` also includes a signed `id_token`.
130
+
131
+ ```ts
132
+ // Client credentials (machine-to-machine)
133
+ const tokens = await exchangeToken(
134
+ {
135
+ grantType: "client_credentials",
136
+ clientId: "service-a",
137
+ clientSecret: process.env.CLIENT_SECRET,
138
+ scope: "admin",
139
+ },
140
+ { tokenEndpoint: discovery.token_endpoint }
141
+ );
142
+ ```
143
+
144
+ On a non-2xx response it throws an [`OAuthError`](#error-handling) carrying the
145
+ server's `error` code and `error_description`.
146
+
147
+ ### TokenManager
148
+
149
+ A small helper that caches an access token and transparently refreshes it (using a
150
+ 30-second expiry skew) when a refresh token is available.
151
+
152
+ ```ts
153
+ import { TokenManager } from "@eetr/eetr-auth-client";
154
+
155
+ const manager = new TokenManager({
156
+ issuerUrl: ISSUER,
157
+ clientId: "my-client",
158
+ clientSecret: process.env.CLIENT_SECRET, // optional for public clients
159
+ tokenEndpoint: discovery.token_endpoint,
160
+ });
161
+
162
+ manager.setTokens(tokens); // seed from an initial exchange
163
+ const accessToken = await manager.getAccessToken(); // refreshes if expired
164
+ ```
165
+
166
+ `getAccessToken()` throws an `OAuthError` with code `no_token` if there is no valid
167
+ token and no refresh token to fall back on.
168
+
169
+ ### JWT verification
170
+
171
+ ```ts
172
+ validateJwt(token: string, jwksUri: string, options?: ValidateJwtOptions): Promise<JWTPayload>
173
+ validateIdToken(token: string, jwksUri: string, options?: ValidateIdTokenOptions): Promise<IDTokenClaims>
174
+ decodeJwtPayload(token: string): JWTPayload
175
+ ```
176
+
177
+ `validateJwt` verifies the signature against the server's JWKS (remote keys are
178
+ cached per `jwksUri`) and validates `issuer`/`audience`/expiry (`clockTolerance`
179
+ defaults to 5 seconds). `decodeJwtPayload` decodes the payload **without** verifying
180
+ the signature — use it only for inspecting claims you have already verified.
181
+
182
+ ```ts
183
+ const payload = await validateJwt(accessToken, discovery.jwks_uri, {
184
+ issuer: ISSUER,
185
+ audience: "my-client",
186
+ clockTolerance: 10,
187
+ });
188
+ ```
189
+
190
+ `validateIdToken` verifies an OIDC `id_token` the same way and, when you pass the
191
+ `nonce` you sent to the authorization endpoint, additionally checks the token's
192
+ `nonce` claim — throwing `id_token nonce mismatch` on a mismatch. It returns the
193
+ typed `IDTokenClaims` (`sub`, `auth_time`, `nonce`, `at_hash`, plus scope-gated
194
+ `name`/`preferred_username`/`picture`/`email`/`email_verified`).
195
+
196
+ ```ts
197
+ const claims = await validateIdToken(tokens.id_token!, discovery.jwks_uri, {
198
+ issuer: ISSUER,
199
+ audience: "my-client",
200
+ nonce, // the value passed to buildAuthorizationUrl
201
+ });
202
+ ```
203
+
204
+ ### Token introspection
205
+
206
+ ```ts
207
+ introspectToken(params: IntrospectTokenParams, config: IntrospectTokenConfig): Promise<TokenValidationResponse>
208
+ ```
209
+
210
+ Asks the server whether a token is active within a given environment. The endpoint
211
+ is published as `token_introspection_endpoint` in the OAuth metadata (defaults to
212
+ `${ISSUER}/api/token/validate`).
213
+
214
+ ```ts
215
+ const metadata = await fetchOAuthMetadata(ISSUER);
216
+
217
+ const result = await introspectToken(
218
+ { token: accessToken, scopes: ["read"], environmentName: "production" },
219
+ { introspectionEndpoint: metadata.token_introspection_endpoint! }
220
+ );
221
+ // → { valid, active, client_id, expires_at }
222
+ ```
223
+
224
+ ### UserInfo
225
+
226
+ ```ts
227
+ getUserInfo(accessToken: string, userInfoEndpoint: string): Promise<UserInfoResponse>
228
+ ```
229
+
230
+ Returns the OIDC UserInfo claims for the bearer token. The endpoint **requires the
231
+ `openid` scope**; only `sub` is always present, and the remaining claims are gated by
232
+ scope (`profile` → `name`/`preferred_username`/`picture`, `email` →
233
+ `email`/`email_verified`).
234
+
235
+ On failure it throws an [`OAuthError`](#error-handling). A token that is valid but
236
+ lacks `openid` yields a `403`, surfaced as `code === "insufficient_scope"` (use
237
+ `err.isInsufficientScope`) so you can re-authorize with the right scopes rather than
238
+ discarding the token:
239
+
240
+ ```ts
241
+ try {
242
+ const info = await getUserInfo(accessToken, discovery.userinfo_endpoint);
243
+ } catch (err) {
244
+ if (err instanceof OAuthError && err.isInsufficientScope) {
245
+ // token is fine but missing `openid` — restart the dance requesting it
246
+ }
247
+ }
248
+ ```
249
+
250
+ #### Normalized profile
251
+
252
+ ```ts
253
+ toUserProfile(userInfo: UserInfoResponse, idTokenClaims?: IDTokenClaims): UserProfile
254
+ ```
255
+
256
+ Merges the UserInfo response (and optionally the decoded `id_token` claims) into a
257
+ single camelCased `UserProfile` (`sub`, `name`, `preferredUsername`, `picture`,
258
+ `email`, `emailVerified`). UserInfo wins; the id_token fills any gap.
259
+
260
+ ```ts
261
+ const info = await getUserInfo(tokens.access_token, discovery.userinfo_endpoint);
262
+ const profile = toUserProfile(info, decodeJwtPayload(tokens.id_token!));
263
+ // → { sub, name?, preferredUsername?, picture?, email?, emailVerified? }
264
+ ```
265
+
266
+ ### Admin API
267
+
268
+ User management against the server's admin API. Requires an access token from a
269
+ client configured as an **admin API client** on the server.
270
+
271
+ ```ts
272
+ import {
273
+ getAdminUser,
274
+ createAdminUser,
275
+ updateAdminUser,
276
+ deleteAdminUser,
277
+ } from "@eetr/eetr-auth-client";
278
+
279
+ const config = { baseUrl: ISSUER, accessToken }; // AdminClientConfig
280
+
281
+ const created = await createAdminUser(
282
+ { username: "alice", password: "•••", email: "alice@example.com" },
283
+ config
284
+ );
285
+
286
+ await updateAdminUser("alice", { name: "Alice B." }, config);
287
+ const user = await getAdminUser("alice", config); // by username or UUID
288
+ await deleteAdminUser(created.id, config);
289
+ ```
290
+
291
+ All admin calls throw `OAuthError` on non-2xx responses.
292
+
293
+ ### Passkey management
294
+
295
+ List, rename, and remove a user's passkeys. The access token must belong to the
296
+ user whose passkeys are being managed.
297
+
298
+ ```ts
299
+ import { listPasskeys, renamePasskey, removePasskey } from "@eetr/eetr-auth-client";
300
+
301
+ const config = { baseUrl: ISSUER, accessToken }; // UserClientConfig
302
+
303
+ const passkeys = await listPasskeys(config);
304
+ await renamePasskey(passkeys[0].id, "Work laptop", config);
305
+ await removePasskey(passkeys[0].id, config);
306
+ ```
307
+
308
+ > Only passkey *management* is available here. Creating or authenticating with a
309
+ > passkey is a WebAuthn ceremony that requires a browser and cannot be driven from a
310
+ > server-side client. `removePasskey` deletes the server-side record only — it does
311
+ > not remove the credential from the device/authenticator.
312
+
313
+ ## Error handling
314
+
315
+ API helpers throw `OAuthError` (a subclass of `Error`) on non-2xx responses, exposing
316
+ the server's machine-readable `code` alongside the message:
317
+
318
+ ```ts
319
+ import { OAuthError } from "@eetr/eetr-auth-client";
320
+
321
+ try {
322
+ await exchangeToken(params, config);
323
+ } catch (err) {
324
+ if (err instanceof OAuthError) {
325
+ console.error(err.code, err.message); // e.g. "invalid_grant"
326
+ }
327
+ }
328
+ ```
329
+
330
+ `OAuthError` also exposes the HTTP `status` and the server's `description`
331
+ (`error_description`) when available, plus an `isInsufficientScope` convenience for
332
+ the `/userinfo` 403 case (token valid but missing `openid`).
333
+
334
+ The discovery helpers throw a plain `Error` with the HTTP status on failure.
335
+
336
+ ## Types
337
+
338
+ The package exports TypeScript types for every request and response shape, including
339
+ `TokenResponse`, `UserInfoResponse`, `UserProfile`, `IDTokenClaims`, `OIDCDiscovery`,
340
+ `OAuthServerMetadata`, `AuthClientConfig`, `JWTPayload`, `TokenValidationResponse`,
341
+ `GrantType`, `ExchangeTokenParams`/`Config`, `AuthorizationUrlParams`,
342
+ `IntrospectTokenParams`/`Config`, `OIDCScopeValue`, `ValidateJwtOptions`,
343
+ `ValidateIdTokenOptions`, `AdminUserRecord`, `AdminClientConfig`, `CreateUserParams`,
344
+ `UpdateUserParams`, `PasskeySummary`, and `UserClientConfig`. It also exports the
345
+ `OIDCScope` / `STANDARD_OIDC_SCOPES` constants, the `resolveScopeParam` helper, and
346
+ the `toUserProfile` profile normalizer.
347
+
348
+ ## License
349
+
350
+ See the [eetr-auth repository](https://github.com/eetr-ai/eetr-auth).
package/dist/admin.d.ts CHANGED
@@ -18,6 +18,54 @@ export interface CreateUserParams {
18
18
  name?: string | null;
19
19
  email?: string | null;
20
20
  }
21
+ export interface AdminConsentRecord {
22
+ /** The client's public client_id. */
23
+ clientId: string;
24
+ clientName: string | null;
25
+ /** The scope names this user has consented to for that client. */
26
+ scopes: string[];
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ }
30
+ export interface RevokeConsentResult {
31
+ ok: boolean;
32
+ /** Access tokens force-expired as part of the revocation. */
33
+ accessTokensExpired: number;
34
+ /** Unused authorization codes dropped as part of the revocation. */
35
+ codesDeleted: number;
36
+ }
37
+ export interface ApiKeyRecord {
38
+ /** Public handle — the middle segment of the credential. Safe to display and log. */
39
+ keyId: string;
40
+ name: string | null;
41
+ /** The bound user. Becomes the `sub` of every token this key mints. */
42
+ userId: string;
43
+ username: string;
44
+ createdBy: string;
45
+ createdAt: string;
46
+ /** null = never expires. */
47
+ expiresAt: string | null;
48
+ /** Non-null once revoked; the record is kept so the audit trail still resolves. */
49
+ revokedAt: string | null;
50
+ lastUsedAt: string | null;
51
+ }
52
+ export interface CreateApiKeyParams {
53
+ /** Internal user UUID or username to bind the key to. Required. */
54
+ userId: string;
55
+ /** Human-readable label, e.g. the pipeline that uses it. */
56
+ name?: string | null;
57
+ /** ISO timestamp. Omit for a key that never expires. */
58
+ expiresAt?: string | null;
59
+ /**
60
+ * Subset of the client's granted scopes. Omit for all of them. The subset is a
61
+ * snapshot: the key does not pick up scopes granted to the client later.
62
+ */
63
+ scopes?: readonly string[];
64
+ }
65
+ export interface CreateApiKeyResult extends ApiKeyRecord {
66
+ /** The full credential. Returned only here — it cannot be recovered afterwards. */
67
+ apiKey: string;
68
+ }
21
69
  export interface UpdateUserParams {
22
70
  username?: string;
23
71
  password?: string;
@@ -41,4 +89,38 @@ export declare function updateAdminUser(idOrUsername: string, updates: UpdateUse
41
89
  * UUID or the username.
42
90
  */
43
91
  export declare function deleteAdminUser(idOrUsername: string, config: AdminClientConfig): Promise<void>;
92
+ /**
93
+ * List the applications a user has authorized, with the scopes consented to for each.
94
+ * `idOrUsername` accepts either the internal UUID or the username.
95
+ */
96
+ export declare function listUserConsents(idOrUsername: string, config: AdminClientConfig): Promise<AdminConsentRecord[]>;
97
+ /**
98
+ * Withdraw a user's consent for one client, addressed by its public `client_id`.
99
+ *
100
+ * This also revokes that user's refresh tokens, access tokens, and unused authorization
101
+ * codes for the client, so access stops immediately rather than at token expiry.
102
+ */
103
+ export declare function revokeUserConsent(idOrUsername: string, clientId: string, config: AdminClientConfig): Promise<RevokeConsentResult>;
104
+ /**
105
+ * List every API key issued for a client, including revoked and expired ones.
106
+ *
107
+ * The secret is never returned — it is shown only once, when the key is created.
108
+ */
109
+ export declare function listClientApiKeys(clientId: string, config: AdminClientConfig): Promise<ApiKeyRecord[]>;
110
+ /**
111
+ * Issue an API key for a client, bound to a user.
112
+ *
113
+ * The full credential comes back in `apiKey` and is not recoverable afterwards — store
114
+ * it at the call site or hand it straight to the operator.
115
+ */
116
+ export declare function createClientApiKey(clientId: string, params: CreateApiKeyParams, config: AdminClientConfig): Promise<CreateApiKeyResult>;
117
+ /**
118
+ * Revoke an API key, addressed by its public handle.
119
+ *
120
+ * Takes effect on the next exchange; access tokens already minted live out their
121
+ * remaining TTL. Idempotent — retrying keeps the original revocation timestamp.
122
+ */
123
+ export declare function revokeClientApiKey(clientId: string, keyId: string, config: AdminClientConfig): Promise<{
124
+ ok: boolean;
125
+ }>;
44
126
  //# sourceMappingURL=admin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAqBD;;GAEG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,eAAe,CAAC,CAQ1B;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,eAAe,CAAC,CAa1B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,eAAe,CAAC,CAa1B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAQf"}
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
package/dist/admin.js CHANGED
@@ -6,6 +6,14 @@ function adminUsersUrl(baseUrl, idOrUsername) {
6
6
  }
7
7
  return `${trimmed}/api/admin/users/${encodeURIComponent(idOrUsername)}`;
8
8
  }
9
+ function adminUserConsentsUrl(baseUrl, idOrUsername) {
10
+ return `${adminUsersUrl(baseUrl, idOrUsername)}/consents`;
11
+ }
12
+ function adminClientApiKeysUrl(baseUrl, clientId, keyId) {
13
+ const trimmed = baseUrl.replace(/\/+$/, "");
14
+ const base = `${trimmed}/api/admin/clients/${encodeURIComponent(clientId)}/api-keys`;
15
+ return keyId === undefined ? base : `${base}/${encodeURIComponent(keyId)}`;
16
+ }
9
17
  async function parseError(res) {
10
18
  const data = (await res.json().catch(() => ({})));
11
19
  return new OAuthError(data.error ?? "server_error", data.error_description ?? `Admin API request failed: ${res.status}`);
@@ -67,4 +75,92 @@ export async function deleteAdminUser(idOrUsername, config) {
67
75
  throw await parseError(res);
68
76
  }
69
77
  }
78
+ /**
79
+ * List the applications a user has authorized, with the scopes consented to for each.
80
+ * `idOrUsername` accepts either the internal UUID or the username.
81
+ */
82
+ export async function listUserConsents(idOrUsername, config) {
83
+ const res = await fetch(adminUserConsentsUrl(config.baseUrl, idOrUsername), {
84
+ headers: { Authorization: `Bearer ${config.accessToken}` },
85
+ });
86
+ if (!res.ok) {
87
+ throw await parseError(res);
88
+ }
89
+ const data = (await res.json());
90
+ return data.consents;
91
+ }
92
+ /**
93
+ * Withdraw a user's consent for one client, addressed by its public `client_id`.
94
+ *
95
+ * This also revokes that user's refresh tokens, access tokens, and unused authorization
96
+ * codes for the client, so access stops immediately rather than at token expiry.
97
+ */
98
+ export async function revokeUserConsent(idOrUsername, clientId, config) {
99
+ const url = new URL(adminUserConsentsUrl(config.baseUrl, idOrUsername));
100
+ url.searchParams.set("client_id", clientId);
101
+ const res = await fetch(url.toString(), {
102
+ method: "DELETE",
103
+ headers: { Authorization: `Bearer ${config.accessToken}` },
104
+ });
105
+ if (!res.ok) {
106
+ throw await parseError(res);
107
+ }
108
+ return res.json();
109
+ }
110
+ /**
111
+ * List every API key issued for a client, including revoked and expired ones.
112
+ *
113
+ * The secret is never returned — it is shown only once, when the key is created.
114
+ */
115
+ export async function listClientApiKeys(clientId, config) {
116
+ const res = await fetch(adminClientApiKeysUrl(config.baseUrl, clientId), {
117
+ headers: { Authorization: `Bearer ${config.accessToken}` },
118
+ });
119
+ if (!res.ok) {
120
+ throw await parseError(res);
121
+ }
122
+ const data = (await res.json());
123
+ return data.apiKeys;
124
+ }
125
+ /**
126
+ * Issue an API key for a client, bound to a user.
127
+ *
128
+ * The full credential comes back in `apiKey` and is not recoverable afterwards — store
129
+ * it at the call site or hand it straight to the operator.
130
+ */
131
+ export async function createClientApiKey(clientId, params, config) {
132
+ const res = await fetch(adminClientApiKeysUrl(config.baseUrl, clientId), {
133
+ method: "POST",
134
+ headers: {
135
+ Authorization: `Bearer ${config.accessToken}`,
136
+ "Content-Type": "application/json",
137
+ },
138
+ body: JSON.stringify({
139
+ userId: params.userId,
140
+ ...(params.name !== undefined ? { name: params.name } : {}),
141
+ ...(params.expiresAt !== undefined ? { expiresAt: params.expiresAt } : {}),
142
+ ...(params.scopes !== undefined ? { scopes: [...params.scopes] } : {}),
143
+ }),
144
+ });
145
+ if (!res.ok) {
146
+ throw await parseError(res);
147
+ }
148
+ return res.json();
149
+ }
150
+ /**
151
+ * Revoke an API key, addressed by its public handle.
152
+ *
153
+ * Takes effect on the next exchange; access tokens already minted live out their
154
+ * remaining TTL. Idempotent — retrying keeps the original revocation timestamp.
155
+ */
156
+ export async function revokeClientApiKey(clientId, keyId, config) {
157
+ const res = await fetch(adminClientApiKeysUrl(config.baseUrl, clientId, keyId), {
158
+ method: "DELETE",
159
+ headers: { Authorization: `Bearer ${config.accessToken}` },
160
+ });
161
+ if (!res.ok) {
162
+ throw await parseError(res);
163
+ }
164
+ return res.json();
165
+ }
70
166
  //# sourceMappingURL=admin.js.map
package/dist/admin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}