I have a Keycloak protected backend that I would like to access via swagger-ui. Keycloak provides the oauth2 implicit and access code flow, but I was not able to make it work. Currently, Keycloak's documentation is lacking regarding which url should be used for authorizationUrl and tokenUrl within swagger.json.
Each realm within Keycloak offers a huge list of configuration urls by accessing http://keycloak.local/auth/realms/REALM/.well-known/openid-configuration
Furthermore I've tried to directly integrate the keycloak js-client within swagger-ui index.html by adding the following lines:
<script src="keycloak/keycloak.js"></script> <script> var keycloak = Keycloak('keycloak.json'); keycloak.init({ onLoad: 'login-required' }) .success(function (authenticated) { console.log('Login Successful'); window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + keycloak.token, "header")); }).error(function () { console.error('Login Failed'); window.location.reload(); } ); </script>
I also tried something like this after 'Login Successful'
swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + keycloak.token, "header"));
But it also doesn't work.
Any suggestions how I can integrate keycloak auth within swagger?
Keycloak is Open Source Identity and Access Management Server, which is a OAuth2 and OpenID Connect(OIDC) protocol complaint.
You access the keycloak login page using a client id and a redirect url to your application. Once the login is successful, the keycloak server redirects to your app again, providing the authorization code appended in the url itself.
Swagger-ui can integrate with keycloak using the implicit
authentication mode. You can setup oauth2 on swagger-ui so that it will ask you to authenticate instead of giving swagger-ui the access token directly.
1st thing, your swagger need to reference a Security definition like:
"securityDefinitions": { "oauth2": { "type":"oauth2", "authorizationUrl":"http://172.17.0.2:8080/auth/realms/master/protocol/openid-connect/auth", "flow":"implicit", "scopes": { "openid":"openid", "profile":"profile" } } }
Then, you swagger-ui need to reference some other parameter: With the pure js, you can use in the index.html
const ui = SwaggerUIBundle({ ...} ); ui.initOAuth({ clientId: "test-uid", realm: "Master", appName: "swagger-ui", scopeSeparator: " ", additionalQueryStringParams: {"nonce": "132456"} })
In this code,
authorizationUrl
is the authorization endpoint on your keycloak realmclientId
is a client parametrized with implicit
mode on keycloak realmnonce
should be random, but swagger-ui don't use it yet.I add here an example if you want to do all this on Spring-boot:
On this framework, you will mainly use swagger and swagger-ui web-jar from Springfox. This is done by adding the dependencies:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency>
Swagger is enable by adding the annotation swagger2
on your main class:
@SpringBootApplication @EnableSwagger2 public class TestSpringApplication { ...
then you can setup a Configuration
class like this:
@Configuration public class SwaggerConfigurer { @Bean public SecurityConfiguration securityConfiguration() { Map<String, Object> additionalQueryStringParams=new HashMap<>(); additionalQueryStringParams.put("nonce","123456"); return SecurityConfigurationBuilder.builder() .clientId("test-uid").realm("Master").appName("swagger-ui") .additionalQueryStringParams(additionalQueryStringParams) .build(); } @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.example.testspring")) .paths(PathSelectors.any()) .build().securitySchemes(buildSecurityScheme()).securityContexts(buildSecurityContext()); } private List<SecurityContext> buildSecurityContext() { List<SecurityReference> securityReferences = new ArrayList<>(); securityReferences.add(SecurityReference.builder().reference("oauth2").scopes(scopes().toArray(new AuthorizationScope[]{})).build()); SecurityContext context = SecurityContext.builder().forPaths(Predicates.alwaysTrue()).securityReferences(securityReferences).build(); List<SecurityContext> ret = new ArrayList<>(); ret.add(context); return ret; } private List<? extends SecurityScheme> buildSecurityScheme() { List<SecurityScheme> lst = new ArrayList<>(); // lst.add(new ApiKey("api_key", "X-API-KEY", "header")); LoginEndpoint login = new LoginEndpointBuilder().url("http://172.17.0.2:8080/auth/realms/master/protocol/openid-connect/auth").build(); List<GrantType> gTypes = new ArrayList<>(); gTypes.add(new ImplicitGrant(login, "acces_token")); lst.add(new OAuth("oauth2", scopes(), gTypes)); return lst; } private List<AuthorizationScope> scopes() { List<AuthorizationScope> scopes = new ArrayList<>(); for (String scopeItem : new String[]{"openid=openid", "profile=profile"}) { String scope[] = scopeItem.split("="); if (scope.length == 2) { scopes.add(new AuthorizationScopeBuilder().scope(scope[0]).description(scope[1]).build()); } else { log.warn("Scope '{}' is not valid (format is scope=description)", scopeItem); } } return scopes; } }
There is a lot of thing you can update in this code. This is mainly the same as before:
nonce
which should be a random thing (swagger-ui don't use it yet)clientId
which you need to setup accordingly to the client you setup in keycloakbasePackage
: You need to set the package in which all your controller areLoginEndpoint
: that need to be the authorization endpoint of you keycloak realmscopeItems
: the scopes you want for this authentication.It will generate the same thing as before: Updating the swagger to add the securityDefinition and make swagger-UI take the parameter for clientId, nonce, ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With