|
12 | 12 | - [Specify a Custom Logout URL](#specify-a-custom-logout-url) |
13 | 13 | - [Trusted Web Activity](#trusted-web-activity) |
14 | 14 | - [DPoP [EA]](#dpop-ea) |
| 15 | + - [PAR (Pushed Authorization Request)](#par-pushed-authorization-request) |
15 | 16 | - [Authentication API](#authentication-api) |
16 | 17 | - [Login with database connection](#login-with-database-connection) |
17 | 18 | - [Login using MFA with One Time Password code](#login-using-mfa-with-one-time-password-code) |
@@ -290,6 +291,73 @@ WebAuthProvider.logout(account) |
290 | 291 | > [!NOTE] |
291 | 292 | > DPoP is supported only on Android version 6.0 (API level 23) and above. Trying to use DPoP in any older versions will result in an exception. |
292 | 293 |
|
| 294 | +## PAR (Pushed Authorization Request) |
| 295 | + |
| 296 | +PAR (Pushed Authorization Request) enables a Backend-for-Frontend (BFF) pattern where your backend server handles the `/par` and `/token` endpoints while the SDK manages the browser-based authorization flow. |
| 297 | + |
| 298 | +This is useful when: |
| 299 | +- You need to use a confidential client with a `client_secret` |
| 300 | +- You want to keep sensitive parameters server-side |
| 301 | +- You're implementing a BFF architecture |
| 302 | + |
| 303 | +### Usage |
| 304 | + |
| 305 | +The PAR flow requires coordination between your backend (BFF) and the mobile app: |
| 306 | + |
| 307 | +1. **Backend calls `/par` endpoint** - Your backend initiates the PAR request with the `client_secret` and receives a `request_uri` |
| 308 | +2. **SDK opens `/authorize`** - The mobile app uses the `request_uri` to open the browser for user authentication |
| 309 | +3. **SDK returns authorization code** - After authentication, the SDK returns the authorization code to the app |
| 310 | +4. **Backend exchanges code for tokens** - Your backend exchanges the code for tokens using the `client_secret` |
| 311 | + |
| 312 | +```kotlin |
| 313 | +// Step 1: Your BFF calls /par and returns request_uri to the app |
| 314 | +val requestUri = yourBffClient.initiatePAR(scope, audience) |
| 315 | + |
| 316 | +// Step 2 & 3: SDK opens browser and returns authorization code |
| 317 | +WebAuthProvider.par(account) |
| 318 | + .start(context, requestUri, object : Callback<AuthorizationCode, AuthenticationException> { |
| 319 | + override fun onSuccess(result: AuthorizationCode) { |
| 320 | + // Step 4: Send code to BFF to exchange for tokens |
| 321 | + yourBffClient.exchangeCode(result.code) |
| 322 | + } |
| 323 | + |
| 324 | + override fun onFailure(error: AuthenticationException) { |
| 325 | + if (error.isCanceled) { |
| 326 | + // User closed the browser |
| 327 | + } else { |
| 328 | + // Handle error |
| 329 | + } |
| 330 | + } |
| 331 | + }) |
| 332 | +``` |
| 333 | + |
| 334 | +<details> |
| 335 | + <summary>Using coroutines</summary> |
| 336 | + |
| 337 | +```kotlin |
| 338 | +try { |
| 339 | + // Step 1: Your BFF calls /par and returns request_uri |
| 340 | + val requestUri = yourBffClient.initiatePAR(scope, audience) |
| 341 | + |
| 342 | + // Step 2 & 3: SDK opens browser and returns authorization code |
| 343 | + val authCode = WebAuthProvider.par(account) |
| 344 | + .await(context, requestUri) |
| 345 | + |
| 346 | + // Step 4: Send code to BFF to exchange for tokens |
| 347 | + val credentials = yourBffClient.exchangeCode(authCode.code) |
| 348 | +} catch (e: AuthenticationException) { |
| 349 | + if (e.isCanceled) { |
| 350 | + // User closed the browser |
| 351 | + } else { |
| 352 | + // Handle error |
| 353 | + } |
| 354 | +} |
| 355 | +``` |
| 356 | +</details> |
| 357 | + |
| 358 | +> [!NOTE] |
| 359 | +> The SDK only handles opening the browser with the `request_uri` and returning the authorization code. Token exchange must be performed by your backend server which holds the `client_secret`. |
| 360 | +
|
293 | 361 | ## Authentication API |
294 | 362 |
|
295 | 363 | The client provides methods to authenticate the user against the Auth0 server. |
|
0 commit comments