Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unable to signup using keycloak through cypress #3119

Closed
wajeeha09 opened this issue Jan 10, 2019 · 14 comments
Closed

unable to signup using keycloak through cypress #3119

wajeeha09 opened this issue Jan 10, 2019 · 14 comments

Comments

@wajeeha09
Copy link

wajeeha09 commented Jan 10, 2019

Current behavior:

In my application, user management is done through Keycloak. Manual signup works fine but while testing my application end to end through cypress, I come across an issue. When I sign up user through it, it gives the following error:

We're sorry. An error has occurred, please login again through your application.

I have noticed that in test case cypress is appending session_code to the request url after I click on submit button. While doing manual test I don't get session code. This can be cause of this issue. Please share your views if you think of any other reason for this issue. Below is the url generated through cypress

.../login-actions/registration?session_code=LsZbmsVVLwEH9s-xwFJ2JdDtaCu1_xzqAGOQCpjxGJI&execution=06fac3bb-fb19-474b-8659-2572586ae371&client_id=web_app&tab_id=PSlmfgdv0ls

Where as manually generated url is like following

.../login-actions/registration?client_id=web_app&tab_id=PSlmfgdv0ls

My application backend is Spring boot and front-end is in React and next.js

It would be really helpful if anyone from your team could guide us for this issue. Please let me know if you need more information about our application.

Desired behavior:

Desired behavior is to able to signup through cypress tests

Steps to reproduce: (app code and test code)

You can use this test case

{
  "baseUrl": "",
  "pageLoadTimeout": 180000,
  "defaultCommandTimeout": 50000,
  "env":{
    "ENVIROMENT":"mock"
  },
  "reporter": "mochawesome",
  "reporterOptions": {
    "reportDir": "reports",
    "reportFilename": "test-report",
    "reportTitle": "Carbook Test Run",
    "overWrite": true,
    "saveHtml": true
  }
}
describe("Carbook Registration Page", () => {
  before(() => {
    cy.visit("/")
  })

  it("Verify that user is access registration page", () => {
    cy.get("#nav-sign-in").should('be.visible')
    cy.get("#nav-sign-in").click()
    cy.wait(5000)
    cy.get("a").contains("Register").should('be.visible')
    cy.get("a").contains("Register").click()
  })

  it("Verify that user able to go back to login page", () => {
    cy.get("#kc-form-options").should('be.visible')
    cy.get("#kc-form-options").click()
    cy.get("#kc-page-title").should('be.visible')
  })

  it("Verify that user is able to register for carbook", () => {
    cy.get("#firstName").type("Test")
    cy.get("#lastName").type("Test")
    cy.get("#email").type("wajeeha@test.com")
    cy.get("#password").type("test@123")
    cy.get("#password-confirm").type("test@123")
    cy.get('input[name="user.attributes.phone"]').type("12341234567")
    cy.get('input[name="user.attributes.cnic"]').type("1234512345678")
    cy.get('input[value="Register"]').click()
    cy.wait(10000)
  })
})
@jennifer-shehane
Copy link
Member

Hey @wajeeha09, we are unable to run the tests provided without the application code or a url to visit in the tests. From the provided test code and settings - everything looks set up properly.

Could you provide screenshots, application code to visit replicating the error, anything else?
Unfortunately we'll have to close this issue if no reproducible example is provided.

If you're interested in dedicated email support, which gives you one-on-one help from our team, you can sign up for one of our paid plans then email us at support@cypress.io.

@jennifer-shehane jennifer-shehane added the stage: needs information Not enough info to reproduce the issue label Jan 14, 2019
@wajeeha09
Copy link
Author

@jennifer-shehane This zip file contains output video of this test. You can use this as you reference
register.e2e-spec.ts.mp4.zip

@wajeeha09
Copy link
Author

@jennifer-shehane Thank you for responding. Application is deployed. Base url is: https://carbook-client-public.carbook-mock.gocarbook.com. Using this url you should be able to reproduce this issue. Let me know if any other information is required.

@kashifali00
Copy link

Hi @jennifer-shehane is there any update on that issue?

@jennifer-shehane
Copy link
Member

I'm able to reproduce a failing test.

