# Using Pushed Authorization Requests (PAR) for Persona Authentications

> Push identity claims to Persona over a backend request instead of the browser: the PAR endpoint, request format, and how it fits the OIDC flow.

Source: https://help.withpersona.com/articles/x9fUiSBIizAnfHL0o6a3SR/
Section: Authentications > Introduction > Configuring Advanced OIDC Options

The standard OIDC authorization flow is client-initiated: every parameter, including identity claims like `given_name`, `family_name`, and `birthdate`, is packed into the `/authorize` URL the browser is redirected to. Data passed as query parameters can leak into browser history, `Referer` headers, and server logs. A Pushed Authorization Request (PAR) avoids that by moving those parameters to a backend call instead.

This article assumes you're already familiar with the OIDC flow Persona Authentications implements; see [Understanding OpenID Connect (OIDC) for Persona Authentications](https://help.withpersona.com/articles/WbPyh1Uk3ZwFpK86iohgVU/) if you need that background first.

## What PAR is

PAR is an OAuth 2.0 / OIDC extension. Instead of sending authorization parameters to the browser-facing `/authorize` URL, the client first pushes those parameters directly to the authorization server over a backend, client-authenticated `POST`. The server stores them and returns a short-lived, single-use `request_uri` handle. The client then sends the user's browser to `/authorize` with just `client_id` and that handle.

Persona uses PAR to securely prefill data on the inquiry it creates for an authentication, and to move responsibility for starting the flow from the browser to your server.

| Detail               | Value                                                             |
| -------------------- | ----------------------------------------------------------------- |
| Endpoint             | `POST https://authenticate.withpersona.com/authenticate/oidc/par` |
| `request_uri` format | `urn:ietf:params:oauth:request_uri:<random>` (treat as opaque)    |
| TTL                  | 90 seconds                                                        |
| Single-use           | Consumed the moment it's redeemed at `/authorize`                 |
| At rest              | The stored authorization parameters are encrypted                 |

💡

PAR works whether or not your authentication template has "Require PAR" enabled. That setting only controls whether Persona rejects an `/authorize` call that skips the push step; it doesn't gate whether PAR itself is available.

## Before you start: map your fields

In your authentication template, configure `query_params_to_inquiry_field_mapping` to map each attribute you plan to push to the corresponding field on your inquiry template. A claim with no mapping is rejected when you try to create the pushed request, and every mapped field has to actually exist on the inquiry template version, or the template won't publish.

If you intend to use [verified claims](https://help.withpersona.com/articles/eVRV0GeHbGkJCNHh1i9aQf/) instead of plain prefill data, skip ahead to that article; verified claims replace the request body shown below with a `claims` parameter and have their own field mapping.

## Step 1: push the request

```text
POST https://authenticate.withpersona.com/authenticate/oidc/par
Content-Type: application/x-www-form-urlencoded

client_id=<env_token>_<client_token>
client_secret=<secret>
response_type=code
redirect_uri=https://your.app/callback
scope=openid
state=<opaque_csrf_value>
login_hint=john@example.com
name_first=John
name_last=Smith
birthdate=1990-07-12
```

Requirements, all enforced by the server:

- **`Content-Type` must be `application/x-www-form-urlencoded`.** Anything else returns `400 invalid_request`.
- **Client authentication** depends on your template's token endpoint auth method. The example above uses `client_secret_post` (id and secret in the body). For `client_secret_basic`, omit them from the body and send HTTP Basic auth instead.
- **`login_hint` is required** unless your template has [account creation deferred](https://help.withpersona.com/articles/BIdQAkOaogc8WXT1TSv4r8/). It's the identifier Persona uses to find or create the account the pushed data is written onto. Omitting it returns `Params login_hint is missing`.
- `birthdate` uses `YYYY-MM-DD`.

A successful push returns `201 Created` with `Cache-Control: no-store`:

```json
{
  "request_uri": "urn:ietf:params:oauth:request_uri:ab12…",
  "expires_in": 90
}
```

## Step 2: redeem the request_uri

Redirect the user's browser here with only `client_id` and `request_uri`. None of the other parameters travel in the URL; they were already pushed in Step 1.

```text
GET https://authenticate.withpersona.com/authenticate/oidc/authorize
      ?client_id=<env_token>_<client_token>
      &request_uri=urn:ietf:params:oauth:request_uri:ab12…
```

Persona redeems the request, checks the stored `client_id` matches, finds or creates the account, and creates the inquiry with the pushed attributes. The browser then continues into the hosted inquiry flow. This is the last PAR-specific step; nothing downstream needs to know PAR was used.

## The rest of the flow is unchanged

Once the `request_uri` is redeemed, the flow is identical to the standard authorization code flow:

1.  The user completes the inquiry and Persona redirects back to your `redirect_uri` with `?code=<auth_code>&state=<...>`. Verify `state` matches what you sent.
2.  Exchange the code at `/token` for `{ id_token, access_token, expires_in, ... }`.
3.  Optionally call `/userinfo` with the `access_token` for additional account-backed claims.

## Where to go from here

[Configure Persona for the Okta Account Management Policy](https://help.withpersona.com/articles/x7vPGY4te68wp1T0Ce5eFi/) is a concrete example of an integration that requires PAR.
