Bartek Andrzejczak

Musings on software development

SSO for Your Single Page Application (Part 1/2 - AngularJS)

Basically every application needs some kind of authorization mechanism. That’s obvious. If we have some container at our disposal which serves HTML for every requested website, we can probably just enrich it with some library, change some XMLs and we’re basically done. Unfortunately that’s not the case with single page applications. Yes, we can still have a container, that will handle things on the backend (which we won’t in this tutorial, but let’s not jump to it yet), but there’s no such magic wand for the frontend. There are some libraries, that will help you in some particular cases, but when I was looking at the authorization needed for my current project, none of them were good enough, and I didn’t want to configure some big library just to do something that can be done in less then 50 lines of code. With that in mind, let me take you on a short journey of implementing SSO for the front-end written in AngularJS with the help of great SSO server called Keycloak.

Keycloak setup

I don’t want to get into details of Keycloak configuration, mostly because all of this is already on the web in one form or another. What I’ve used while setting up my Keycloak are mostly videos by Bill Burke mentioned in official Keycloak documentation as The Basics screencast. I’m just gonna cover what’s absolutely necessary for this tutorial.

Keycloak comes in with a WildFly application server build right in, so you can just start it using /bin/standalone.sh script. After you’re done with this you should be able to see a welcome page at http://localhost:8080/auth. You then need to enter administrative console, login with admin/admin credentials and change admin password. You should see a screen similar to the one below.

For the purpose of this post we’re not even going to create our own realm, which is a must when preparing authorization for a production application. Instead, we’re going to use master realm, which is Keycloak’s default realm. To use it in our application we need to do just one thing - create a client. To do that you should go to clients list via menu on the left, and click Create. There are just two mandatory fields in the form that pops out, and a few more which need some clarification:

  • Client ID - an identifier which will be used everywhere where we want to use that client.
  • Access Type
    • confidential - default access type - will redirect to an authorization form (in our case simple login form) every time someone unauthorized tries to obtain a token. We’ll use that type for front-end authorization.
    • public - anyone can access.
    • bearer-only - only requests bearing valid access token can be authorized. It’s a very simple client, which we’ll use for authorizing our request on the backend.
  • Valid Redirect URIs - after user is authorized via Keycloak, he’ll be redirected back to our application. Authorization request will include redirect URI, which needs to be a part of this valid redirect URIs list. Otherwise, the user won’t be able to authorize himself. We can add here the root URL of our application, which in my case is http://localhost:8000/*.

When you click save, you’ll be redirected to client details page, where there are just two things left to do. One is adding a Web Origin to the client so we don’t bump into Cross-Origin issues. In our case it should be just http://localhost:8000. The other is to go to the Installation tab, choose a desired format (in our case that’s gonna be JSON) and save the output for later. We’re going to use that JSON in our client application, so it knows where to authorize its users.

Application

Depending on your application, you can choose one of two forms of authorization:

  • Authorization on demand - When some parts of your application are available for the general public and only some are restricted, you’ll probably want to develop it in a way that authorization is not necessary in the whole application and only some requests are going to redirect you to the login page.
  • Automatic authorization - When you’re working with some internal application which doesn’t have any public facing interface, you’ll want user to be authorized before even reaching your application. That’s the kind we’re going to deal with here.

With automatic authorization and AngularJS it gets kinda tricky. You absolutely don’t want to have any requests sent before user is authorized, because they’re going to fail with 401 Unauthorized, so we cannot use a regular service initialized in run function. What we can do instead, we can delay Angular’s initialization, so user is already initialized when Angular starts. How can we do that? When Angular library is loaded in the browser, it looks for DOM element with ng-app attribute on it. If it doesn’t find one, that’s that. It won’t initialize Angular. But we do want Angular initialized, right? Right. That’s why Angular has angular.bootstrap method, which can bootstrap our application on demand. More on Angular initialization can be found in the docs.

The first thing we want to do, is to get the user to authorize and receive his user data. Keycloak uses OAuth2, but we don’t need to know it at all (although it’s recommended these days). Why is that? Because it comes with JavaScript library that will do all the OAuth2 magic for us. It can be obtained in a few different ways:

  • When Keycloak server is running it can be obtained straight from the server. It lays under localhost:8080/auth/js/keycloak.js.
  • You can get keycloak.js from your Keycloak server or from the official website and put it in your project.
  • You can get it from bower via bower install --save keycloak.

