Auth0
convex-angular has no dependency on @auth0/auth0-angular. You write a small service that
exposes Auth0’s state as signals, register it under the AUTH0_AUTH token, and
provideAuth0Auth() bridges it to Convex.
Auth0AuthProvider
Section titled “Auth0AuthProvider”interface Auth0AuthProvider { isLoading: Signal<boolean>; isAuthenticated: Signal<boolean>; getAccessTokenSilently(options?: { cacheMode?: 'on' | 'off' }): Promise<string | null>; error?: Signal<Error | undefined>;}| Member | Type | Required | Purpose |
|---|---|---|---|
isLoading |
Signal<boolean> |
yes | true while Auth0 initializes, false once auth state is known. Forwarded unchanged. |
isAuthenticated |
Signal<boolean> |
yes | Whether Auth0 reports the user as authenticated. Forwarded unchanged. |
getAccessTokenSilently |
(options?: { cacheMode?: 'on' | 'off' }) => Promise<string | null> |
yes | Returns the token for Convex, or null when signed out. |
error |
Signal<Error | undefined> |
no | Provider-owned errors, mirrored onto injectAuth().error(). |
AUTH0_AUTH
Section titled “AUTH0_AUTH”const AUTH0_AUTH: InjectionToken<Auth0AuthProvider>;Register your implementation under this token before calling provideAuth0Auth(). Prefer
useExisting over useClass when the service is also injected elsewhere, so Angular reuses the
singleton instead of constructing a second instance.
provideAuth0Auth()
Section titled “provideAuth0Auth()”function provideAuth0Auth(): EnvironmentProviders;It registers a CONVEX_AUTH factory that adapts AUTH0_AUTH, and then provideConvexAuth().
Token fetching
Section titled “Token fetching”forceRefreshToken maps to Auth0’s cacheMode:
| Convex asks for | Call made |
|---|---|
forceRefreshToken: false |
getAccessTokenSilently({ cacheMode: 'on' }) |
forceRefreshToken: true |
getAccessTokenSilently({ cacheMode: 'off' }) |
Unlike the Clerk adapter, there is no reauthVersion. Auth0 has no
equivalent of a session or organization identity that the adapter can watch, so auth setup re-runs
only when isLoading or isAuthenticated change. If your application needs to force a re-auth on
some other signal, implement a custom provider and expose your own
reauthVersion.
Example
Section titled “Example”import { ApplicationConfig, Injectable, InjectionToken, Signal, inject } from '@angular/core';import { AUTH0_AUTH, Auth0AuthProvider, provideAuth0Auth, provideConvex } from 'convex-angular';
/** * These docs do not depend on `@auth0/auth0-angular`, so the slice of its * `AuthService` used below is declared locally. In your application you inject * the real `AuthService` and bridge its observables with `toSignal(...)`. */interface Auth0Sdk { isLoading: Signal<boolean>; isAuthenticated: Signal<boolean>; getAccessTokenSilently(options: { detailedResponse: true; cacheMode?: 'on' | 'off' }): Promise<{ id_token: string }>;}
const AUTH0_SDK = new InjectionToken<Auth0Sdk>('AUTH0_SDK');
@Injectable({ providedIn: 'root' })export class Auth0AuthService implements Auth0AuthProvider { private readonly auth0 = inject(AUTH0_SDK);
readonly isLoading = this.auth0.isLoading; readonly isAuthenticated = this.auth0.isAuthenticated;
// Convex validates the OIDC **id token**, not the access token, so ask for // the detailed response and return `id_token`. async getAccessTokenSilently(options?: { cacheMode?: 'on' | 'off' }): Promise<string | null> { const response = await this.auth0.getAccessTokenSilently({ detailedResponse: true, cacheMode: options?.cacheMode, });
return response.id_token; }}
export const appConfig: ApplicationConfig = { providers: [ provideConvex('https://example-123.convex.cloud'), { provide: AUTH0_AUTH, useExisting: Auth0AuthService }, // Already includes provideConvexAuth(). Do not register that as well. provideAuth0Auth(), ],};In a real application the SDK binding comes from Auth0 directly:
import { toSignal } from '@angular/core/rxjs-interop';import { AuthService } from '@auth0/auth0-angular';Then read the state with injectAuth(), gate templates with
the auth directives, and protect routes with
the route guards.