DEV Community

Cover image for 🪖 Setup a production grade keycloak instance on docker
Justin
Justin

Posted on

🪖 Setup a production grade keycloak instance on docker

princess bride: begin

Stand up a keycloak instance w/ username: admin, password: pa55word. With postgres as a database. And SSL forwarding (for use with like cloudflare).

Create this docker-compose file i.e. docker-compose.yml

version: '3'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres
      volumes:
        - postgres_data:/var/lib/postgresql/data
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
  keycloak:
      image: quay.io/keycloak/keycloak:latest
      environment:
        DB_VENDOR: POSTGRES
        DB_ADDR: postgres
        DB_DATABASE: keycloak
        DB_USER: keycloak
        DB_SCHEMA: public
        DB_PASSWORD: password
        KEYCLOAK_USER: admin
        KEYCLOAK_PASSWORD: Pa55w0rd
        PROXY_ADDRESS_FORWARDING: 'true'
        # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
        #JDBC_PARAMS: "ssl=true"
      ports:
        - 8010:8080
      depends_on:
        - postgres
Enter fullscreen mode Exit fullscreen mode

Then run docker-compose up -d.

With that in place, you just need to

  • Make a new realm
  • Make a new client
  • Add a valid redirect uri to the client (from the application you are wishing to secure)

Then you can add the following type of logic for authentication on your browser facing application

var keycloak;
function init() {

    keycloak = new Keycloak({
        url: 'https://[key-cloak-instance]/auth',
        realm: '[your-realm]',
        clientId: '[your-client]'
    });

    keycloak.init({
        checkLoginIframe: false
    }).success(async function (auth) {
        if (auth) {
            var name;
            if (keycloak.tokenParsed['family_name'] || keycloak.tokenParsed['given_name']) {
                name = keycloak.tokenParsed['given_name'] + ' ' + keycloak.tokenParsed['family_name']
            } else {
                name = keycloak.tokenParsed.preferred_username;
            }
            hide('login')

            // Set axios header for auth
            axios.defaults.headers.common['Authorization'] = `Bearer ${keycloak.token}`;
        } else {
            console.info('Not Authenticated');
            window.location = await keycloak.login();
        }
    })
}

function show(id) {
    document.getElementById(id).classList.remove('hide');
    document.getElementById(id).classList.add('show');
}

function hide(id) {
    document.getElementById(id).classList.add('hide');
    document.getElementById(id).classList.remove('show');
}

window.onhashchange = init;
window.onload = init;
Enter fullscreen mode Exit fullscreen mode

Then on your server (nodejs/express in this case), add the following logic:

const jwt = require('express-jwt');
const { data: response } = await axios.get('https://[keycloak-instance]/auth/realms/[your-realm]');
const secret = `-----BEGIN PUBLIC KEY-----\r\n${response.public_key}\r\n-----END PUBLIC KEY-----`;
const jwtMiddleware = jwt({ secret, algorithms: ['RS256'] });
Enter fullscreen mode Exit fullscreen mode

And then you can either add the middleware globally

app.use(jwtMiddleware)
Enter fullscreen mode Exit fullscreen mode

Or add it to a specific route

app.post('/api/endpoint', jwtMiddleware, async (req, res) => {
  return 'something-secure';
});
Enter fullscreen mode Exit fullscreen mode

Now good luck to those pesky people that were previously trying to storm your castle!

princess bride: bye boys! have fun storming the castle

Discussion (0)