0
# Token Management
1
2
Token introspection, revocation, and lifecycle management operations for OAuth 2.0 access and refresh tokens.
3
4
## Capabilities
5
6
### Token Introspection
7
8
Query token status and metadata using RFC 7662 token introspection.
9
10
```typescript { .api }
11
/**
12
* Introspect token status and metadata
13
* @param config - Configuration instance
14
* @param token - Token to introspect (access or refresh token)
15
* @param parameters - Additional introspection parameters
16
* @returns Promise resolving to introspection response
17
*/
18
function tokenIntrospection(
19
config: Configuration,
20
token: string,
21
parameters?: URLSearchParams | Record<string, string>
22
): Promise<IntrospectionResponse>;
23
```
24
25
**Usage Examples:**
26
27
```typescript
28
import * as client from "openid-client";
29
30
// Basic token introspection
31
const introspection = await client.tokenIntrospection(config, accessToken);
32
33
if (introspection.active) {
34
console.log("Token is active");
35
console.log("Scope:", introspection.scope);
36
console.log("Client ID:", introspection.client_id);
37
console.log("Username:", introspection.username);
38
console.log("Expires at:", new Date(introspection.exp * 1000));
39
} else {
40
console.log("Token is inactive");
41
}
42
43
// With token type hint for better performance
44
const introspection = await client.tokenIntrospection(
45
config,
46
accessToken,
47
{
48
token_type_hint: "access_token"
49
}
50
);
51
52
// Introspect refresh token
53
const refreshIntrospection = await client.tokenIntrospection(
54
config,
55
refreshToken,
56
{
57
token_type_hint: "refresh_token"
58
}
59
);
60
61
// Check specific token properties
62
if (introspection.active) {
63
// Check if token has required scope
64
const hasScope = introspection.scope?.includes("read:users");
65
66
// Check token expiration
67
const expiresIn = introspection.exp - Math.floor(Date.now() / 1000);
68
if (expiresIn < 300) {
69
console.log("Token expires in less than 5 minutes");
70
}
71
72
// Check audience
73
if (Array.isArray(introspection.aud)) {
74
console.log("Token audiences:", introspection.aud);
75
} else if (introspection.aud) {
76
console.log("Token audience:", introspection.aud);
77
}
78
}
79
```
80
81
### Token Revocation
82
83
Revoke access or refresh tokens using RFC 7009 token revocation.
84
85
```typescript { .api }
86
/**
87
* Revoke token
88
* @param config - Configuration instance
89
* @param token - Token to revoke (access or refresh token)
90
* @param parameters - Additional revocation parameters
91
* @returns Promise resolving when revocation is complete
92
*/
93
function tokenRevocation(
94
config: Configuration,
95
token: string,
96
parameters?: URLSearchParams | Record<string, string>
97
): Promise<void>;
98
```
99
100
**Usage Examples:**
101
102
```typescript
103
import * as client from "openid-client";
104
105
// Basic token revocation
106
await client.tokenRevocation(config, accessToken);
107
console.log("Access token revoked");
108
109
// With token type hint
110
await client.tokenRevocation(config, refreshToken, {
111
token_type_hint: "refresh_token"
112
});
113
console.log("Refresh token revoked");
114
115
// Revoke all tokens (if supported by server)
116
// Revoking refresh token may invalidate associated access tokens
117
await client.tokenRevocation(config, refreshToken, {
118
token_type_hint: "refresh_token"
119
});
120
121
// Error handling
122
try {
123
await client.tokenRevocation(config, token);
124
} catch (error) {
125
if (error.error === "unsupported_token_type") {
126
console.log("Server doesn't support revoking this token type");
127
} else if (error.error === "invalid_request") {
128
console.log("Invalid revocation request");
129
} else {
130
console.log("Revocation failed:", error.message);
131
}
132
}
133
```
134
135
## Token Lifecycle Management
136
137
Complete token lifecycle management patterns:
138
139
**Token Storage and Refresh:**
140
141
```typescript
142
import * as client from "openid-client";
143
144
interface TokenStorage {
145
accessToken?: string;
146
refreshToken?: string;
147
expiresAt?: number;
148
idToken?: string;
149
}
150
151
class TokenManager {
152
private tokens: TokenStorage = {};
153
154
constructor(private config: client.Configuration) {}
155
156
async getValidAccessToken(): Promise<string> {
157
// Check if current token is valid
158
if (this.tokens.accessToken && this.tokens.expiresAt) {
159
const now = Math.floor(Date.now() / 1000);
160
const bufferTime = 300; // 5 minutes buffer
161
162
if (this.tokens.expiresAt > now + bufferTime) {
163
return this.tokens.accessToken;
164
}
165
}
166
167
// Refresh if needed
168
if (this.tokens.refreshToken) {
169
await this.refreshTokens();
170
return this.tokens.accessToken!;
171
}
172
173
throw new Error("No valid tokens available");
174
}
175
176
async refreshTokens(): Promise<void> {
177
if (!this.tokens.refreshToken) {
178
throw new Error("No refresh token available");
179
}
180
181
try {
182
const newTokens = await client.refreshTokenGrant(
183
this.config,
184
this.tokens.refreshToken
185
);
186
187
this.storeTokens(newTokens);
188
} catch (error) {
189
// Refresh failed - tokens may be expired
190
this.clearTokens();
191
throw error;
192
}
193
}
194
195
storeTokens(tokens: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers): void {
196
this.tokens.accessToken = tokens.access_token;
197
this.tokens.refreshToken = tokens.refresh_token || this.tokens.refreshToken;
198
this.tokens.idToken = tokens.id_token;
199
200
if (tokens.expires_in) {
201
this.tokens.expiresAt = Math.floor(Date.now() / 1000) + tokens.expires_in;
202
}
203
}
204
205
async revokeTokens(): Promise<void> {
206
const promises: Promise<void>[] = [];
207
208
if (this.tokens.accessToken) {
209
promises.push(
210
client.tokenRevocation(this.config, this.tokens.accessToken, {
211
token_type_hint: "access_token"
212
})
213
);
214
}
215
216
if (this.tokens.refreshToken) {
217
promises.push(
218
client.tokenRevocation(this.config, this.tokens.refreshToken, {
219
token_type_hint: "refresh_token"
220
})
221
);
222
}
223
224
await Promise.allSettled(promises);
225
this.clearTokens();
226
}
227
228
clearTokens(): void {
229
this.tokens = {};
230
}
231
232
async introspectToken(token?: string): Promise<client.IntrospectionResponse> {
233
const tokenToIntrospect = token || this.tokens.accessToken;
234
if (!tokenToIntrospect) {
235
throw new Error("No token to introspect");
236
}
237
238
return client.tokenIntrospection(this.config, tokenToIntrospect);
239
}
240
}
241
```
242
243
## Response Types
244
245
```typescript { .api }
246
interface IntrospectionResponse {
247
/** Whether the token is currently active */
248
active: boolean;
249
250
/** Space-separated list of scopes */
251
scope?: string;
252
253
/** Client identifier */
254
client_id?: string;
255
256
/** Username of the resource owner */
257
username?: string;
258
259
/** Token type (usually "Bearer") */
260
token_type?: string;
261
262
/** Token expiration time (seconds since epoch) */
263
exp?: number;
264
265
/** Token issued at time (seconds since epoch) */
266
iat?: number;
267
268
/** Token not before time (seconds since epoch) */
269
nbf?: number;
270
271
/** Subject identifier */
272
sub?: string;
273
274
/** Intended audience */
275
aud?: string | string[];
276
277
/** Issuer identifier */
278
iss?: string;
279
280
/** JWT ID */
281
jti?: string;
282
283
// Additional server-specific claims may be present
284
[key: string]: any;
285
}
286
```
287
288
## Error Handling
289
290
Common error scenarios and handling:
291
292
```typescript
293
import * as client from "openid-client";
294
295
// Introspection error handling
296
try {
297
const result = await client.tokenIntrospection(config, token);
298
} catch (error) {
299
if (error.error === "invalid_request") {
300
console.log("Invalid introspection request");
301
} else if (error.error === "invalid_token") {
302
console.log("Token is invalid or malformed");
303
} else if (error.error === "unsupported_token_type") {
304
console.log("Server doesn't support introspecting this token type");
305
}
306
}
307
308
// Revocation error handling
309
try {
310
await client.tokenRevocation(config, token);
311
} catch (error) {
312
if (error.error === "invalid_request") {
313
console.log("Invalid revocation request");
314
} else if (error.error === "invalid_token") {
315
console.log("Token is invalid or already revoked");
316
} else if (error.error === "unsupported_token_type") {
317
console.log("Server doesn't support revoking this token type");
318
}
319
320
// Note: Some servers return success even for invalid tokens
321
// Check server documentation for specific behavior
322
}
323
```
324
325
## Server Capability Detection
326
327
Check server support for token management operations:
328
329
```typescript
330
import * as client from "openid-client";
331
332
// Check server metadata for supported operations
333
const serverMetadata = config.serverMetadata();
334
335
// Check for introspection support
336
if (serverMetadata.introspection_endpoint) {
337
console.log("Server supports token introspection");
338
339
// Check supported authentication methods for introspection
340
if (serverMetadata.introspection_endpoint_auth_methods_supported) {
341
console.log("Supported auth methods:",
342
serverMetadata.introspection_endpoint_auth_methods_supported);
343
}
344
}
345
346
// Check for revocation support
347
if (serverMetadata.revocation_endpoint) {
348
console.log("Server supports token revocation");
349
350
// Check supported authentication methods for revocation
351
if (serverMetadata.revocation_endpoint_auth_methods_supported) {
352
console.log("Supported auth methods:",
353
serverMetadata.revocation_endpoint_auth_methods_supported);
354
}
355
}
356
```