I want to be able to log in via Swagger UI using Keycloak and Spring Boot Security. So far, i can log in with bearer generated token via postman, but how to configure user credentials login?
Security config:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private static final String[] AUTH_WHITELIST = {"/swagger-resources", "/swagger-resources/**", "/configuration/ui",
"/configuration/security", "/swagger-ui.html", "/webjars/**", "/v3/api-docs/**", "v3/api-docs",
"/api/public/**", "/api/public/authenticate", "/actuator/*", "/swagger-ui/**", "/api-docs/**"};
private final JwtAuthConverter jwtAuthConverter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable());
http.authorizeHttpRequests(auth -> auth.requestMatchers(AUTH_WHITELIST).permitAll().anyRequest().authenticated());
http.oauth2ResourceServer(o2 -> o2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter)));
http.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
}
Converterer:
@Component
public class JwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
@Value("${jwt.auth.converter.principle_attribute}")
private String principleAttribute;
@Value("${jwt.auth.converter.resource-id}")
private String resourceId;
@Override
public AbstractAuthenticationToken convert(@NonNull Jwt jwt) {
Collection<GrantedAuthority> authorities = Stream.concat(jwtGrantedAuthoritiesConverter.convert(jwt).stream(), extractResourceRoles(jwt).stream()).collect(Collectors.toSet());
return new JwtAuthenticationToken(jwt, authorities, getPrincipleClaimName(jwt));
}
private String getPrincipleClaimName(Jwt jwt) {
String claimName = JwtClaimNames.SUB;
if (principleAttribute != null) {
claimName = principleAttribute;
}
return jwt.getClaim(claimName);
}
private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
Map<String, Object> resourceAccess;
Map<String, Object> resource;
Collection<String> resourceRoles;
if (jwt.getClaim("resource_access") == null) {
return Set.of();
}
resourceAccess = jwt.getClaim("resource_access");
if (resourceAccess.get(resourceId) == null) {
return Set.of();
}
resource = (Map<String, Object>) resourceAccess.get(resourceId);
resourceRoles = (Collection<String>) resource.get("roles");
return resourceRoles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toSet());
}
}
Swagger config:
@Configuration
public class SwaggerConfig {
private static final String OAUTH_SCHEME_BEARER = "bearerAuth";
@Bean
public OpenAPI customizeOpenAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement()
.addList(OAUTH_SCHEME_BEARER))
.components(new Components()
.addSecuritySchemes(OAUTH_SCHEME_BEARER, new SecurityScheme()
.name(OAUTH_SCHEME_BEARER)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
}
Any suggestions or tutorial links, would be welcoming, I tried what I could find, but with no luck.
I had to re-write my swagger config, and it looks like this:
private static final String OAUTH_SCHEME = "auth";
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
String authURL;
@Bean
public OpenAPI customizeOpenAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement()
.addList(OAUTH_SCHEME))
.components(new Components()
.addSecuritySchemes(OAUTH_SCHEME, createOAuthScheme()))
.addSecurityItem(new SecurityRequirement().addList(OAUTH_SCHEME));
}
private SecurityScheme createOAuthScheme() {
return new SecurityScheme().type(SecurityScheme.Type.OAUTH2).flows(createOAuthFlows());
}
private OAuthFlows createOAuthFlows() {
final var oauthFlow = new OAuthFlow()
.authorizationUrl(authURL + "/protocol/openid-connect" + "/auth")
.refreshUrl(authURL + "/protocol/openid-connect" + "/token")
.tokenUrl(authURL + "/protocol/openid-connect" + "/token")
.scopes(new Scopes());
return new OAuthFlows().authorizationCode(oauthFlow);
}
I also had to add some configurations in my application.yml
server:
port: 8081
keycloak:
username: test-client
password: *****
realm: test-realm
uri: http://localhost:8080
auth-server-url: ${keycloak.uri}/realms/${keycloak.realm}/protocol/openid-connect
springdoc:
swagger-ui:
oauth:
clientId: ${keycloak.username}
clientSecret: ${keycloak.password}
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${keycloak.uri}/realms/${keycloak.realm}
jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
client:
registration:
keycloak:
authorization-grant-type: client_credentials
scope: openid
provider:
keycloak:
issuer-uri: ${keycloak.uri}/realms/${keycloak.realm}
jwt:
auth:
converter:
resource-id: ${keycloak.username}
principle_attribute: preferred_username
It works now, by redirecting to keycloak logging page, i'm also pre-filling client id and secret in properties to make it easier.