> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Universal Portals Architecture

> Learn how a Universal Portal signs users in, gets access tokens for resource servers, handles step-up authentication and Forms, and manages sessions.

> [!WARNING]
>
> **The Auth0 Universal Portals feature is in .**

A Portal is a server-side application that Auth0 hosts. The portal server signs users in, keeps their tokens, and calls resource servers for them. The browser never sees an access token or a refresh token. It holds only a secure session cookie.

The following sections explain the actors in a Portal, how users sign in, how components call resource servers, and how step-up authentication, Forms, and sessions work.

To create your first Portal, read [Universal Portals Quickstart](https://auth0.com/docs/customize/portals/quickstart.md). To open a Portal from your application, read [Access Portals](https://auth0.com/docs/customize/portals/access-portals.md). To configure the portal application, read [Configure an Application for Universal Portals](https://auth0.com/docs/customize/portals/configure-portal-application.md).

## Actors

* **Browser**: Shows the Portal and the components.
* **Portal server**: Signs users in, stores tokens, and proxies component calls to the resource servers.
* **Auth0**: The authorization server that signs users in and issues tokens.
* **[My Account API](https://auth0.com/docs/manage-users/my-account-api.md) and [My Organization API](https://auth0.com/docs/manage-users/my-organization-api.md)**: The resource servers that components call.
* **Forms backend**: Runs [Auth0 Forms](https://auth0.com/docs/customize/forms.md) and their Flows.

```mermaid theme={null}
flowchart LR
    browser["Browser"] -- "Session cookie" --> portal["Portal server"]
    portal -- "Sign-in, token requests" --> auth0["Auth0"]
    portal -- "Access token per call" --> apis["My Account API<br/>My Organization API"]
    browser -- "Form submissions" --> forms["Forms backend"]
```

## Access with single sign-on

When a user opens the portal URL, the portal server starts an authorization code flow with [Proof Key for Code Exchange (PKCE)](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce.md). If the user has an Auth0 session, Auth0 returns an authorization code without a login prompt. The portal server exchanges the code for an ID token, an access token, and a refresh token, and stores them. It then sets the session cookie.

```mermaid theme={null}
sequenceDiagram
    participant B as Browser
    participant P as Portal server
    participant A as Auth0

    B->>P: Open portal URL
    P-->>B: Redirect to /authorize
    B->>A: GET /authorize
    Note over A: User has an Auth0 session
    A-->>B: Redirect to /portals/auth/callback with code
    B->>P: GET /portals/auth/callback
    P->>A: Exchange code for tokens
    A-->>P: ID token, access token, refresh token
    Note over P: Tokens stay on the server
    P-->>B: Session cookie and portal page
```

If the user has no Auth0 session, Auth0 shows the login page before it returns the code.

## Access with a session transfer token

A [native application](https://auth0.com/docs/get-started/auth0-overview/create-applications/native-apps.md) can pass a session transfer token in the portal URL. The portal server forwards the token to `/authorize`. Auth0 validates the token, signs the user in without a prompt, and returns the code, as in single sign-on (Single Sign-On (SSO): Service that, after a user logs into one application, automatically logs them into other applications as well. Single Logout works similarly in reverse.). A session transfer token works once and expires after 60 seconds. If Auth0 rejects the token, the user sees the login page.

```mermaid theme={null}
sequenceDiagram
    participant N as Native application
    participant B as In-app browser
    participant P as Portal server
    participant A as Auth0

    N->>A: Exchange refresh token for session transfer token
    A-->>N: Session transfer token
    N->>B: Open portal URL with the token
    B->>P: GET portal URL
    P-->>B: Redirect to /authorize with the token
    B->>A: GET /authorize
    A-->>B: Redirect to callback with code
    B->>P: GET callback
    P->>A: Exchange code for tokens
    P-->>B: Session cookie and portal page
```

To implement this scenario, read [Open a Portal from a native application](https://auth0.com/docs/customize/portals/access-portals.md#open-a-portal-from-a-native-application).

## Component calls to a resource server

A component never calls a resource server from the browser. It sends the request to the portal server at `/portals/{slug}/proxy/...`, with the audience and scope it needs. The portal server adds an access token and forwards the request to the API.

### Access token minting

Portals always require a refresh token (Refresh Token: Special kind of token that can be used to obtain a renewed Access Token. It is useful for renewing expiring Access Tokens without forcing the user to log in again.). The portal server uses it as the main mechanism to mint access tokens for each resource server that your components call. It relies on a multi-resource refresh token (MRRT) to pivot between audiences, so the user signs in once. The Portal reuses an access token until it expires, and then mints a new one.

The portal application needs two settings for each resource server: a client grant that allows the scopes, and a refresh token policy that allows the exchange. To learn more, read [Multi-Resource Refresh Token](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token.md).

```mermaid theme={null}
sequenceDiagram
    participant B as Browser
    participant P as Portal server
    participant A as Auth0
    participant R as Resource server

    B->>P: Request to /portals/{slug}/proxy/...
    alt The portal has a valid access token for the audience and scope
        Note over P: Reuse the access token
    else No valid access token
        P->>A: Refresh token grant for the audience and scope
        A-->>P: Access token
    end
    P->>R: Request with the access token
    R-->>P: Response
    P-->>B: Response
```

### Allowed resource servers

Component calls go only to the My Account API and the My Organization API of your tenant, and only to the ones that the portal application has a client grant for. The recommended configuration grants both. The portal server rejects a request for any other audience with `403`.

### Rate limits

If the My Account API returns `429 Too Many Requests` while a [multi-factor authentication (MFA)](https://auth0.com/docs/secure/multi-factor-authentication.md) or passkey component loads data, the Portal replaces the component with a "Too many requests" message and a retry button.

## Step-up authentication

Whether an operation requires MFA depends on your tenant's policies and Actions. For example, imagine that your tenant has this post-login Action. It requests an MFA challenge when a refresh token exchange asks for the My Account API with the scope to delete an authentication method. The request hostname gives the domain, so the Action works with your Auth0 domain or a custom domain:

```javascript wrap lines theme={null}
exports.onExecutePostLogin = async (event, api) => {
    const { protocol } = event.transaction ?? {};
    const { hostname, body } = event.request ?? {};

    const isRefreshTokenExchange = protocol === "oauth2-refresh-token";
    const isMyAccountApi = body?.audience === `https://${hostname}/me/`;
    const deletesAuthenticator = body?.scope?.includes("delete:me:authentication_methods");

    if (isRefreshTokenExchange && isMyAccountApi && deletesAuthenticator) {
        api.authentication.challengeWithAny([{ type: "email" }, { type: "otp" }]);
    }
};
```

When the Portal requests an access token for that audience and scope, Auth0 runs the Action, which asks the user to choose a factor, and responds with `mfa_required`. The Portal starts the MFA challenge in a popup and retries the original request after the user completes it. To learn more, read [Action Triggers: post-login - API object](https://auth0.com/docs/actions/reference/post-login/post-login-api-object.md).

```mermaid theme={null}
sequenceDiagram
    participant B as Browser
    participant P as Portal server
    participant A as Auth0
    participant R as My Account API

    B->>P: Delete an authenticator
    P->>A: Request an access token with the delete scope
    A-->>P: mfa_required
    P-->>B: Start the MFA challenge
    B->>A: Complete the MFA challenge
    A-->>P: Access token with the delete scope
    P->>R: Retry the request with the access token
    R-->>P: Response
    P-->>B: Response
```

## Forms submission

An [Auth0 Form](https://auth0.com/docs/customize/forms.md) renders inside the portal page, but its requests go from the browser to the Forms backend, not through the portal server. The Forms backend runs the Flow. If the Flow calls an API, it uses credentials from a Forms vault connection. The API is a resource server, typically the Management API, but it can be any API.

```mermaid theme={null}
sequenceDiagram
    participant B as Browser
    participant F as Forms backend
    participant R as Resource server

    B->>F: Submit the Form
    F->>F: Run the Flow
    F->>F: Read credentials from the vault
    F->>R: Call the API with the credentials
    R-->>F: Response
    F-->>B: Form response and completion message
```

The Form receives its context from the user's ID token when the Portal loads. To learn more, read [Forms context variables](https://auth0.com/docs/customize/portals/portal-components.md#forms-context-variables).

## Sessions and token lifetimes

A Portal has two sessions and two kinds of tokens:

* Auth0 session: Set by Auth0 when the user signs in. Your tenant's session settings control it.
* Portal session: The Portal's session cookie. It ends after 1 hour of inactivity or 1 day after sign-in, whichever comes first.
* Refresh token: The portal server's credential for minting access tokens. Every API call depends on it, so set its lifetimes to match the portal session.
* Access token: Minted when a component needs it and reused until it expires. Set its lifetime on the resource server according to your security policy.

When the portal session ends, the Portal redirects the user to Auth0 to sign in again.

```mermaid theme={null}
flowchart LR
    ended["Portal session ends"] --> check{"Is the Auth0 session<br/>still active?"}
    check -- "Yes" --> silent["The user continues<br/>without a prompt"]
    check -- "No" --> login["The user sees<br/>the login page"]
```

If the refresh token expires before the portal session, API calls fail until the user signs in again. To avoid this, use the lifetimes in [Configure an Application for Universal Portals](https://auth0.com/docs/customize/portals/configure-portal-application.md#recommended-configuration).

## Next steps

> **[Universal Portals Quickstart](https://auth0.com/docs/customize/portals/quickstart.md)**
>
> Create your first Portal in minutes.

> **[Configure an Application for Universal Portals](https://auth0.com/docs/customize/portals/configure-portal-application.md)**
>
> Set the client grants and refresh token policies that access token minting needs.

> **[Access Portals](https://auth0.com/docs/customize/portals/access-portals.md)**
>
> Send users from your applications to the Portal.

> **[Troubleshoot Universal Portals](https://auth0.com/docs/customize/portals/troubleshoot-portals.md)**
>
> Fix sign-in, session, and API errors.
