# Authenticating your application

> The OAuth 2.1 Authorisation Code flow with PKCE: discovery, authorisation, and token exchange.

> **Preview / beta**
>
> The Application Marketplace is under active development and may change without long deprecation windows. See the [Application Marketplace overview](/docs/platform/integrations/marketplace).

Authentication is standard OpenID Connect: Authorisation Code flow with PKCE (S256). There is nothing Road-specific to implement, so you can use an off-the-shelf OAuth/OIDC client library. The authorize step happens on the provider dashboard (so the customer consents in their familiar branded environment); the token, userinfo and JWKS endpoints live on the Road API.

## Public vs confidential clients

- **Confidential clients** hold a `client_secret` (provider installations and templates). The secret is presented at the token endpoint. Keep it on a server you control; never ship it to a browser or mobile binary.
- **Public clients** have no secret (dynamic registrations, and any client that can not keep one). Single-page apps, browser extensions, mobile and desktop apps are all public clients. Do not embed or display a secret in the browser.

If your application runs in the browser, it is a public client. Register it as one and rely on PKCE.

## PKCE is required for all clients

Per OAuth 2.1, PKCE with `S256` is mandatory for every client, public and confidential alike. On each authorisation you generate a random `code_verifier`, send its `S256` challenge on the authorize request, and present the verifier on the token exchange. A missing or mismatched verifier fails the exchange.

## Discovery

```http
GET https://api.road.io/1/oauth/{{providerSlug}}/.well-known/openid-configuration
```

Returns a standard discovery document. Key fields:

- `authorization_endpoint` points at the provider dashboard. Open it in the user's browser; do not call it from a back channel.
- `token_endpoint`, `userinfo_endpoint`, `jwks_uri` live on the Road API.
- `registration_endpoint` is present only when dynamic registration is enabled for the provider.
- `scopes_supported` lists the scopes Road recognises.
- `response_types_supported: ["code"]`, `grant_types_supported: ["authorization_code", "refresh_token"]`, `code_challenge_methods_supported: ["S256"]`, `token_endpoint_auth_methods_supported: ["client_secret_post", "none"]`.

ID tokens are signed RS256; verify them against `jwks_uri`.

## The flow

```mermaid
sequenceDiagram
  actor User
  participant App as Your App
  participant Browser
  participant Dashboard as Provider Dashboard
  participant API as Road API

  User->>App: Click "Connect Road"
  App->>App: Generate PKCE verifier + S256 challenge
  App->>Browser: Redirect to authorization_endpoint
  Browser->>Dashboard: Authorize (provider-branded UI)
  Dashboard->>API: GET /1/oauth/:slug/authorize/details
  API-->>Dashboard: { client, scopes, autoApprove, requiresResourceShare, ... }
  Dashboard-->>User: Show scopes (and a data-sharing step if required)
  User->>Dashboard: Approve
  Dashboard->>API: POST /1/oauth/:slug/authorize/consent
  API-->>Dashboard: redirect_uri?code=...&state=...
  Dashboard->>Browser: Redirect
  Browser->>App: Callback with code + state
  App->>API: POST /1/oauth/:slug/token (code + verifier [+ secret])
  API-->>App: { access_token, id_token, refresh_token }
```

Consent grants scopes only. For applications that request a data scope, the customer also chooses which resources to share; that step is handled separately from the protocol. See [Consent, grants and data sharing](/docs/platform/integrations/marketplace/consent-and-sharing).

## Authorisation request

The application redirects the user to:

```
{authorization_endpoint}?
  client_id={client_id}&
  redirect_uri={your_callback}&
  response_type=code&
  scope=openid+offline_access+sessions:read+chargers:read&
  state={csrf_token}&
  code_challenge={base64url_sha256(verifier)}&
  code_challenge_method=S256&
  nonce={random}
```

The `redirect_uri` must be registered on the client; unregistered URIs are rejected.

### Forcing or skipping the consent UI (`prompt`)

The standard OIDC `prompt` parameter is honoured:

| `prompt`  | Behaviour                                                                                                                                                                                  |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| (omitted) | Show consent only if needed. If an active grant already covers the requested scopes, auto-approve and redirect immediately.                                                                |
| `consent` | Always show consent; never auto-approve. Useful to let the user adjust their data sharing.                                                                                                 |
| `none`    | Never show UI. Either a code is returned, or a standard error redirect: `?error=login_required`, `consent_required`, or `interaction_required`. Use in a hidden iframe for silent refresh. |

## Token exchange

```http
POST {token_endpoint}
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code={code}&
client_id={client_id}&
client_secret={client_secret}&
redirect_uri={your_callback}&
code_verifier={pkce_verifier}
```

Confidential clients include `client_secret`. Public clients omit it: PKCE alone authenticates the exchange. The response is the standard OIDC token envelope:

```json
{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "...",
  "refresh_token": "..."
}
```

For refreshing tokens and calling the API, see [Using the API](/docs/platform/integrations/marketplace/using-the-api).

## Grants are scoped to the user

A grant authorises one application for one user. It records the scopes that user approved. Different users of the same application each have their own grant, and each token acts only for the user it was issued to. Revoking a grant disconnects the application for that user only.

## Verifying the ID token

Verify `iss`, `aud` (your `client_id`), `exp`, and the signature against the JWKS at `{issuer}/.well-known/jwks.json`. The `kid` header indicates which JWKS entry to use. Pass a CSRF-resistant `state` on the authorize request and check it on the callback; Road round-trips `state` faithfully on success and error redirects.
