Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keycloak integration in Swagger

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?

like image 271
melistik Avatar asked Jan 29 '17 08:01

melistik


People also ask

Does Keycloak use OAuth2?

Keycloak is Open Source Identity and Access Management Server, which is a OAuth2 and OpenID Connect(OIDC) protocol complaint.

How do I get authorization code from Keycloak?

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.


1 Answers

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 realm
  • Scopes are something you can set to your needs
  • clientId is a client parametrized with implicit mode on keycloak realm
  • the additional parameter nonce 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 keycloak
  • basePackage: You need to set the package in which all your controller are
  • If you need an api-key, you can enable it and add it on the security scheme list
  • LoginEndpoint: that need to be the authorization endpoint of you keycloak realm
  • scopeItems: 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, ...

like image 107
wargre Avatar answered Oct 09 '22 07:10

wargre