After keycloak.js is loaded we’ve got everything we need to perform an authorization. Here’s what’s going to happen after source files are loaded into the browser:

  1. Keycloak library is initialized, which leads to redirect to Keycloak login form
  2. User logs in putting credentials into the form (if he’s already logged in, this step is skipped)
  3. Keycloak redirects user back to the application with the special code in URL’s query parameters (?code=...)
  4. Keycloak library is initialized again, reads the code from URL and makes a request to Keycloak to obtain access token
  5. Access token comes back, so now we can use it to fetch user’s data from Keycloak
  6. User data come back, so we can finally initialize AngularJS

Let’s first look at steps 1 and 4, which are pretty much one thing - Keycloak initialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var keycloakConfig = {
    "url": "http://localhost:8080/auth",
    "realm": "master",
    "clientId": "frontend",
    "credentials": {
        "secret": "1a1b98b6-66c5-4384-a4b4-7361717e773e"
    }
};
var keycloak = Keycloak(keycloakConfig);
keycloak.init({
    onLoad: 'login-required'
}).success(function () {
    //User has logged in
});

As you can probably spot, keycloakConfig is a little different, than what we’ve got from Keycloak JSON, but we can surely transform one into another, by renaming auth-server-url to just url, resource to clientId and leaving credentials as it is. Everything else can go.

Thanks to the line 10 of the code above, Keycloak will be initialized with the configuration saying, that user needs to log in before accesing the website. That will trigger either request to get access token if the code is included in request URL or the redirect to login form if it’s not. The nice thing about Keycloak’s JavaScript library is that it’s promise-based. Here init() returns a promise, which can either succeed, if the user is logged in, or fail if authorization fails (which could be due to communication failure, because Keycloak won’t redirect back to the application unless the authorization succeeds).

Let’s now look at fetching data of logged in user, which is point 5 on our list.

1
2
3
keycloak.loadUserInfo().success(function (userInfo) {
    // Bootstrap Angular
});

That’s fairly simple. We call another method on Keycloak and get another promise.

Now comes the most important part. With the user authorized and user data obtained we must put it all into the Angular machine so it’s available on runtime. We may want to check user’s groups, display name and email address or perform a logout. It may be all stored in a service, but one thing is important - this service needs to be registered before bootstrap happens. The thing with by-hand bootstrap in Angular is, that after the bootstrap happens, you cannot register any new controllers, services or directives. In this example, we’ll do what we have to do in Angular’s run function. With that said, let’s look at the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function bootstrapAngular(keycloak, userInfo) {
    angular.module('KeycloakApp')
        .run(function ($rootScope) {
            // store the token in header's defaults or a cookie - we'll cover that with the backend in part 2

            $rootScope.currentUser = {
                username: userInfo.name || userInfo.preferred_username,
                roles: keycloak.realmAccess.roles
            };

            $rootScope.userLogout = function () {
                keycloak.logout();
            };
        });
    angular.bootstrap(document, ['KeycloakApp']);
}

In the bootstrapAngular function, we close over Keycloak object and userInfo, to put it into Angular’s root scope after it runs, and then we bootstrap Angular to the root of the DOM tree, which is represented by document here. It’s almost as we’ve put ng-app on the opening <html> tag. We also need to pass the name of the application we want to bootstrap. You may want to research Keycloak object capabilities on your own to see what you can get out of it. The Keycloak JS adapter docs are here.

As an epilogue let’s look at the HTML for this test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html ng-cloak>
    <head>
        <meta charset="utf-8">
        <title>Keycloak Test</title>
    </head>
    <body ng-cloak>
        <h1>
            Hello, {{ currentUser.username }}
        </h1>
        <a ng-click="userLogout()" href="#">Logout</a>

        <script src="https://code.angularjs.org/1.4.7/angular.min.js"></script>
        <script src="/keycloak.js"></script>
        <script src="/app.js"></script>
    </body>
</html>

As you can see it’s a very simple code, which will display user’s username (which is always defined, because guests are restricted from the realm of application) and a Logout link, which will end user’s session and redirect him to the login form.

I hope you’ve enjoyed this simple tutorial and learned from it how easy it is to set up single sign-on in a single page application like this one. If you have any questions regarding this topic don’t hesitate to ask them a comment section below. Of course the whole source code for this tutorial is available at GitHub: https://github.com/bandrzejczak/keycloak-angular-akka-http.

Stay tuned for part two, where I’ll show you how to validate the requests at the server side using Scala and akka-http!

Comments