or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdauthorization.mdindex.md
tile.json

authorization.mddocs/

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

```