# OAuth 2.0 / OpenID Connect (/oauth-overview)

e-Próspera provides an OAuth 2.0 and OpenID Connect authorization server so third-party applications can authenticate users and access their data with consent.



## Capabilities [#capabilities]

* Authenticate users with their e-Próspera account
* Receive standard OIDC claims (`sub`, `name`, `email`, `picture`)
* Obtain refresh tokens for long-lived server access via `offline_access`
* Fetch user-authorized personal and legal-entity data through the `/api/v1/me/*` endpoints

## Getting credentials [#getting-credentials]

OAuth clients are provisioned per account. If you already have one, you can view the **client ID**, allowed scopes, and redirect URIs in [Settings > Developer](https://portal.eprospera.com/settings).

To request a new client or have your secret re-issued, contact [gmembreno@prospera.hn](mailto:gmembreno@prospera.hn).

## Standard endpoints [#standard-endpoints]

| Endpoint               | URL                                                             |
| ---------------------- | --------------------------------------------------------------- |
| OpenID configuration   | `https://portal.eprospera.com/.well-known/openid-configuration` |
| JWKS                   | `https://portal.eprospera.com/api/oauth/.well-known/jwks.json`  |
| Authorization endpoint | `https://portal.eprospera.com/api/oauth/authorize`              |
| Token endpoint         | `https://portal.eprospera.com/api/oauth/token`                  |
| Userinfo endpoint      | `https://portal.eprospera.com/api/oauth/userinfo`               |
| Revocation endpoint    | `https://portal.eprospera.com/api/oauth/revoke`                 |
| Introspection endpoint | `https://portal.eprospera.com/api/oauth/introspect`             |

Reference pages:

* [OpenID configuration](/reference/oauth-openid-configuration)
* [JWKS](/reference/oauth-jwks)
* [Authorization endpoint](/reference/oauth-authorize)
* [Token endpoint](/reference/oauth-token)
* [Userinfo endpoint](/reference/oauth-userinfo)
* [Revocation endpoint](/reference/oauth-revoke)
* [Introspection endpoint](/reference/oauth-introspect)

## Recommended initial scopes [#recommended-initial-scopes]

Start with:

```text
openid profile email
```

Add `offline_access` if your backend needs refresh tokens.

## Supported scopes [#supported-scopes]

| Scope                                   | Description                                                     |
| --------------------------------------- | --------------------------------------------------------------- |
| `openid`                                | Authenticate the user with e-Próspera.                          |
| `profile`                               | Read the user's name and profile picture.                       |
| `email`                                 | Read the user's email address.                                  |
| `offline_access`                        | Receive a refresh token for background access-token renewal.    |
| `eprospera:person.details.read`         | Read detailed natural-person profile data.                      |
| `eprospera:person.residency.read`       | Read the user's current residency status.                       |
| `eprospera:person.id_verification.read` | Read the latest approved identity-verification images.          |
| `eprospera:entity.read`                 | Read legal-entity data for entities the user consents to share. |
| `eprospera:entity.documents.read`       | Read legal-entity documents for consented entities.             |

## Consent behavior [#consent-behavior]

When you request entity scopes (`eprospera:entity.read`, `eprospera:entity.documents.read`), the consent screen lets the user choose which legal entities to share. The access token is limited to that selection.

This means:

* `GET /api/v1/me/legal-entities` can return an empty array even with a valid token.
* `GET /api/v1/me/legal-entities/{id}` and `/documents` only work for consented entity IDs.
* If the user is no longer a representative of an entity, it stops appearing in responses.

## Remembered consent [#remembered-consent]

Once a user approves your client, the grant is remembered. Later authorization requests covered by the remembered grant skip the consent screen — the user is redirected straight back to your `redirect_uri` with a fresh authorization code. Requests for additional scopes show the consent screen again, and you can always force it with `prompt=consent`.

## Revocation from Connected apps [#revocation-from-connected-apps]

Users can revoke your app's access at any time from portal **Settings → Connected apps**. Revocation invalidates all of your tokens for that user: access tokens stop working, and refresh attempts fail with `invalid_grant`. Handle `invalid_grant` by sending the user back through the authorization flow.

## PKCE [#pkce]

All newly registered clients must use PKCE: send `code_challenge` + `code_challenge_method=S256` on the authorization request and the matching `code_verifier` on the token exchange. Authorization requests from PKCE-required clients without a code challenge fail with `error=invalid_request`. Existing clients without the flag should adopt PKCE too — it is strongly recommended for every integration.

## Client secret rotation [#client-secret-rotation]

You can request a rotation of your client secret at any time (contact [gmembreno@prospera.hn](mailto:gmembreno@prospera.hn)). After a rotation, the old secret keeps working during a grace period so you can deploy without downtime — update your integration to the new secret before the grace period expires.

## Resource endpoints [#resource-endpoints]

The scopes above unlock these endpoints:

| Endpoint                                                                                           | Required scope                          |
| -------------------------------------------------------------------------------------------------- | --------------------------------------- |
| [GET `/api/v1/me/natural-person`](/reference/oauth-get-natural-person)                             | `eprospera:person.details.read`         |
| [GET `/api/v1/me/natural-person/residency`](/reference/oauth-get-natural-person-residency)         | `eprospera:person.residency.read`       |
| [GET `/api/v1/me/natural-person/id-verification`](/reference/oauth-get-id-verification)            | `eprospera:person.id_verification.read` |
| [GET `/api/v1/me/legal-entities`](/reference/oauth-get-legal-entities)                             | `eprospera:entity.read`                 |
| [GET `/api/v1/me/legal-entities/[id]`](/reference/oauth-get-legal-entities-id)                     | `eprospera:entity.read`                 |
| [GET `/api/v1/me/legal-entities/[id]/documents`](/reference/oauth-get-legal-entities-id-documents) | `eprospera:entity.documents.read`       |

<Callout type="info">
  `/api/v1/me/natural-person/id_verification` (underscore) exists as a
  compatibility alias. Use `/id-verification` (hyphen) for new integrations.
</Callout>

## Authorization flow [#authorization-flow]

* `response_type=code` is required.
* `state` is required.
* `nonce` is required when `openid` is in scope.
* `response_mode` is optional (`query`, `fragment`, or `form_post`; default `query`).
* `prompt`, `max_age`, and `login_hint` are supported — see the [authorization endpoint reference](/reference/oauth-authorize).
* Every authorization response includes the `iss` parameter (RFC 9207); validate it against `https://portal.eprospera.com`.
* Unauthenticated users are redirected to the portal login before returning to the authorization request.

## Refresh tokens [#refresh-tokens]

Access tokens expire after 1 hour.

If the granted scope set includes `offline_access`, the token response also includes a `refresh_token` valid for up to 180 days.

Example refresh request:

```bash
curl -X POST https://portal.eprospera.com/api/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=$REFRESH_TOKEN"
```

<Callout type="warn">
  Refresh tokens rotate on every successful refresh. Always persist the newest
  `refresh_token` from the response. You may request a reduced scope set on
  refresh, provided it is a subset of the originally granted scopes. When
  `openid` is granted, the refresh response also includes a fresh `id_token`.
</Callout>

## Framework guides [#framework-guides]

* [Integration with NextAuth.js](/oauth-nextauth)
* [Integration with Firebase Authentication](/oauth-firebase)
