[thumbnail target=”_self” src=”https://sumglobal.com/wp-content/uploads/2016/05/AngularKeycloakWildfly.png”]
In previous posts, we have:
- Used vagrant to create a development environment
- Secured REST services with Keycloak
You can find the code on our GitHub site: https://github.com/sumglobal
Now let’s use Keycloak and AngularJS to create a secure web application
Once the REST services are secured, we will use Keycloak’s javascript adapter to secure the web application.
Configure a client through the keycloak admin console.
[thumbnail target=”_blank” src=”https://sumglobal.com/wp-content/uploads/2016/05/Keycloak_Admin_Console-1024×501.png”]
When configuring the client, we will select public
for the Access Type
field. Since we can’t use a client secret, you will also need to specify valid redirect uris. Once configured, click on the Installation
tab and create a keycloak.json
file that will be used to configure the client. If you’ve followed the previous posts, this will already be setup for you.
The keycloak.json
file will look something like:
1 2 3 4 5 6 7 8 |
{ realm: "serenity", realm-public-key: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXk...", auth-server-url: "https://172.16.0.100/auth", ssl-required: "external", resource: "serenity-web", public-client: true } |
Next, we need to initialize the adapter inside our web application code like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
(function() { 'use strict'; angular.element(document).ready(function () { var keycloakAuth = new Keycloak('keycloak.json'); keycloakAuth.init({ onLoad: 'check-sso' }).success(function () { angular .module('cortex') .factory('Auth', function() { return keycloakAuth; }); angular.bootstrap(document, ["cortex"]); }).error(function () { window.location.reload(); }); }); angular .module('cortex') .run(['$rootScope', '$location', 'Auth', runBlock]); /** @ngInject */ function runBlock($rootScope, $location, Auth) { $rootScope.$on( "event:auth-loginRequired", function() { Auth.login(); }); } })(); |
notice that we are passing check-sso
to the init function. This will redirect to the auth server to check to see if the user is already logged in.
After logging in, our application will be able to make REST calls using bearer token authentication. In our angular application, we use interceptors to inject the bearer token into the headers as such:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
function authInterceptor($q, Auth) { return { request: function (config) { if (Auth.token) { var deferred = $q.defer(); Auth.updateToken(30).success(function() { config.headers = config.headers || {}; config.headers.Authorization = 'Bearer ' + Auth.token; deferred.resolve(config); }).error(function() { location.reload(); }); return deferred.promise; } else { return config; } } }; } function errorInterceptor($rootScope, $q) { return { responseError: function(response) { if (!response.config.ignoreAuthModule) { switch (response.status) { case 0: // cors issue case 401: var deferred = $q.defer(); $rootScope.$broadcast('event:auth-loginRequired', response); return deferred.promise; case 403: $rootScope.$broadcast('event:auth-forbidden', response); break; } } return $q.reject(response); } }; } |
The authInterceptor
method takes an HTTP request and adds a bearer token to the Authorization
header after verifying and updating the token. The errorInterceptor
method takes an HTTP response to check for error codes. If an HTTP 401
is returned, a loginRequired
event is broadcast. If an HTTP 403
is returned, an auth-forbidden
event is broadcast meaning the user does not have sufficient privileges to access the service.
After starting the application, you should get a screen similar to:
[thumbnail target=”_blank” src=”https://sumglobal.com/wp-content/uploads/2016/05/hogwarts-1024×303.png”]
Clicking the User
or Cargo
links will redirect the application to the keycloak login page as such:
[thumbnail target=”_blank” src=”https://sumglobal.com/wp-content/uploads/2016/05/Log_in_to_serenity-1024×536.png”]
Once logged in, selecting the all seeing eye
in the bottom right corner will display the current token value
[thumbnail target=”_blank” src=”https://sumglobal.com/wp-content/uploads/2016/05/hogwarts-1-1024×815.png”]
Wrapping Up
There you have it, in the last three posts we have:
- Created a reusable development environment using Vagrant and Ansible
- Used Ansible to download and install
- Wildfly
- Keycloak
- OpenLdap
- MySql
- Nginx
- Used Ansible to
- Load OpenLdap data into the server
- Load schema and test data into MySql
- Tuned and loaded database drivers for Wildfly
- Setup a working realm for Keycloak
- Setup SSL and proxying with Nginx
- Used Angular, Node, Npm, Bower and Gulp to create a test web application
Using these tools, we have been able to create reusable, scriptable and stable development environments that can be shared and used as a platform for development on multiple operating systems.
Stijn de Witt says
> case 0: // cors issue
Yes, how to solve that CORS issue?
This hack / workaround in the client is not a real solution.
Caused by WildFly not activating filters (that add cors headers) for ‘errors’, such as 401. http://stackoverflow.com/questions/31806351/servlet-response-filter-for-non-2xx-http-code
But how to solve?