Is there a way to login into the application through Keycloak programmatically using cypress/JS?

Viewed 5789

I writing automated tests in Cypress for a web application that is protected by Keycloak. I have managed to retrieve the jwt token from keycloak, but I don't know what to do with it. I have seen that after you login into keycloak, you are redirected to your domain and keyclock sets cookies, local storage, etc. Is there a way to get the same results programmatically as keyclock does when you login in their page? With other words, have something like:

cy.getToken().then((token) => {
   cy.login(token);
   cy.visit(myDomain)
})
2 Answers

You can use the plugin cypress-keycloak to achieve this.

After installing go to cypress/support/commands.js and write:

// Using ES6
import 'cypress-keycloak';

OR

// using CommonJS
require('cypress-keycloak');

Then in your tests you can write (Example taken from cypress-keycloak npm page) :

describe('thing', () => {
  beforeEach(() => {
    cy.login({
      root: 'https://keycloak.babangsund.com',
      realm: 'stage',
      username: 'babangsund',
      password: 'bacon',
      client_id: 'frontend',
      redirect_uri: 'https://babangsund.com/',
    });

    // or login with OTP
    cy.loginOTP({
      root: 'https://keycloak.babangsund.com',
      realm: 'stage',
      username: 'babangsund',
      password: 'bacon',
      client_id: 'frontend',
      redirect_uri: 'https://babangsund.com/',
      otp_secret: 'OZLDC2HZKM3QUC...', // e.g. 32 chars
      otp_credential_id: '5e231f20-8ca7-35e1-20a694b60181ca9', // e.g. 36 chars
    });
  });

  afterEach(() => {
    cy.logout({
      root: 'https://keycloak.babangsund.com',
      realm: 'stage',
      redirect_uri: 'https://babangsund.com/',
    });
  });
});

Looking at libraries, cypress-keycloak-commands is another one, more popular (around three time the weekly downloads).

I like the way it is configuration-based, and has cy.kcFakeLogin() for fixture-based mocks.

There is also this blog Cypress.io Keycloak Integration if you want to learn the details of Keycloak.

Reproduced for future reference if the link disappears

Cypress.Commands.add('kcLogin', (username, password) => {
  const kcRoot = 'http://my.keycloak.com';
  const kcRealm = 'MYrealm';
  const kcClient = 'my-client';
  const kcRedirectUri = 'http://localhost:3000/';
  const loginPageRequest = {
    url: `${kcRoot}/auth/realms/${kcRealm}/protocol/openid-connect/auth`,
    qs: {
      client_id: kcClient,
      redirect_uri: kcRedirectUri,
      state: createUUID(),
      nonce: createUUID(),
      response_mode: 'fragment',
      response_type: 'code',
      scope: 'openid'
    }
  };
  // Open the KC login page, fill in the form with username and password and submit.
  return cy.request(loginPageRequest)
    .then(submitLoginForm);
  ////////////
  function submitLoginForm(response) {
    const _el = document.createElement('html');
    _el.innerHTML = response.body;
    // This should be more strict depending on your login page template.
    const loginForm = _el.getElementsByTagName('form');
    const isAlreadyLoggedIn = !loginForm.length;
    if (isAlreadyLoggedIn) {
      return;
    }
    return cy.request({
      form: true,
      method: 'POST',
      url: loginForm[0].action,
      followRedirect: false,
      body: {
        username: username,
        password: password        
      }
    });
  }
  // Copy-pasted code from KC javascript client. It probably doesn't need to be 
  // this complicated but I refused to spend time on figuring that out.
  function createUUID() {
    var s = [];
    var hexDigits = '0123456789abcdef';
    for (var i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4';
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23] = '-';
    var uuid = s.join('');
    return uuid;
  }
});
Related