0
# Authorization and Policy Enforcement
1
2
User-Managed Access (UMA) and policy enforcement capabilities providing fine-grained authorization for applications protected by Keycloak policy enforcers.
3
4
## Core Imports
5
6
```javascript
7
import Keycloak from "keycloak-js";
8
import KeycloakAuthorization from "keycloak-js/keycloak-authz";
9
10
// Create Keycloak instance first
11
const keycloak = new Keycloak({
12
url: "http://keycloak-server",
13
realm: "my-realm",
14
clientId: "my-app"
15
});
16
17
// Create authorization instance
18
const authorization = new KeycloakAuthorization(keycloak);
19
20
// Initialize Keycloak before using authorization
21
await keycloak.init();
22
```
23
24
## Capabilities
25
26
### KeycloakAuthorization Constructor
27
28
Creates an authorization client instance for UMA and policy enforcement.
29
30
```javascript { .api }
31
/**
32
* Creates an authorization client for UMA and policy enforcement
33
* @param keycloak - Initialized Keycloak instance
34
*/
35
constructor(keycloak: Keycloak);
36
```
37
38
### Initialize Authorization (Deprecated)
39
40
Initializes the authorization client. This method is deprecated but still available.
41
42
```javascript { .api }
43
/**
44
* Initializes authorization client (deprecated)
45
* @returns Promise that resolves when initialization completes
46
* @deprecated This method is deprecated and may be removed in future versions
47
*/
48
init(): Promise<void>;
49
```
50
51
**Usage Example:**
52
53
```javascript
54
import Keycloak from "keycloak-js";
55
import KeycloakAuthorization from "keycloak-js/keycloak-authz";
56
57
const keycloak = new Keycloak({
58
url: "https://auth.mycompany.com",
59
realm: "my-realm",
60
clientId: "resource-server"
61
});
62
63
await keycloak.init();
64
65
const authorization = new KeycloakAuthorization(keycloak);
66
```
67
68
### Authorization Request
69
70
Requests authorization based on permission ticket from UMA-protected resource server.
71
72
```javascript { .api }
73
/**
74
* Requests authorization using permission ticket or resource permissions
75
* @param request - Authorization request with ticket or permissions
76
* @returns Promise resolving to Requesting Party Token (RPT)
77
*/
78
authorize(request: AuthorizationRequest): Promise<string>;
79
```
80
81
**Usage Examples:**
82
83
```javascript
84
// Handle 401 response with permission ticket
85
async function handleUnauthorizedResponse(response) {
86
const wwwAuthHeader = response.headers.get("WWW-Authenticate");
87
const ticket = extractTicketFromHeader(wwwAuthHeader);
88
89
try {
90
const rpt = await authorization.authorize({ ticket });
91
92
// Retry original request with RPT
93
const retryResponse = await fetch(originalUrl, {
94
headers: {
95
Authorization: `Bearer ${rpt}`
96
}
97
});
98
99
return retryResponse;
100
} catch (error) {
101
console.error("Authorization denied:", error);
102
showAccessDeniedMessage();
103
}
104
}
105
106
// Request specific resource permissions
107
const rpt = await authorization.authorize({
108
permissions: [
109
{
110
id: "photo-album",
111
scopes: ["view", "edit"]
112
},
113
{
114
name: "document-folder",
115
scopes: ["view"]
116
}
117
]
118
});
119
120
// Request with multiple tickets
121
const rpt2 = await authorization.authorize({
122
tickets: ["ticket1", "ticket2"],
123
incrementalAuthorization: true
124
});
125
```
126
127
### Entitlement Request
128
129
Obtains entitlements (RPT) for a specific resource server.
130
131
```javascript { .api }
132
/**
133
* Obtains entitlements (RPT) for specified resource server
134
* @param resourceServerId - Client ID of the resource server
135
* @param request - Optional authorization request with specific permissions
136
* @returns Promise resolving to Requesting Party Token (RPT)
137
*/
138
entitlement(resourceServerId: string, request?: AuthorizationRequest): Promise<string>;
139
```
140
141
**Usage Examples:**
142
143
```javascript
144
// Get all permissions user can access for resource server
145
const rpt = await authorization.entitlement("photo-service");
146
147
// Get specific permissions for resource server
148
const rpt = await authorization.entitlement("document-service", {
149
permissions: [
150
{
151
name: "quarterly-reports",
152
scopes: ["view", "download"]
153
}
154
]
155
});
156
157
// Use RPT for subsequent API calls
158
const response = await fetch("/api/documents/quarterly-reports", {
159
headers: {
160
Authorization: `Bearer ${rpt}`
161
}
162
});
163
```
164
165
### RPT Access
166
167
Gets the current Requesting Party Token.
168
169
```javascript { .api }
170
/**
171
* Current Requesting Party Token (RPT) with permissions
172
*/
173
rpt?: string;
174
```
175
176
**Usage Example:**
177
178
```javascript
179
// Check if we have a current RPT
180
if (authorization.rpt) {
181
console.log("Current RPT:", authorization.rpt);
182
183
// Use existing RPT for API calls
184
const response = await fetch("/api/protected-resource", {
185
headers: {
186
Authorization: `Bearer ${authorization.rpt}`
187
}
188
});
189
} else {
190
// Request new entitlement
191
const rpt = await authorization.entitlement("my-resource-server");
192
}
193
```
194
195
## Authorization Types
196
197
### AuthorizationRequest
198
199
Configuration object for authorization requests.
200
201
```javascript { .api }
202
interface AuthorizationRequest {
203
/** Permission ticket from UMA-protected resource server */
204
ticket?: string;
205
206
/** Array of permission tickets */
207
tickets?: string[];
208
209
/** Array of specific resource permissions to request */
210
permissions?: ResourcePermission[];
211
212
/** Metadata controlling request processing */
213
metadata?: AuthorizationMetadata;
214
215
/** Whether to create permission requests for ticket resources */
216
submit_request?: boolean;
217
218
/** Enable incremental authorization */
219
incrementalAuthorization?: boolean;
220
221
/** Submitter identifier */
222
submitterId?: string;
223
224
/** Claim token for additional claims */
225
claimToken?: string;
226
227
/** Format of the claim token */
228
claimTokenFormat?: string;
229
}
230
```
231
232
### ResourcePermission
233
234
Represents a resource and its requested scopes.
235
236
```javascript { .api }
237
interface ResourcePermission {
238
/** Resource identifier or name */
239
id?: string;
240
241
/** Resource name */
242
name?: string;
243
244
/** Array of scope names to request for this resource */
245
scopes?: string[];
246
}
247
```
248
249
### AuthorizationMetadata
250
251
Controls how authorization requests are processed by the server.
252
253
```javascript { .api }
254
interface AuthorizationMetadata {
255
/** Include resource names in RPT permissions */
256
response_include_resource_name?: boolean;
257
258
/** Limit number of permissions in RPT */
259
response_permissions_limit?: number;
260
}
261
```
262
263
## UMA Authorization Flow
264
265
Complete example of handling UMA authorization flow:
266
267
```javascript
268
class ResourceClient {
269
constructor(resourceServerUrl, keycloak, authorization) {
270
this.baseUrl = resourceServerUrl;
271
this.keycloak = keycloak;
272
this.authorization = authorization;
273
}
274
275
async accessResource(resourcePath) {
276
try {
277
// Try initial request
278
let response = await this.makeRequest(resourcePath);
279
280
if (response.status === 401) {
281
// Handle UMA authorization
282
response = await this.handleUMAAuth(response, resourcePath);
283
}
284
285
return response;
286
} catch (error) {
287
console.error("Resource access failed:", error);
288
throw error;
289
}
290
}
291
292
async makeRequest(resourcePath, rpt = null) {
293
const token = rpt || this.keycloak.token;
294
295
return fetch(`${this.baseUrl}${resourcePath}`, {
296
headers: {
297
Authorization: `Bearer ${token}`,
298
"Content-Type": "application/json"
299
}
300
});
301
}
302
303
async handleUMAAuth(response, resourcePath) {
304
const wwwAuth = response.headers.get("WWW-Authenticate");
305
306
if (wwwAuth && wwwAuth.includes("UMA")) {
307
const ticket = this.extractTicket(wwwAuth);
308
309
try {
310
// Request authorization with ticket
311
const rpt = await this.authorization.authorize({
312
ticket,
313
submit_request: true
314
});
315
316
// Retry request with RPT
317
return await this.makeRequest(resourcePath, rpt);
318
319
} catch (authError) {
320
console.error("Authorization denied:", authError);
321
throw new Error("Access denied to resource");
322
}
323
}
324
325
throw new Error("Unexpected authentication challenge");
326
}
327
328
extractTicket(wwwAuthHeader) {
329
const ticketMatch = wwwAuthHeader.match(/ticket="([^"]+)"/);
330
return ticketMatch ? ticketMatch[1] : null;
331
}
332
}
333
334
// Usage
335
const resourceClient = new ResourceClient(
336
"https://api.mycompany.com",
337
keycloak,
338
authorization
339
);
340
341
const data = await resourceClient.accessResource("/protected/documents/123");
342
```
343
344
## Error Handling
345
346
Common authorization error scenarios:
347
348
```javascript
349
// Authorization with error handling
350
async function requestPermissions() {
351
try {
352
const rpt = await authorization.authorize({
353
permissions: [
354
{ name: "document-123", scopes: ["view", "edit"] }
355
]
356
});
357
358
console.log("Authorization successful");
359
return rpt;
360
361
} catch (error) {
362
if (error.message.includes("access_denied")) {
363
console.log("Access denied by policy");
364
showPermissionDeniedDialog();
365
} else if (error.message.includes("invalid_ticket")) {
366
console.log("Invalid permission ticket");
367
// Request new ticket from resource server
368
} else {
369
console.error("Authorization error:", error);
370
showGenericErrorMessage();
371
}
372
373
throw error;
374
}
375
}
376
377
// Entitlement with fallback
378
async function getEntitlements(resourceServerId) {
379
try {
380
return await authorization.entitlement(resourceServerId);
381
} catch (error) {
382
console.warn(`No entitlements for ${resourceServerId}:`, error);
383
return null;
384
}
385
}
386
```
387
388
## Integration with Callback Pattern
389
390
The authorization functions support callback-style error handling:
391
392
```javascript
393
// Using callback pattern (legacy style)
394
authorization.authorize({ ticket })
395
.then((rpt) => {
396
// onGrant callback - authorization successful
397
console.log("Access granted, RPT:", rpt);
398
retryRequestWithRPT(rpt);
399
})
400
.catch((error) => {
401
if (error.type === "access_denied") {
402
// onDeny callback - access denied
403
console.log("Access denied by policy");
404
showAccessDeniedUI();
405
} else {
406
// onError callback - unexpected error
407
console.error("Authorization error:", error);
408
showErrorUI();
409
}
410
});
411
```