quarkusio/quarkus · error · OIDCException

OidcProviderClient can not be injected

Error message

OidcProviderClient can not be injected

What it means

produceProviderClient() resolves the OidcProviderClient from the SecurityIdentity's TENANT_ID_ATTRIBUTE (default tenant, static tenant, or dynamic tenant). If the attribute is missing or maps to no known tenant, the client is null and OIDCException 'OidcProviderClient can not be injected' is thrown.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcConfigurationAndProviderProducer.java:51

    @Produces
    @RequestScoped
    OidcProviderClient produceProviderClient() {
        OidcProviderClient client = null;
        String tenantId = OidcUtils.getAttribute(identity, OidcUtils.TENANT_ID_ATTRIBUTE);
        if (tenantId != null) {
            if (OidcUtils.DEFAULT_TENANT_ID.equals(tenantId)) {
                return tenantConfig.getDefaultTenant().getOidcProviderClient();
            }
            TenantConfigContext context = tenantConfig.getStaticTenant(tenantId);
            if (context == null) {
                context = tenantConfig.getDynamicTenant(tenantId);
            }
            if (context != null) {
                client = context.getOidcProviderClient();
            }
        }
        if (client == null) {
            throw new OIDCException("OidcProviderClient can not be injected");
        }
        return client;
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Only inject OidcProviderClient in endpoints secured by OIDC (enforce authentication, e.g. quarkus.http.auth.permission policies) so TENANT_ID_ATTRIBUTE is set.
  2. Look up the client explicitly via TenantConfigBean (getDefaultTenant()/getStaticTenant(tenantId)) instead of injecting the produced bean.
  3. Ensure tenant names in configuration match those recorded in existing sessions after refactors; restart users through login if tenant ids changed.

Example fix

// before
@Inject OidcProviderClient client; // null on anonymous endpoints

// after
@Inject TenantConfigBean tenantConfig;
OidcProviderClient client = tenantConfig.getStaticTenant("hr").getOidcProviderClient();
Defensive patterns

Strategy: type-guard

Validate before calling

String tenantId = OidcUtils.getAttribute(identity, OidcUtils.TENANT_ID_ATTRIBUTE);
if (tenantId == null || tenantConfig.getStaticTenant(tenantId) == null) {
    // endpoint not secured by OIDC or tenant no longer exists - avoid injecting
}

Type guard

boolean hasProviderClient(SecurityIdentity identity, TenantConfigBean cfg) {
    String tenantId = OidcUtils.getAttribute(identity, OidcUtils.TENANT_ID_ATTRIBUTE);
    if (tenantId == null) return false;
    if (OidcUtils.DEFAULT_TENANT_ID.equals(tenantId)) return true;
    return cfg.getStaticTenant(tenantId) != null || cfg.getDynamicTenant(tenantId) != null;
}

Try / catch

try {
    return client.introspect(token).await().indefinitely();
} catch (OIDCException e) {
    if (e.getMessage().contains("OidcProviderClient")) {
        // request has no OIDC identity; respond 401 instead of 500
    }
    throw e;
}

Prevention

When it happens

Trigger: Inject OidcProviderClient in a request where SecurityIdentity lacks a TENANT_ID_ATTRIBUTE (unauthenticated or non-OIDC identity), or the attribute references a tenant not registered in tenantConfig (tenant removed/renamed while a session references the old id).

Common situations: Injecting OidcProviderClient into endpoints that allow anonymous access or use another IdentityProvider; stale sessions for a tenant deleted from configuration; using dynamic tenants resolved lazily so no client is bound yet at injection time.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/187c59aa95fada57. Report an issue: GitHub.