In OAuth 2.0 Resource Server mode, LuciadFusion validates a bearer token on every incoming request. Clients obtain a token from the identity provider and include it in each request they send to the server. The LuciadFusion Studio web application uses the Authorization Code + PKCE flow for this.
For an explanation of how this mode compares to OAuth 2.0 Login, see OAuth 2.0 authentication in LuciadFusion.
Prerequisites
|
The prerequisites below describe what LuciadFusion requires from your identity provider. For general client setup such as enabling the Authorization Code flow or assigning roles to users, refer to your identity provider’s documentation. |
-
A registered OAuth 2.0 / OIDC client application in your identity provider.
-
The redirect URI registered with your identity provider must be:
<base-url>/studio/index.html
For example,https://luciadfusion.example.com/studio/index.html. -
If the API console is enabled (
fusion.studio.rest.apiConsoleEnabled: true), also register<base-url>/api/console/oauth2-redirect.htmlas a redirect URI.
For example,https://luciadfusion.example.com/api/console/oauth2-redirect.html. -
The post-logout redirect URI registered with your identity provider must be:
<base-url>/studio/index.html
For example,https://luciadfusion.example.com/studio/index.html.
Configure OAuth 2.0 Resource Server
Set the authentication type
In the LuciadFusion configuration file, set fusion.security.authenticationTypes to oauth2ResourceServer:
fusion.security:
authenticationTypes:
- oauth2ResourceServer
|
When using OAuth 2.0 authentication, leave the |
|
|
For more information about the LuciadFusion configuration file, see Configuring LuciadFusion Studio.
Configure the LuciadFusion Studio OIDC client
LuciadFusion Studio uses a built-in OIDC client to authenticate the user against the identity provider and obtain a bearer
token.
Configure it using the fusion.studio.security.oidc-client.* properties.
The full list of available properties is documented in application-fusion.production-template.yml.
fusion:
studio:
security:
oidc-client:
authority: https://<keycloak-host>/realms/<your-realm>
client-id: <your-client-id>
Configure token validation
LuciadFusion needs to know how to validate the bearer tokens it receives. You provide this information by setting standard Spring Boot OAuth 2.0 Resource Server properties. Refer to the Spring Security OAuth 2.0 Resource Server documentation for the full list of available properties and customization beans.
LuciadFusion supports two token formats:
-
JWT tokens: are validated locally, making request processing faster.
-
opaque tokens: require LuciadFusion to contact the identity provider for every request, but support immediate token revocation.
JWT tokens example
Supply the issuer URI so that LuciadFusion can fetch the identity provider’s OIDC discovery document to verify token signatures:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://<keycloak-host>/realms/<your-realm>
Alternatively, if your identity provider does not expose a discovery document, specify the JWK Set URI directly:
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://<keycloak-host>/realms/<your-realm>/protocol/openid-connect/certs
Opaque tokens example
Supply the introspection URI and client credentials so that LuciadFusion can validate tokens by contacting the identity provider:
spring:
security:
oauth2:
resourceserver:
opaquetoken:
introspection-uri: https://<keycloak-host>/realms/<your-realm>/protocol/openid-connect/token/introspect
client-id: <your-introspection-client-id>
client-secret: <your-introspection-client-secret>
|
This client is only used to authenticate the introspection request itself. It is not necessarily the same as the OIDC client configured above for the Authorization Code + PKCE flow. Depending on how your IdP is set up, these might need to be different clients with different client IDs. |
Customize JWT
Configure the username
By default, the sub claim is used as the username, which is often an opaque UUID rather than a human-readable name.
If you want to use a different claim instead, such as preferred_username, provide a custom JwtAuthenticationConverter bean.
Check your IdP’s documentation for the correct claim name in your setup.
See the Spring Security JWT documentation for more details.
@Configuration
public class OAuth2ResourceServerConfiguration {
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME);
return converter;
}
}
To make LuciadFusion pick up this configuration, add its package to the scan path:
fusion.config.additionalScanPackages: <your.package>
Role mapping
LuciadFusion supports role-based access control.
If you use this, provide a custom JwtAuthenticationConverter bean to map identity provider roles to LuciadFusion roles.
If you also need to configure the username, you can combine both in the same bean.
See the Spring Security JWT documentation for more details.
This example handles Keycloak, which places realm roles under the realm_access.roles claim:
@Configuration
public class OAuth2ResourceServerConfiguration {
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
Map<String, Object> realmAccess = jwt.getClaim("realm_access");
if (realmAccess != null) {
List<String> roles = (List<String>) realmAccess.get("roles");
if (roles != null) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
}
return List.of();
});
return converter;
}
}
To make LuciadFusion pick up this configuration, add its package to the scan path:
fusion.config.additionalScanPackages: <your.package>
Customize opaque tokens
Configure the username
By default, the sub claim is used as the username, which is often an opaque UUID rather than a human-readable name.
If you want to use a different claim instead, such as preferred_username, provide a custom OpaqueTokenIntrospector bean.
Check your IdP’s documentation for the correct claim name in your setup.
See the Spring Security opaque token documentation for more details.
@Configuration
public class OAuth2ResourceServerConfiguration {
@Bean
public OpaqueTokenIntrospector introspector() {
OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector
.withIntrospectionUri("https://<keycloak-host>/realms/<your-realm>/protocol/openid-connect/token/introspect")
.clientId("<your-introspection-client-id>")
.clientSecret("<your-introspection-client-secret>")
.build();
return token -> {
OAuth2AuthenticatedPrincipal principal = delegate.introspect(token);
String name = principal.getAttribute("preferred_username");
return new DefaultOAuth2AuthenticatedPrincipal(name, principal.getAttributes(),
List.copyOf(principal.getAuthorities()));
};
}
}
To make LuciadFusion pick up this configuration, add its package to the scan path:
fusion.config.additionalScanPackages: <your.package>
Role mapping
LuciadFusion supports role-based access control.
If you use this, provide a custom OpaqueTokenIntrospector bean to map identity provider roles to LuciadFusion roles.
If you also need to configure the username, you can combine both in the same bean.
See the Spring Security opaque token documentation for more details.
This example handles Keycloak, which places realm roles under the realm_access.roles claim:
@Configuration
public class OAuth2ResourceServerConfiguration {
@Bean
public OpaqueTokenIntrospector introspector() {
OpaqueTokenIntrospector delegate = SpringOpaqueTokenIntrospector
.withIntrospectionUri("https://<keycloak-host>/realms/<your-realm>/protocol/openid-connect/token/introspect")
.clientId("<your-introspection-client-id>")
.clientSecret("<your-introspection-client-secret>")
.build();
return token -> {
OAuth2AuthenticatedPrincipal principal = delegate.introspect(token);
Collection<GrantedAuthority> authorities = null;
Map<String, Object> realmAccess = principal.getAttribute("realm_access");
if (realmAccess != null) {
List<String> roles = (List<String>) realmAccess.get("roles");
if (roles != null) {
authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
}
return new DefaultOAuth2AuthenticatedPrincipal(principal.getName(), principal.getAttributes(), authorities);
};
}
}
To make LuciadFusion pick up this configuration, add its package to the scan path:
fusion.config.additionalScanPackages: <your.package>