Azure OAuth with Cypress: Infinite Loop

Viewed 743

Trying to set up Cypress to test an application that uses OAuth against Azure AD. My login command is defined as follows:

Cypress.Commands.add('login', () => {
    return cy.request('POST', Cypress.env('AccessTokenUrl') +
        '?grant_type=' + Cypress.env('GrantType') +
        '&client_id=' + Cypress.env('ClientId') +
        '&client_secret=' + Cypress.env('ClientSecret'))
})

This is what I call in a test:

        cy.login().then(response => {
            expect(response.status).to.eq(200)
            expect(response.body).to.have.property('access_token')
            expect(response.body).to.have.property('token_type', 'Bearer')

            const {access_token, expires_in, id_token} = response.body
            cy.setCookie('access_token', access_token)
        })

        cy.visit('my-url')

The validations pass. The login response contains a valid token. However, the ct.visit call fails with infinite recursion, as the parameters like &iframe-request-id=[some uuid] become added over and over to a login.microsoftonline.com URL, until eventually returning HTTP Error 414. The request URL is too long.

Here's what the URL looks like, with some information redacted and with some formatting for clarity:

https://login.microsoftonline.com/
    [tenant-id]/oauth2/v2.0/authorize
    ?response_type=code
    &client_id=[client-id]
    &redirect_uri=[my-url]
    &scope=openid+profile+email+https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
    &iframe-request-id=1a9fdcbd-6b9e-46c8-93e3-ce0edf62b600
    &iframe-request-id=b5b5cf2b-e0a6-4d92-9e55-cf32208ab900
    &iframe-request-id=8471e17f-1d36-48f7-8419-f54e14b3b100
    &iframe-request-id=56113dad-6029-4a37-9758-5828f93f0300
    &iframe-request-id=51c06224-98f1-4b83-a8f2-84f8dfe9aa00
    &iframe-request-id=09775645-505c-42e0-ac56-1335b5a7ba00
    &iframe-request-id=5c98158b-b202-41fe-9d65-8fbfe4e46500
    &[and-so-on]

I have found various suggestions on the web about using Puppeteer as a task for Azure AD SSO, but none of them works for my purposes. First, they try to resolve the problem of actually obtaining the token, which I have already solved. Second, they rely on the login URL presenting an HTML form, which is not the case with login.microsoftonline.com.

What do you suggest?

UPDATE: Trying a different solution, I receive an interesting error. The loginMS command:

import * as MSAL from '@azure/msal-browser'

Cypress.Commands.add('loginMS', () => {
    cy.request({
        method: 'POST',
        url: `https://login.microsoftonline.com/${Cypress.env('TenantId')}/oauth2/token`,
        form: true,
        body: {
            scope: Cypress.env('LoginScope'),
            client_id: Cypress.env('ClientId'),
            client_secret: Cypress.env('ClientSecret'),
            redirect_uri: Cypress.env('LoginRedirect'),
            grant_type: Cypress.env('GrantType'),
            username: Cypress.env('Username'),
            password: Cypress.env('Password'),
            response_type: 'code'
        }
    }).then(response => {
        console.log(response)
        window.localStorage.setItem(`msal.idtoken`, response.body.access_token);
        window.localStorage.setItem(`msal.client.info`, MSAL.clientInfo);
    })
})

The error is:

Failed to find a valid digest in the 'integrity' attribute for resource
'https://aadcdn.msauth.net/shared/1.0/content/js/OldConvergedLogin_PCore_Up8WrFIk8-TG_eqBz8MSlw2.js'
with computed SHA-256 integrity 'NxfOkHjbTYDy/EOknsK0PMOfym7iLRGY+yBShyznzx4='.
The resource has been blocked.
2 Answers

It realy depends how the application under test handles requests. But I guess you use the adal libary.

With the help of https://mechanicalrock.github.io/2020/05/05/azure-ad-authentication-cypress.html it worked for me in a vuejs application using adal v1.

The important part is

   localStorage.setItem("adal.token.keys", `${Cypress.config("clientId")}|`);
    localStorage.setItem(`adal.access.token.key${Cypress.config("clientId")}`, ADALToken);
    localStorage.setItem(`adal.expiration.key${Cypress.config("clientId")}`, expiresOn);
    localStorage.setItem("adal.idtoken", ADALToken);

I actually did not request the token from azure but just copied in what I saw F12 tools as my token when using the application under test.

I managed to solve the Azure AD login by creating the following Cypress custom command for my Angular application:

Cypress.Commands.add('login', () => {
  return cy
    .request({
      method: 'POST',
      url: `https://login.microsoftonline.com/${tenantId}/oauth2/token`,
      form: true,
      body: {
        grant_type: 'password',
        tenant: 'tenantId',
        client_id: 'clientId',
        client_secret: 'clientSecret',
        username: 'username',
        password: 'password',
        resource: 'clientId',
      },
    })
    .then((response) => {
      sessionStorage.setItem('access_token', response.body.access_token);
    });
});

Related