Building an Idle game Part 3 - Authentication
Tags: node, gamedev, keycloak, javascript
Published: 2020-03-26 15:42:04

> Welcome to my series on how to go about building an Idle game. This third episode focuses on authentication both in Vue and Node.

Authentication is a fun subject, there are a million ways to authenticate a user from the more basic user / password combination, to OAuth, OpenID and others. For our game we will be using [KeyCloak](https://www.keycloak.org/), it is an "Open source Identity and Access Management" application. For a lot of apps this may be overboard, but our use case requires it, so we will be taking advantage of it.

#### Why not use just a user/pass combo?

This is a great question and originally we did do this. However we have several applications that require a central authentication portal. Keycloak provides this, but not only this it also allows for very easy integration of social logins, access control and more.

#### What if I don't want to use KeyCloak?

That is fine. As we develop the authentication you will notice we use JWT tokens, so most of the code can be easily changed to remove the need for KeyCloak.

## Getting started

Make sure you have KeyCloak running before you start, there is a standalone version or a docker image, by default it runs on port `8080` and on `http://localhost/auth`.

## Environment Variables

The first new bit of tech we are going to go over is environment variables. Almost all languages have this concept to some degree - even frontend frameworks like [React](https://medium.com/@trekinbami/using-environment-variables-in-react-6b0a99d83cf5). The idea is that this `.env` file is specific to the user/server and is not shared, it is also ignored in git to prevent commiting the file. This file will contain all the applications secrets. For our app we use 4 variables

```
AUTH_REALM=divinity-world
AUTH_URL=http://localhost:8080/auth
AUTH_CLIENT_ID=game
AUTH_REDIRECT_URI=https://localhost:8081/play
```

The `AUTH_REALM` is the realm you created in KeyCloak, the URL being the url to auth, the `AUTH_CLIENT_ID` is the client id, by default Keycloak creates an `account` client and a few others, I created my own `game`. And lastly the `AUTH_REDIRECT_URI` is where you get redirected after authentication.


## JWKS and JWT's

There are 2 parts to authentication on the server side, the first part being the need to get the signing key from the server and the second being verification of the token, for this we use two libraries.

```
npm install jsonwebtoken
npm install jwks-rsa
```

Let's head into `cluster/child.js` now and declare those as constants

```js

const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
```

After this we need to create the jwks client, this is generated based on the `.env` variables above

```js
const jwksUri = `${process.env.AUTH_URL}/realms/${process.env.AUTH_REALM}/protocol/openid-connect/certs`;

const jwks = jwksClient({
    strictSsl: true, // Default value
    jwksUri: jwksUri
});

const kid = 'OHNidHJInGLWbWHanztSf8A8GDfnJVATENxKjchqvc0';
```

The last variable `kid` is the client id in Keycloak.

Next is actually protecting the socket from unauthorized requests

```js
...
io.on('connection', (socket) => {

    let token = socket.handshake.query.token;

    try {
        jwks.getSigningKey(kid, (err, key) => {
            const signingKey = key.getPublicKey();


            jwt.verify(token, signingKey, {
                azp: 'game',
                issuer: `${process.env.AUTH_URL}/realms/${process.env.AUTH_REALM}`,
                algorithms: ['RS256']
            }, function (err, decoded) {
                if (!err)
                    console.log("User authorized", decoded);
            });
        });
    } catch (e) {
        socket.disconnect()
    }

    ...

}
```

This little chunk gets the token from the query, we then attempt to get the signing key from Keycloak, once we have that we then verify the token against the `azp` and `issuer`. Is anything goes wrong, we kick them out of the socket.

## The frontend

The frontend doesn't require much code at all as we pull in two packages, one that handles the logic of tokens such as refreshing them. For this we use [vue-keycloak-js](https://github.com/dsb-norge/vue-keycloak-js) and also [vue-socket.io](https://github.com/MetinSeylan/Vue-Socket.io) which handles the logic of binding events to components. All of this lives inside `main.js` in the client side.

```js
Vue.use(VueKeyCloak, {
    config: {
        authRealm: 'divinity-world',
        authUrl: 'http://localhost:8080/auth',
        authClientId: 'game',
        redirectUri: "http://localhost:8081/play"
    },
    onReady: (keycloak) => {
        console.log({keycloak});


        Vue.use(new VueSocketIO({
            debug: true,
            connection: 'http://localhost:3994?token=' + keycloak.token,
        }));

        new Vue({
            router,
            render: h => h(App)
        }).$mount('#app')
    }
});
```

An improvement on this would be to also use [environment variables in Vue](https://cli.vuejs.org/guide/mode-and-env.html#environment-variables), you may need to change the config depending on your realms.

By wrapping the app in Vue Keycloak, if the user is not authenticated it will always redirect to the login page so it's quite seamless when it comes to integrating it into other applications.