---
title: Interactive OAuth
description: Authorization code flow with PKCE for user-facing applications — app registration, redirect URIs, consent screen, token lifecycle.
nav: Auth / OAuth
order: 3
version: v1
source: handwritten
updated: 2026-06-01
---

# Interactive OAuth

Interactive OAuth uses the Authorization Code flow with PKCE
([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)). Use this mode for
applications where each end user authenticates individually — patient portals, clinician
apps, and integrations where the access token must represent a specific user identity
rather than the organization.

## App registration

Contact your Huli organization admin to register your application. You will need to
provide:

- **Redirect URIs** — the exact URIs your application will use for the OAuth callback.
  Wildcards are not permitted. Include all environments (development, staging,
  production).
- **Display name** — shown on the consent screen.
- **Requested scopes** — the scopes your app will request (see [Scopes](/v1/scopes)).

You receive a `client_id`. Public clients (SPAs, mobile apps, the CLI) do not receive a
`client_secret` — PKCE replaces the secret.

## Authorization endpoint

```
https://app.huli.ai/oauth/authorize
```

Build the authorization URL:

```bash
# Generate PKCE verifier and challenge
CODE_VERIFIER=$(openssl rand -base64 48 | tr -d '=+/' | head -c 64)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')

AUTH_URL="https://app.huli.ai/oauth/authorize\
?response_type=code\
&client_id=your-client-id\
&redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback\
&scope=system%2FPatient.rs%20system%2FAppointment.rs\
&state=$(openssl rand -hex 16)\
&code_challenge=${CODE_CHALLENGE}\
&code_challenge_method=S256"

echo "$AUTH_URL"
```

Open the URL in a browser. The user authenticates to HuliPractice and sees the consent
screen listing the requested scopes. After approval, the browser redirects to your
`redirect_uri` with a `code` query parameter.

## Token exchange

<Endpoint method="POST" path="/auth/token" />

```bash
curl -X POST https://api.huli.ai/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=<code from redirect>" \
  -d "redirect_uri=https://yourapp.example.com/callback" \
  -d "client_id=your-client-id" \
  -d "code_verifier=${CODE_VERIFIER}"
```

Successful response:

```json
{
  "access_token": "eyJhbGciOiJSUzM4NCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  "scope": "system/Patient.rs system/Appointment.rs"
}
```

Default TTLs: access token 1h, refresh token 30 days.

## Refreshing access tokens

```bash
curl -X POST https://api.huli.ai/auth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..." \
  -d "client_id=your-client-id"
```

Refresh tokens are single-use (rotation). Each refresh issues a new access token and a
new refresh token. Store the new refresh token immediately — the old one is invalidated.

## PKCE requirements

PKCE is **required** for all public clients. Requests to the authorization endpoint
without `code_challenge` and `code_challenge_method=S256` return an error.

Never use `code_challenge_method=plain`. The `S256` method is enforced.

## State parameter

Always pass `state`. Verify the `state` returned in the redirect matches what you sent.
This prevents CSRF attacks on the redirect.

## Consent screen

The consent screen at `app.huli.ai/oauth/authorize` shows:

- Your application name (set at registration).
- The organization the user is authenticated to.
- The scopes being requested (in plain language).

The user must actively click **Authorize** — there is no auto-approval.

## Token identity

Interactive OAuth tokens carry the identity of the authenticated user. The access token
encodes the user's identity, their `organization_id`, and the granted scopes. Resource
operations are audited against the user, not just the application.

This is the primary distinction from admin bearer tokens and SMART backend services,
which represent the application/key rather than an individual.

## CLI usage

Interactive OAuth is a supported v1 auth mode on the API. The `huli` CLI does not include
an interactive-login subcommand for it — for CLI access use SMART backend services
(`huli auth setup`) or an admin bearer token. See the [CLI reference](/v1/cli) for the
full `huli auth` reference.
