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, inject } from '@angular/core';import { toSignal } from '@angular/core/rxjs-interop';import { AUTH0_AUTH, Auth0AuthProvider, provideAuth0Auth, provideConvex } from 'convex-angular';import { Observable, firstValueFrom } from 'rxjs';
/** * These docs do not depend on `@auth0/auth0-angular`, so the slice of its * `AuthService` used below is declared locally, with the same shape: Observables, * not Signals or Promises. In your application you inject the real `AuthService` * (`inject(AuthService)`) instead of this token. */interface Auth0Sdk { isLoading$: Observable<boolean>; isAuthenticated$: Observable<boolean>; getAccessTokenSilently(options: { detailedResponse: true; cacheMode?: 'on' | 'off'; }): Observable<{ id_token: string } | undefined>;}
const AUTH0_SDK = new InjectionToken<Auth0Sdk>('AUTH0_SDK');
@Injectable({ providedIn: 'root' })export class Auth0AuthService implements Auth0AuthProvider { private readonly auth0 = inject(AUTH0_SDK);
readonly isLoading = toSignal(this.auth0.isLoading$, { initialValue: true }); readonly isAuthenticated = toSignal(this.auth0.isAuthenticated$, { initialValue: false });
// Convex validates the OIDC **id token**, not the access token, so ask for // the detailed response and return `id_token`. The SDK call returns an // Observable, so unwrap it with `firstValueFrom` before reading the token. async getAccessTokenSilently(options?: { cacheMode?: 'on' | 'off' }): Promise<string | null> { const response = await firstValueFrom( this.auth0.getAccessTokenSilently({ detailedResponse: true, cacheMode: options?.cacheMode, }), );
return response?.id_token ?? null; }}
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 your application, inject(AuthService) from @auth0/auth0-angular replaces inject(AUTH0_SDK); bridge its Observables with toSignal(...) and firstValueFrom(...) as above, because awaiting an Observable directly yields the Observable itself, not the token.
Then read the state with injectAuth(), gate templates with
the auth directives, and protect routes with
the route guards.