Securing an enterprise application is a key aspect of modern enterprise application development. There are plenty of mechanisms to secure such applications, especially in distributed environments. Identity and Access Management (IAM) system is one of those very popular security components in a modern day distributed application. Concepts such as Identity Federation, Single Sign On (SSO) and Identity Provisioning are some of those key concepts, which are coupled with IAM systems.
Keycloak is a popular “open source” IAM solutions in the market, which is aimed at securing modern
enterprise applications and services. It makes it easy to secure applications and services with
little to no code [1].
An IAM solution means, it can provide authentication to your applications as well as helps
to manage the users and their access levels / roles / permissions. Furthermore, it has other
built-in features like User Federation, Single Sign On (SSO), Centralized Identity Management,
Standard Protocols, Client Adapters, Identity brokering and high performance.
Keycloak is completely a separate server that runs in a different place or a different port. Our
applications are configured to be secured by this independent server. Keycloak uses open protocol
standards like Open ID Connect or SAML 2.0, especially in Identity Federation and SSO scenarios.
In our browser, when we try to access applications, it redirects the browser from the
application to the Keycloak authentication server where they enter their credentials. This is
important because users are completely isolated from applications and applications never see user
credentials. Instead, applications are given an identity token or assertion that is
cryptographically signed. These tokens can identify information such as user name, address, email,
and other profile data. They can also hold permission data so that applications can make
authorization decisions. It can also be used to make secure invocations on REST-based services [1].
If the user credentials provided are correct, the Keycloak Identity Server generates a
token and gives it back to the application. If the token is valid, the user is able to continue
with the rest of the tasks. When it comes to the context of Microservices architecture, the role of
Keycloak is unimaginable with its facility of SSO.
One of Auxenta’s key Telco clients uses Keycloak as its IAM solution for one of their
Microservices environments. There are around 40 Microservices, which are federated through a
centralized Keycloak environment.
The following diagram shows the message flow in this system:
This is how a typical JWT, generated by the Keycloak server, looks like. We will notice that this JWT contains all the information about the application user and its Keycloak configurations.
{ "jti": "e994fc9e-e96e-49ac-ab71-7a2a917674df", "exp": 1533104336, // Wed Aug 01 2018 11:48:56 GMT+0530 (India Standard Time) "nbf": 0, "iat": 1533104036, // Wed Aug 01 2018 11:43:56 GMT+0530 (India Standard Time) "iss": "http://localhost:8080/auth/realms/Aplos", "aud": "aplos-frontend", "sub": "8562a2ac-ca92-4741-812a-3557d12da057", "typ": "Bearer", "azp": "aplos-frontend", "nonce": "0ffec321-b829-45e5-83cf-9ab205fc8103", "auth_time": 1533097554, // Wed Aug 01 2018 09:55:54 GMT+0530 (India Standard Time) "session_state": "2b70a773-42f9-4ebb-b8f6-135150f1486a", "acr": "0", "allowed-origins": [ "http://localhost:4000" ], "realm_access": { "roles": [ "offline_access", "uma_authorization" ] }, "resource_access": { "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }, "scope": "openid profile email", "email_verified": false, "name": "FSM MHD", "preferred_username": "fsm", "given_name": "FSM", "family_name": "MHD", "email": "fsm@aplos.com" }
Follow the steps given below in order to develop a simple Angular Spring Boot Microservice, which can be authenticated by a Keycloak server.
export const environment = { production: false, KEYCLOAK_URL: 'http://localhost:8080/auth', KEYCLOAK_REALM: 'test-realm', KEYCLOAK_CLIENTID: 'test-client', BACKEND_URL: 'http://localhost:8000/api/' };
Note that our backend root url is also provided here itself.
When the Keycloak.init() method is invoked, we can fetch the user details and the getToken()
method is invoked. Then it will update the token with the time period given in the configuration.
When the logout() method is invoked, we set the Keycloak authentication to null and will be
redirected to the login page of the respective Keycloak Realm.
When the hasRole(string array) is invoked, it will return a Boolean value by checking whether the
logged in user has the role or not.
This will extend the standard Angular Http service. Its purpose is to include the authorization token of a logged-in user to every single Http request automatically. That means every request made with the Http service will get the "Bearer" authorization header automatically.
@Injectable() export class KeycloakHttp extends Http { constructor(_backend: ConnectionBackend, _defaultOptions: RequestOptions, private _keycloakService: KeycloakService) { super(_backend, _defaultOptions); } request(url: string | Request, options?: RequestOptionsArgs): Observable<-Response-> { const tokenPromise: Promise<-string-> = this._keycloakService.getToken(); const tokenObservable: Observable<-string-> = Observable.fromPromise(tokenPromise); if (typeof url === 'string') { return tokenObservable .map(token => { const authOptions = new RequestOptions({ headers: new Headers({ Authorization: 'Bearer ' + token }) }); return new RequestOptions().merge(options).merge(authOptions); }) .concatMap(opts => super.request(url, opts)); } else if (url instanceof Request) { return tokenObservable .map(token => { url.headers.set('Authorization', 'Bearer ' + token); return url; }) .concatMap(request => super.request(request)); } } } export function keycloakHttpFactory(backend: XHRBackend, defaultOptions: RequestOptions, keycloakService: KeycloakService) { return new KeycloakHttp(backend, defaultOptions, keycloakService); } export const KEYCLOAK_HTTP_PROVIDER = { provide: Http, useFactory: keycloakHttpFactory, deps: [XHRBackend, RequestOptions, KeycloakService] };
This consists of the following methods: getAllStaff(), getAllStudents(), getStudentById(id)
and getStaffById(id)
Let’s assume that the staff resources can be accessed only by the admin users.
KeycloakService.init() .then(() => { if (KeycloakService.auth.loggedIn) { platformBrowserDynamic().bootstrapModule(AppModule); } else { KeycloakService.init(); } }) .catch((e: string) => { console.log('Error Logging : ' + e); });
Now we will call the init() method of the Keycloak service class and if the app has been
successfully logged in to, we will bootstrap our app to load in the browser, and if not
Keycloak will automatically redirect the browser to the default login page of the Keycloak
server.
Everything is now set in the Angular end.
@RestController @RequestMapping("/api") public class MyTestRestController { static MapstaffMap = new HashMap<>(); static Map studMap = new HashMap<>(); @RequestMapping(value = "staff/getAll", method = RequestMethod.GET) public List getAllStaff() { return new ArrayList (staffMap.values()); } @RequestMapping(value = "stud/getAll", method = RequestMethod.GET) public List getAllStud() { return new ArrayList (studMap.values()); } @RequestMapping(value = "staff/getStaffById", method = RequestMethod.GET) public String getStaffById(@RequestParam("staffId") Integer staffId) { return staffMap.get(staffId); } @RequestMapping(value = "stud/getStudById", method = RequestMethod.GET) public String getStudById(@RequestParam("studId") Integer studId) { return studMap.get(studId); } @PostConstruct public void init() { staffMap.put(1,"Staff 1"); staffMap.put(2,"Staff 2"); staffMap.put(3,"Staff 3"); staffMap.put(4,"Staff 4"); staffMap.put(5,"Staff 5"); studMap.put(1,"Stud 1"); studMap.put(2,"Stud 2"); studMap.put(3,"Stud 3"); studMap.put(4,"Stud 4"); studMap.put(5,"Stud 5"); } }
This class has the @PostConstruct method to initialize a data set of students and staff
Here we have added both spring security and Keycloak as dependencies so our Keycloak security configurations will work on top of the spring security. Let’s add the following class to configure the security:
@Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal( AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper( new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean public KeycloakSpringBootConfigResolver KeycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy( new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .antMatchers("/api/staff/*") .hasRole("admin") .anyRequest() .permitAll(); http.authorizeRequests() .antMatchers("/api/student/*") .hasRole("user") .anyRequest() .permitAll(); } }
Here we have extended the Keycloak Web Security ConfigurerAdapter and have overridden several methods to provide our own configuration. So if a particular user is requesting staff resources, then that user should be an admin. In addition, for student resource requests, the accessing user should have the user role.
server: port : 8001 keycloak: realm : Test-Realm auth-server-url : http://localhost:8080/auth ssl-required : external resource : test-client cors : true
In addition to the server port settings, we have to configure the Keycloak server properties as well in order to fetch information from the Keycloak server.
Now staff/ resources can be accessed only by the admin role users and the user should have the user role to access the student/ resources.
1. Keycloak official website: https://www.keycloak.org/