Cypress is not appending a session_code to the url. This is coming from keycloak. Likely there is something within the Cypress environment that makes keycloak behave differently and append this session_code (authorization plugins often don't play well with automated testing).

I'm unfamiliar with keycloaks api, but likely you want to skip over manually registering and stub/mock what is necessary for keycloak to recognize a registered user. Maybe after they register, you save their session id in localStorage, for example. Just set the values needed in localStorage within cypress. We do this with our own tests. There should be no need to test the particulars of keycloaks implementation. Only test that the methods in your application are working properly when they send what is expected.

Also, arbitrary waits are very very rarely necessary and can cause flake. I updated your tests to remove the wait times, instead waiting for the url to change.

describe('Carbook Registration Page', () => {
  before(() => {
    cy.visit('https://carbook-client-public.carbook-mock.gocarbook.com')
  })

  it('Verify that user is access registration page', () => {
    cy.get('#nav-sign-in').should('be.visible')
    cy.get('#nav-sign-in').click()
    cy.url().should('include', 'https://keycloak.carbook-dev.gocarbook.com')
    cy.get('a').contains('Register').should('be.visible')
    cy.get('a').contains('Register').click()
  })

  it('Verify that user able to go back to login page', () => {
    cy.get('#kc-form-options').should('be.visible')
    cy.get('#kc-form-options').click()
    cy.get('#kc-page-title').should('be.visible')
  })

  it('Verify that user is able to register for carbook', () => {
    cy.get('#firstName').type('Test')
    cy.get('#lastName').type('Test')
    cy.get('#email').type('wajeeha@test.com')
    cy.get('#password').type('test@123')
    cy.get('#password-confirm').type('test@123')
    cy.get('input[name="user.attributes.phone"]').type('12341234567')
    cy.get('input[name="user.attributes.cnic"]').type('1234512345678')
    cy.get('input[value="Register"]').click()
    cy.url().should('include', 'https://keycloak.carbook-dev.gocarbook.com')
    cy.contains('We\'re sorry').should('not.exist')
  })
})

@jennifer-shehane
Copy link
Member

@wajeeha09 Any update on this? Were you able to resolve this issue? I'd like to close it if so.

@ZijlkerVR
Copy link

ZijlkerVR commented May 17, 2019

@wajeeha09 Have the same problem in my application using keycloak tested by cypress, but I do not recall seeing any extra parameters in my URL.

Do you have the issue when you login right away as first step of your first test? I'm only getting it whenever I try to login in test after some other actions/tests.

Workaround for now is changing before to beforeEach so it revisits the login page every test. I'm going straight to login page. Only doing this in a specific login.spec which has only 3 tests so impact is minimal.

Now trying to create a cy.request to skip the UI login altogether for all other tests as recommended by Cypress team. Having a hard time with that though. Not familiar with the API. Might be easier to just stub the authentications.

@ZijlkerVR
Copy link

ZijlkerVR commented May 23, 2019

I've succeeded in automating our (proxied) Keycloak login without UI using a chain of 5 requests.

  1. GET request to retrieve actionUrl plus query string parameters from login page:
    /auth/realms/{realm}/protocol/openid-connect/auth?scope=name%2Cemail&response_type=code&approval_prompt=auto&redirect_uri={site}%2Fuserauth&client_id=account
    Used this regex to retrieve the url from the HTMLstring object and fix it (using &):
    resp.body.match(/action\=\"(.*)\" /)[1].replace(/&/g, '&');

  2. POST form request to the actionUrl with user/pass in body:
    /auth/realms/{realm}/login-actions/authenticate?session_code={x}&execution={x}&client_id=account&tab_id={x}
    Keycloak cookies are now set.

  3. Request to our login page which will determine 'state' and redirect to authenticate because of presence keycloak cookies

  4. Redirected to the same "auth" of step 1 but with state:
    /auth/realms/{realm}/protocol/openid-connect/auth?state={x}&scope=name%2Cemail&response_type=code&approval_prompt=auto&redirect_uri={site}%2Fuserauth&client_id=account

  5. Redirected to "userauth" eventually leading to our application setting our final login cookies:
    /userauth?state={x}&session_state={x}&code={x}

I've disabled followRedirect on all calls and handled them myself instead. Main reason is it didn't result in an authentication cookies for some reason. Second reason is we use Keycloak server on 1 environment for several other test environments. Catching the redirect enables us to go to particular environment.

Normally I would expect it to be possible to handle this in 2 - 3 steps when the redirecting is followed properly by both Cypress and website. Not sure where it failed. We have a custom keycloak proxy layer so just followed that one. Going through UI entirely will result in even more requests/redirects (8-10).

I realize this is not the most ideal way, but it was the quickest one and still decent result. Login in 2.5s instead of 6-10s through UI. Call 1 and 2 are done in 0.4 seconds. Step 3 is slowing it down caused by lazy acceptance server.

@pouriaMaleki
Copy link

pouriaMaleki commented May 28, 2019

@ZijlkerVR I have followed steps 1 to 3 using this Blog Post of @vrockai, still can't figure out why it won't work.

It might be because of step 4 and 5 of your guide, can you provide more detailed information about that?

context('Logged in user', () => {
  beforeEach(() => {
    cy.kcLogin('testuser', 'testuserpass');
  });

  afterEach(() => {
    cy.kcLogout();
  });

  it('Should render logged user name somewhere on the page', () => {
    cy.visit('http://SAMPLE.com'); // still not logged in, although beforeEach is successful.
    cy.get('#username').should('contain', 'testuser');
  });
});

@ZijlkerVR
Copy link

ZijlkerVR commented Jun 3, 2019

@pouriaMaleki Interesting blog post. His method is comparable to my first 2 steps. He has a cleaner way of retrieving the ActionURL though. He creates a UUID to fill state in step 1 but, atleast in my case, it's optional so I just skipped it and get it later. Although creating a state UUID beforehand might actually decrease the number of steps I need in total so gotta look into that. Not sure if it's the case.

I noticed he stops after his POST. In my case it would only give me KC cookies, but not site specific login cookies. I really need to follow through to atleast 3 more requests untill our site specific cookie will be set. Again.. it might be specifically related to our implementation since they have build quite some code around Keycloak here. It's been build by external supplier so lacking the knowledge on site.

Notice that my step 3 is a specific call back to the login page. Now that KC cookies are available it triggers our site to process and eventually end up with site specific auth cookies.

If I would follow redirect after step 2 POST I would get stuck. Might also be your problem.

Unfortunately I don't understand why steps 4 and 5 are needed. I would expect followRedirect=true in step 3 to work, but I had to disable followRedirect and manually catch and explicitly request them in order to get a successfull login cookie. Could be a Cypress bug.

My advice would be to go through the requests step by step by catching en requesting manually like I did. Will give more control and insight. When we login through UI there is even more than these 5 requests/redirects, but I cut it off as soon as I get the cookie.

My code might help (simplified a bit by removing our environment specific condition) :

Cypress.Commands.add("login", (user, pass) => {
    Cypress.log({ name: 'Login' })
    const userName = (user != undefined) ? user : Cypress.env('user')
    const passWord = (pass != undefined) ? pass : Cypress.env('pass')
    cy.clearCookies()

    // Non UI login - awesomely fast
    // 1. Get login params from auth
    // 2. Post form to authenticate
    // 3. Get back to /login
    // 4. Follow redirect to auth
    // 5. Follow redirect to origin

    const getStartBody = {
        url: 'Cypress.config('baseUrl')' + '/auth/realms/*REALM*/protocol/openid-connect/auth',
        followRedirect: false,
        qs: {
            scope: 'name,email',
            response_type: 'code',
            approval_prompt: 'auto',
            redirect_uri: Cypress.config('baseUrl') + '/userauth',
            client_id: 'account'
        }
    }
    // Step 1
    cy.request(getStartBody).then((getStartResp) => {

        const actionUrl = getStartResp.body.match(/action\=\"(.*)\" /)[1].replace(/&/g, '&');
        const postLoginBody = {
            method: 'POST',
            url: actionUrl,
            followRedirect: false,
            form: true,
            body: { username: userName, password: passWord }
        }
        // Step 2
        cy.request(postLoginBody)
        // Keycloak cookies now set

    }).then(() => {
            // Step 3
            cy.request({
                url: '/login?redirect=/',
                followRedirect: false
            }).then((redirectResp1) => {
                // Step 4
                cy.request({
                    url: redirectResp1.redirectedToUrl,
                    followRedirect: false
                })
            }).then((redirectResp2) => {
                // Step 5
                cy.request({
                    url: redirectResp2.redirectedToUrl,
                    followRedirect: false
                })
            }).then(() => {
                // Finally got the site cookie
                cy.getCookie('cookie').should('exist')
            })
    })

@jennifer-shehane
Copy link
Member

Closing as resolved. Please comment if you are still having this issue and we will consider reopening.

@KhizerRehan
Copy link

KhizerRehan commented Mar 15, 2022

Hi @jennifer-shehane
I will highly appreciate if you could help

Tried following packages to login with cypress:

I am able to login in the sense that Local-storage does show state and token but cypress fails to redirect back to /home page

BUT as i manually click on URL i am able to redirect to correct page,

2da9874de802637bdaf8df96020ff30a.mp4

@Medmj
Copy link

Medmj commented Nov 8, 2022

Hello I had the same issue,
Cypress is able to see the cookies, I even tried getting them and force setting them just after the login and before every test but still acts as if there are no cookies.

The actual message I am getting from the app I'm testing is "Cookie not found. Please make sure cookies are enabled in your browser."

Weirdly enough, I changed the Cypress browser to use for from Chrome 107 to Electron 93 and the problem is gone. (using cypress 8.7.0)

Aren't the browsers supposed tp work in "similar ways" regarding the basic stuff like cookies and login to a certain degree ?

@Swiech-Jan
Copy link

Swiech-Jan commented Sep 28, 2023

@KhizerRehan
Did you solve your problem?
I have the same issue since we upgraded KeyCloak from version 11 to 21.1.
All works in browser as user interacts with the page but Cypress have problem with last redirect to home page after getting all auth cookies from KeyCloak.

I'm Starting the test on the KeyCloak login page then after submitting the login form that last redirect to App home page ends up with timeout - even If all cookies are set properly. Strange that this was working on old version of KeyCloak... Perhaps newest version of KeyCloak are more restrict?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants