Skip to main content

Configuration

provideAuth() takes two arguments:

provideAuth(feature, config?)
ArgumentTypePurpose
featureAuthFeatureThe adapter — withKeycloak({ … }), withFakeAuth({ … }), or your own
configAuthConfigurationProvider-agnostic options. Optional; every field has a default

The split is the point: anything provider-specific belongs in the adapter's own configuration, not here. Keycloak's realm, PKCE method and role sources are arguments to withKeycloak() (see the Keycloak guide). What remains in AuthConfiguration is behaviour the core owns regardless of who issues the tokens.

Everything at a glance

provideAuth(withKeycloak({ url, realm, clientId }), {
forbiddenRoute: '/forbidden',
bearer: {
urlPattern: /^\/api(\/.*)?$/,
methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
},
});

Those are the defaults, written out. Omitting config entirely gives you exactly this.

OptionTypeDefault
forbiddenRoutestring | null'/forbidden'
bearerBearerTokenConfiguration | falsesee below

forbiddenRoute

Where authGuard sends an authenticated user who lacks the roles a route requires.

provideAuth(withKeycloak({ ... }), { forbiddenRoute: '/no-access' });

Note the distinction: an unauthenticated user is sent to the identity provider's login page, never here. This route is for the case where signing in again wouldn't help — the user is who they say they are and still isn't allowed in.

Make sure the path you name has a route, or the redirect lands on your wildcard route.

Refusing without navigating

Pass null to block activation and stay put, letting the application decide what to do:

provideAuth(withKeycloak({ ... }), { forbiddenRoute: null });

The guard then returns false. Navigation is cancelled and the user remains on the current page, which is useful when a route is reached from a menu you'd rather not have appeared at all — pair it with hiding the link via auth.hasRole().

null is meaningful, undefined is not

provideAuth() treats an explicit null as "no redirect" and only falls back to /forbidden when the field is absent. Passing undefined gets you the default.

bearer

Which outgoing requests receive an Authorization: Bearer … header from authTokenInterceptor.

provideAuth(withKeycloak({ ... }), {
bearer: {
urlPattern: /^https:\/\/api\.example\.com(\/.*)?$/i,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
});
FieldTypeDefault
urlPatternRegExp/^\/api(\/.*)?$/ — same-origin, relative /api/* requests
methodsreadonly HttpMethod[]every method

HttpMethod is 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'.

Both fields must match for the header to be attached. A request that fails either one goes out untouched.

This is an allowlist, and it fails closed

A pattern that matches nothing attaches no token — it does not fall back to attaching one everywhere. That's the safe direction to fail: a missing header produces a 401 you'll notice in development, whereas a too-broad pattern silently ships your access token to whatever third-party host you happen to call.

Match on a full origin (/^https:\/\/api\.example\.com/) rather than a bare path fragment when your API isn't same-origin.

Disabling it entirely

Pass false when a different layer owns outgoing authorisation:

provideAuth(withKeycloak({ ... }), { bearer: false });

The interceptor then attaches nothing, even if it's registered. AuthService.getToken() still works, so you can sign requests yourself.

The g and y flags are stripped

If you pass a pattern with the global or sticky flag, provideAuth() removes it:

bearer: {
urlPattern: /\/api\//g;
} // stored as /\/api\//

Both flags make RegExp.test() stateful through lastIndex, so the same pattern would match every other request. A token silently missing from half your calls is a miserable bug to track down, so it's prevented rather than documented as a caveat.

Where configuration ends up

provideAuth() resolves your options against the defaults and provides the result as AUTH_CONFIG. The guard and the interceptor read it from there.

const config = inject(AUTH_CONFIG); // ResolvedAuthConfiguration

In the resolved shape, bearer: false has become bearer: null, and both bearer fields are required rather than optional. See Injection tokens.

Runtime configuration

Values that aren't known at build time — a realm resolved per tenant, a URL from a config endpoint — need the adapter to be constructed after that value is available. Because withKeycloak() is a plain function call, this is just ordinary JavaScript:

src/main.ts
const runtime = await fetch('/config.json').then((r) => r.json());

bootstrapApplication(App, {
providers: [
provideAuth(
withKeycloak({
url: runtime.keycloakUrl,
realm: runtime.realm,
clientId: runtime.clientId,
}),
),
provideHttpClient(withInterceptors([authTokenInterceptor])),
],
});

Fetching before bootstrapApplication() keeps it simple, at the cost of delaying bootstrap by one request. Serve config.json from the same origin so it isn't a cross-origin round trip on the critical path.

Next