Configuring Bearer Authentication with Keycloak for Swagger UI
In this blog post, we'll walk through configuring Swagger UI to use Bearer Authentication with Keycloak, focusing on both Authorization Code and Client Credentials flows. This guide will help you secure your API documentation and ensure that only authenticated users can access your APIs.
Introduction
Swagger UI is a powerful tool for documenting and interacting with RESTful APIs. When working with secured APIs, it's essential to configure Swagger UI to handle authentication. In this tutorial, we'll set up Bearer Authentication with Keycloak, a popular open-source Identity and Access Management solution, for Swagger UI using the springdoc-openapi-starter-webflux-ui
package in a Spring Boot WebFlux application.
1. Setting Up Keycloak
Before configuring Swagger UI, ensure you have Keycloak set up:
Create a Keycloak Realm:
Log in to the Keycloak admin console.
Create a new realm or use an existing one.
Create a Keycloak Client:
Go to the "Clients" section.
Click "Create" and configure your client:
Client ID: Your application's client ID.
Client Protocol:
openid-connect
.Access Type: For Authorization Code flow, set it to
confidential
. For Client Credentials flow, also useconfidential
.
Configure Client Credentials:
- Under the "Credentials" tab, note down the
Client Secret
.
- Under the "Credentials" tab, note down the
Set Redirect URIs (if required):
- For Authorization Code flow, add the redirect URIs as needed.
2. Configure Swagger UI with Bearer Authentication
Authorization Code Flow
For user authentication via OAuth2 Authorization Code flow:
Update OpenApiConfig
Class:
package com.example.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
@OpenAPIDefinition(
info = @Info(
contact = @Contact(
name = "Example User",
email = "user@example.com"
),
description = "API documentation",
title = "API Documentation",
version = "1.0",
license = @License(
name = "License Name",
url = "https://example.com"
),
termsOfService = "Terms of Service"
),
servers = {
@Server(
description = "Local Environment",
url = "http://localhost:8080"
)
},
security = {
@SecurityRequirement(
name = "keycloak"
)
}
)
@SecurityScheme(
name = "keycloak",
type = SecuritySchemeType.OAUTH2,
in = SecuritySchemeIn.HEADER,
description = "Keycloak OAuth2 Authorization Code flow",
flows = @OAuthFlows(
authorizationCode = @OAuthFlow(
authorizationUrl = "http://localhost:8080/auth/realms/your-realm/protocol/openid-connect/auth",
tokenUrl = "http://localhost:8080/auth/realms/your-realm/protocol/openid-connect/token"
)
)
)
public class OpenApiConfig {
}
Client Credentials Flow
For server-to-server authentication using OAuth2 Client Credentials flow:
Update OpenApiConfig
Class:
package com.example.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
@OpenAPIDefinition(
info = @Info(
contact = @Contact(
name = "Example User",
email = "user@example.com"
),
description = "API documentation",
title = "API Documentation",
version = "1.0",
license = @License(
name = "License Name",
url = "https://example.com"
),
termsOfService = "Terms of Service"
),
servers = {
@Server(
description = "Local Environment",
url = "http://localhost:8080"
)
},
security = {
@SecurityRequirement(
name = "keycloak"
)
}
)
@SecurityScheme(
name = "keycloak",
type = SecuritySchemeType.OAUTH2,
in = SecuritySchemeIn.HEADER,
description = "Keycloak OAuth2 Client Credentials flow",
flows = @OAuthFlows(
clientCredentials = @OAuthFlow(
tokenUrl = "http://localhost:8080/auth/realms/your-realm/protocol/openid-connect/token"
)
)
)
public class OpenApiConfig {
}
3. Customize Unauthorized Responses
To provide a custom error response for unauthorized access:
Implement Custom Error Handling:
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class SecurityConfig {
@Bean
public ServerAuthenticationEntryPoint authenticationEntryPoint() {
return (exchange, ex) -> {
String expMessage = ex.getMessage() != null ? ex.getMessage() : "Unauthorized access - please provide valid credentials.";
byte[] responseBytes = String.format("{\"status\": %d, \"message\": \"%s\"}", HttpStatus.UNAUTHORIZED.value(), expMessage)
.getBytes(StandardCharsets.UTF_8);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
.bufferFactory()
.wrap(responseBytes)));
};
}
@Bean
public ServerAccessDeniedHandler accessDeniedHandler() {
return (exchange, denied) -> {
String expMessage = denied.getMessage() != null ? denied.getMessage() : "Access Denied";
byte[] responseBytes = String.format("{\"status\": %d, \"message\": \"%s\"}", HttpStatus.FORBIDDEN.value(), expMessage)
.getBytes(StandardCharsets.UTF_8);
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
.bufferFactory()
.wrap(responseBytes)));
};
}
}
Conclusion
By following these steps, you can configure Swagger UI to use Bearer Authentication with Keycloak for both Authorization Code and Client Credentials flows. Additionally, customizing unauthorized responses helps provide clear, user-friendly error messages. This setup enhances the security and usability of your API documentation.
Feel free to adapt these instructions based on your specific requirements and Keycloak setup. If you have any questions or run into issues, don't hesitate to reach out for further assistance.
Happy coding!
Subscribe to my newsletter
Read articles from Shohanur Rahman directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Shohanur Rahman
Shohanur Rahman
👋 Hey there! I’m Shohanur Rahman! I’m a backend developer with over 5.5 years of experience in building scalable and efficient web applications. My work focuses on Java, Spring Boot, and microservices architecture, where I love designing robust API solutions and creating secure middleware for complex integrations. 💼 What I Do Backend Development: Expert in Spring Boot, Spring Cloud, and Spring WebFlux, I create high-performance microservices that drive seamless user experiences. Cloud & DevOps: AWS enthusiast, skilled in using EC2, S3, RDS, and Docker to design scalable and reliable cloud infrastructures. Digital Security: Passionate about securing applications with OAuth2, Keycloak, and digital signatures for data integrity and privacy. 🚀 Current Projects I’m currently working on API integrations with Spring Cloud Gateway and designing an e-invoicing middleware. My projects often involve asynchronous processing, digital signature implementations, and ensuring high standards of security. 📝 Why I Write I enjoy sharing what I’ve learned through blog posts, covering everything from backend design to API security and cloud best practices. Check out my posts if you’re into backend dev, cloud tech, or digital security!