{"record":{"id":"ccb6602e1b4c962f","repo":"apereo/cas","slug":"service-definition-does-not-request-a-pairwis","errorCode":null,"errorMessage":"Service definition [{}] does not request a pairwise subject type","messagePattern":"Service definition \\[(.+?)\\] does not request a pairwise subject type","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-oidc-services/src/main/java/org/apereo/cas/services/PairwiseOidcRegisteredServiceUsernameAttributeProvider.java","lineNumber":59,"sourceCode":"@EqualsAndHashCode(callSuper = true)\n@Setter\npublic class PairwiseOidcRegisteredServiceUsernameAttributeProvider extends BaseRegisteredServiceUsernameAttributeProvider {\n\n    @Serial\n    private static final long serialVersionUID = 469929103943101717L;\n\n    private PersistentIdGenerator persistentIdGenerator = new OidcPairwisePersistentIdGenerator();\n\n    @Override\n    public String resolveUsernameInternal(final RegisteredServiceUsernameProviderContext context) {\n        if (context.getRegisteredService() == null || !OidcRegisteredService.class.isAssignableFrom(context.getRegisteredService().getClass())) {\n            LOGGER.warn(\"Service definition [{}] is undefined or it's not an OpenId Connect relying party\", context.getRegisteredService());\n            return context.getPrincipal().getId();\n        }\n        val oidcSvc = (OidcRegisteredService) context.getRegisteredService();\n        if (StringUtils.isBlank(oidcSvc.getSubjectType())\n            || Strings.CI.equals(OidcSubjectTypes.PUBLIC.getType(), oidcSvc.getSubjectType())) {\n            LOGGER.warn(\"Service definition [{}] does not request a pairwise subject type\", oidcSvc);\n            return context.getPrincipal().getId();\n        }\n        val sectorIdentifier = getSectorIdentifier(oidcSvc);\n        val id = this.persistentIdGenerator.generate(context.getPrincipal(), sectorIdentifier);\n        LOGGER.debug(\"Resolved username [{}] for pairwise access\", id);\n        return id;\n    }\n\n    private static @Nullable String getSectorIdentifier(final OidcRegisteredService client) {\n        if (StringUtils.isNotBlank(client.getSectorIdentifierUri())) {\n            val uri = UriComponentsBuilder.fromUriString(client.getSectorIdentifierUri()).build();\n            return uri.getHost();\n        }\n        val uri = UriComponentsBuilder.fromUriString(client.getServiceId()).build();\n        return uri.getHost();\n    }\n\n}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-services/src/main/java/org/apereo/cas/services/PairwiseOidcRegisteredServiceUsernameAttributeProvider.java#L41-L77","documentation":"The same provider logs this warning when the OidcRegisteredService exists but its subjectType is blank or 'public'. Pairwise subject identifiers are only generated for services with subjectType 'pairwise'; otherwise the provider falls back to the principal's plain id.","triggerScenarios":"A client requests pairwise subject behavior (or is wired with PairwiseOidcRegisteredServiceUsernameAttributeProvider) but the OidcRegisteredService.subjectType is unset or set to 'public'; resolveUsernameInternal is called during ID token/sub claim generation.","commonSituations":"Forgetting to set subjectType=pairwise on the service JSON while expecting pairwise sub values; services created via management app defaulting to public; a request with specific subject type while service is public.","solutions":["Set \"subjectType\": \"pairwise\" on the OidcRegisteredService definition.","Confirm the sector identifier setup (sectorIdentifierUri or redirect URI host) so persistent IDs are stable.","If public subjects are intended, switch to a non-pairwise username provider to remove the warning."],"exampleFix":"// before\n\"subjectType\": \"public\"\n// after\n\"subjectType\": \"pairwise\"","handlingStrategy":"validation","validationCode":"if (oidcSvc == null || oidcSvc.getSubjectType() == null\n        || \"public\".equalsIgnoreCase(oidcSvc.getSubjectType())) {\n    LOGGER.warn(\"Pairwise provider needs subjectType=pairwise on service {}\", oidcSvc == null ? null : oidcSvc.getName());\n}","typeGuard":"boolean requestsPairwise(OidcRegisteredService svc) {\n    return svc != null && \"pairwise\".equalsIgnoreCase(svc.getSubjectType());\n}","tryCatchPattern":null,"preventionTips":["Always set subjectType=pairwise on services using the pairwise username provider","Configure a sectorIdentifierUri when multiple redirect URIs share one sector","Document the expected subject type per client in the service registry"],"tags":["oidc","pairwise","subject-type","config"],"backgroundTag":"invalid-config-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}