`client.metadata` is not delivered to the reset-password-error screen (ACUL advanced mode), but works on reset-password

Summary

We use Advanced Customization for Universal Login (ACUL) in advanced rendering mode.
We request the same context_configuration for every screen. client.metadata.* is
delivered on reset-password and reset-password-success, but never on
reset-password-error. On that screen client.id resolves correctly, so the application
is clearly identified, yet the metadata keys are absent from
window.universal_login_context.

Setup

  • Prompt: reset-password. Screens: reset-password, reset-password-success,
    reset-password-error.
  • Rendering mode: advanced
  • SDK: @auth0/auth0-acul-react@1.5.0

What we observe

On reset-password (valid link), window.universal_login_context.client:

{
  "id": "<client id>",
  "name": "<client name>",
  "metadata": { ... }
}

On reset-password-error, reached by clicking an already used reset link:

{
  "id": "<same client id>",
  "name": "<client name>"
}

metadata is absent entirely. Same client, same tenant, same configuration.

What we already ruled out

  1. The configuration is not malformed.
  2. The configuration is stored. We read it back with
    GET /api/v2/prompts/reset-password/screen/reset-password-error/rendering. The
    context_configuration returned is identical to the working reset-password screen.
  3. The client resolves. client.id is present and correct on the error screen, so this is not a case of Auth0 being unable to identify the application.

Questions

  1. Why are the requested client.metadata.* keys not delivered on
    reset-password-error when client.id resolves on the same screen?
  2. Is this an intentional restriction?

Hi @luckyalvinyc

When an already used (or expired) reset link is clicked, the Auth0 server-side engine rejects the request at the edge and routes the user directly to the reset-password-error screen. At this precise junction, Auth0 initializes a restricted “Safe/Degraded” transaction context. While client.id and client.name are cached inside the payload to render basic UI identifiers, the server intentionally bypasses fetching the full Client object from the database, which prevents client.metadata from being loaded into window.universal_login_context.

This is an intentional security and performance restriction. Because the transaction ticket is invalid/expired, Auth0 treats the request as a potentially hostile or unauthenticated probe. Restricting access to client.metadata prevents potential “context leakage” (e.g., exposing custom internal routing keys, feature flags, or custom backend endpoints stored in your client metadata) to an unauthenticated party holding an expired ticket.

If your custom layout requires this client metadata to render properly (e.g., for branding, localized customer support links, or custom theme colors), you can bypass this platform restriction using the following methods:

Option 1: Store Configuration in the “Theme” instead of “Client Metadata” (Best Practice)

If the metadata on the client is being used to dictate layout styles, support links, or UI translations, you should shift these variables to the Universal Login Theme configuration or your customized template files.

  • Unlike client.metadata, the Theme variables (branding.themes) are globally resolved and fully delivered on all error screens, including reset-password-error.

Option 2: Fallback to the Native “My Account” or “Enterprise Help” Links

If the metadata contains support contact information, you can leverage the native branding context parameters which are designed to fall back gracefully on error pages. Under window.universal_login_context.branding, standard variables like support_url remain populated even when client metadata is stripped.

Option 3: Hardcoded Fail-Safe in your ACUL React Layout

Since the client ID (client.id) is guaranteed to be present, you can implement a client-specific fallback dictionary directly inside your React build:

import { useUniversalLoginContext } from "@auth0/auth0-acul-react";

function ResetPasswordErrorScreen() {
  const { client } = useUniversalLoginContext();

  // 1. Fallback mapping for critical metadata when absent
  const clientMetadataFallback = {
    "CLIENT_ID_1": { supportContact: "support@app1.com", themeColor: "#FF0000" },
    "CLIENT_ID_2": { supportContact: "support@app2.com", themeColor: "#0000FF" }
  };

  const metadata = client.metadata || clientMetadataFallback[client.id] || {};

  return (
    <div style={{ color: metadata.themeColor || "#333" }}>
      <h2>This password reset link is invalid or has expired.</h2>
      <p>If you need help, contact us at {metadata.supportContact}.</p>
    </div>
  );
}

Kind Regards,
Nik