apereo/cas · warning
Dynamic client registration mode is not configured as…
Error message
Dynamic client registration mode is not configured as protected.
What it means
CAS's dynamic client registration endpoint can be protected by an initial access token. This controller (initial-access-token mode) first checks that cas.authn.oidc.registration.dynamic-client-registration-mode is 'PROTECTED'; if it is ANY or omitted, it refuses the operation with HTTP 406 NOT_ACCEPTABLE because initial access tokens are meaningless in unprotected modes.
Solutions
- Set cas.authn.oidc.registration.dynamic-client-registration-mode=PROTECTED
- Obtain and supply a valid initial access token with the registration request
- If open registration is intended, use the endpoint/mode matching ANY and do not require an initial token
- Confirm the registered-mode flow via discovery metadata (registration_endpoint)
Example fix
// before cas.authn.oidc.registration.dynamic-client-registration-mode=ANY // after cas.authn.oidc.registration.dynamic-client-registration-mode=PROTECTED
Defensive patterns
Strategy: validation
Validate before calling
if (registrationMode !== 'PROTECTED') {
throw new Error('Initial access token registration requires dynamic-client-registration-mode=PROTECTED');
}
if (!initialAccessToken) throw new Error('Missing initial access token for protected registration'); Type guard
function supportsInitialAccessToken(cfg) {
return cfg?.authn?.oidc?.registration?.dynamicClientRegistrationMode === 'PROTECTED';
} Try / catch
try {
return await registerClient(token);
} catch (e) {
if (e.status === 406) {
// set mode to PROTECTED or use the open registration flow
}
throw e;
} Prevention
- Set dynamic-client-registration-mode=PROTECTED before using initial access tokens
- Provision the initial access token through CAS tooling
- Match the endpoint variant to the configured mode
- Re-check registration config after CAS upgrades
When it happens
Trigger: POST to the OIDC registration endpoint while the initial-access-token controller is active and dynamicClientRegistrationMode is not PROTECTED (e.g. mode left unset, set to ANY).
Common situations: Deployers forgetting to set dynamic-client-registration-mode=PROTECTED after enabling registration; copying example configs with mode=ANY; upgrading CAS where default mode changed; hitting the wrong registration controller variant for the configured mode.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Unable to use 'none' for the user-info signing algorithm
- Unable to use 'none' as user-info encryption algorithm
- Service with client id is configured to encrypt tokens, yet…
- Unable to use 'none' as ID token signing algorithm
- Unable to use 'none' as ID token encryption algorithm
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/331d860a252173cd.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/dynareg/OidcInitialAccessTokenController.java:101
@GetMapping(value = {
'/' + OidcConstants.BASE_OIDC_URL + '/' + OidcConstants.REGISTRATION_INITIAL_TOKEN_URL,
"/**/" + OidcConstants.REGISTRATION_INITIAL_TOKEN_URL
}, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Handle OIDC initial access token request")
public ModelAndView handleRequestInternal(
final HttpServletRequest request, final HttpServletResponse response) {
val webContext = new JEEContext(request, response);
if (!getConfigurationContext().getIssuerService().validateIssuer(webContext, List.of(OidcConstants.REGISTRATION_INITIAL_TOKEN_URL))) {
val body = OAuth20Utils.getErrorResponseBody(OAuth20Constants.INVALID_REQUEST, "Invalid issuer");
val modelAndView = new ModelAndView(new JacksonJsonView(), body);
modelAndView.setStatus(HttpStatus.BAD_REQUEST);
return modelAndView;
}
val casProperties = getConfigurationContext().getCasProperties();
val oidcProperties = casProperties.getAuthn().getOidc();
if (!oidcProperties.getRegistration().getDynamicClientRegistrationMode().isProtected()) {
LOGGER.warn("Dynamic client registration mode is not configured as protected.");
return getBadRequestResponseEntity(HttpStatus.NOT_ACCEPTABLE);
}
val callContext = new CallContext(webContext, getConfigurationContext().getSessionStore(),
getConfigurationContext().getOauthConfig().getProfileManagerFactory());
return accessTokenClient.getCredentials(callContext)
.map(credentials -> accessTokenClient.validateCredentials(callContext, credentials))
.filter(Optional::isPresent)
.map(Optional::get)
.map(credentials -> {
val principal = FunctionUtils.doUnchecked(() -> PrincipalFactoryUtils.newPrincipalFactory().createPrincipal(credentials.getUserProfile().getId()));
val service = getConfigurationContext().getWebApplicationServiceServiceFactory()
.createService(casProperties.getServer().getPrefix());
val tokenRequestContext = AccessTokenRequestContext
.builder()
.authentication(DefaultAuthenticationBuilder.newInstance().setPrincipal(principal).build())
.service(service)
.grantType(OAuth20GrantTypes.NONE)View on GitHub (pinned to e7288fc434)