Please help ASAP
@jeffradom We will need a reproducible example in order to see the issue you are having. Also, if there is an existing issue already for this - we suggest you comment within that issue instead of creating a new one.
Can you link the previous issues you were looking at?
Can you provide a way for us to reproduce this exactly?
@jennifer-shehane There are some existing issues similar what I got but none were ever fixed and some closed because people simply ''ditched' Cypress as a tool for testing because it can't handle simple login with redirects. I can't give you more details for OKTA behavior but you might need to get OKTA account and a site that uses it or create your own to see that issue. With SAlesforce you can can create a free SFDC account and try to login using Cypress. All logins above work seemingly with Selenium, Testcafe, etc. In other words none of the code I tried with redirects don't work. I'm looking not a recipe but a real work out of the box like other tools do. In real life for E2E testing users just will login and Login/Logout are main tests for usual release criteria
I've asked our UI expert and the suggested he following code to bypass OKTA SSO but it was able to set a cookie successfully but read a value as blank. Please, advise
describe('CAIR login and case creation', function() {
it('CAIR login and case creation', function() {
fetch(http://aaa.com/api/v1.1/session, { method: 'GET' })
.then(res => {
console.log(--> Response, res.headers);
return res.headers.get('Set-Cookie').match(/XSRF-TOKEN=([^;]+)/)[1]
})
.then(() => {
console.log(--> Before login...
});
cy.visit('login');
cy.get('#okta-signin-username').type('login');
cy.get('#okta-signin-password').type('password');
cy.get('#okta-signin-submit').click();
});
You may want to look into using the cy.setCookie()
command after fetching the cookie value.
I understand this is for OKTA specifically, but I'm not sure it should as my understanding is it will happen with all SAML Idps. We used OKTA in the past and now Google IDaaS and we're never going to get in without being SAML-authenticated.
I don't know that there's a perfect solution to this issue. #1489 captures this well I think. I might try cy.setCookie()
at some point though IDK whether or not it'll be enough.
Just faced with the same issue! Our company moving to OKTA. First application already moved and that broke Cypress tests. Cypress just literally don't want to redirect to OKTA login page....
@jeffradom
If this is still an issue for you, I have figured out how to authenticate in OKTA with cypress and bypass the login and password screen. I have tested it on two applications in our company, everything works fine. So here is the implementation I did, not fancy but working:
const optionsSessionToken = {
method: 'POST',
url: 'https://your_company_link_to_auth_server.com/api/v1/authn',
body: {
username: 'your_OKTA_username',
password: 'your_OKTA_password',
options: {
warnBeforePasswordExpired: 'true'
}
}
};
//first cy.request you need to get a OKTA session token
cy.request(optionsSessionToken).then(resp => {
const sessionToken = resp.body.sessionToken;
//once you have a token, you need to hit okta authorize URL with client ID and redirect URL.
//it will be a very long string with different params. Add a session token at the end of the string as parameter
cy.request({
method: 'GET',
url:
'https://your_company_link_to_authorize.com/oauth2/default/v1/authorize?client_id=11111111&redirect_uri=http://localhost:4200/callback&response_type=id_token token&OTHER PARAMS PARAMS PARAMS&sessionToken=' + sessionToken,
form: true,
followRedirect: false
}).then(respWithToken => {
const url = respWithToken.redirectedToUrl;
//if you want to save bearer token to make another API calls with cy.request
//use code below with cy.wrap(). If you don't just ignore it
const token = url
.substring(url.indexOf('access_token'))
.split('=')[1]
.split('&')[0];
cy.wrap(token).as('token');
//last step is to visit the redirecturl and then visit homepage of the app
cy.visit(url).then(() => {
cy.visit('/');
});
});
});
Hi Postavshik,
we use reactjs application , i was following your code but i did not undertood "OTHER PARAMS PARAMS PARAMS", currently i have mentioned url as https://xyz.oktapreview.com/oauth2/default/v1/authorize?client_id=0oalc0l9yi92mlnOU0h7&response_type=id_token token&scope=openid&redirect_uri=https://abc.com/implicit/callback&sessionToken=' + sessionToken,
i am getting
error=invalid_request&error_description=The+%27nonce%27+parameter+is+required+for+authorize+requests+with+either+the+%27id_token%27+or+%27token%27
in cypress , please help how to fix this issue...
PARAMS PARAMS PARAMS - your link should have different generated parameters. My application has there parameters: response_mode, state and nonce tokens. According to your error message you are missing this parameter (nonce token) in your redirect URL. Ask your Devs about the correct URL with all these params. Also you can grab this link from the Chrome DevTools in the networking tab if you are able successfully to authenticate with OKTA.
Test your link in the Postman first, but switch off redirect in the settings first, otherwise you ll get an error. Once you get Status: 302 in the Postman, go ahead and try this link in Cypress.
So in Postman make a first call and get your session token. Then make a second call with correct URL and add a session token as parameter to that URL. If you did everything correct, you should get Status 302.
Thanks a lot , i got the URL and we are also using same response_mode, state and nonce tokens, but i am not sure how to get state and nonce value for my URL.
below is the application flow we have :
We browse Reactjs APP Url and there we click Login button , its redirect us to Okta Login screen
here after redirecting to Okta login screen , i am seeing cookie with state and nonce token but not understanding how to use that in your above mentioned code. Could you please guide me.
These tokens should be part of your redirect URL as parameters, like: redirect_uri=https://abc.com/implicit/callback&response_mode=fragment&state=xxxxxxxxxx&nonce=xxxxxxxxx&scope= something something something
Instead of "xxx" put your tokens
At the end of this link add a sessionToken from the step 1.
I made a custom command with some instructions that can be downloaded and imported into Cypress. @Postavshik Thanks for finding a way to get this accomplished.
https://gist.github.com/ndavis/2c84ab40aaa3c98c3a8062bdb3938232
Thank you for this code! It helped me so much, I was looking all over for code to help me with PKCE specifications, I truly appreciate it!
throws me below error
my code
Cypress.Commands.add('loginOkta', () => {
const optionsSessionToken = {
method: 'POST',
url: 'https://xxxxx/v1/token',
form: true,
headers: {
accept: 'application/json'
},
body: {
username: 'xxxx',
password: 'xxxx!',
options: {
warnBeforePasswordExpired: true,
multiOptionalFactorEnroll: true
}
}
}
cy.request(optionsSessionToken).then(response => {
const sessionToken = response.body.sessionToken;
const qs = {
client_id: 'xxxxxx',
client_secret: 'xxxxx',
state: 'test',
redirect_uri: '<my UI url>',
scope: 'openid',
sessionToken: sessionToken
}
cy.request({
method: 'GET',
url: 'https://xxxxxx/v1/authorize',
form: true,
followRedirect: false,
qs: qs
}).then(responseWithToken => {
const redirectUrl = responseWithToken.redirectedToUrl;
console.log('Test1' + redirectUrl)
const accessToken = redirectUrl
.substring(redirectUrl.indexOf('access_token'))
.split('=')[1]
.split('&')[0];
cy.wrap(accessToken).as('accessToken');
cy.visit(redirectUrl).then(() => {
cy.visit('/');
});
});
});
})
Error
The request we sent was:
Method: POST
URL: https://xxxxx/v1/token
Headers: {
"Connection": "keep-alive",
"accept": "application/json",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
"cookie": "xxxxx; JSESSIONID=579BD4F7DFDCD14D1A3947C0D6BE07FF",
"content-type": "application/x-www-form-urlencoded",
"accept-encoding": "gzip, deflate",
"content-length": 147
}
Body: username=xxxx.com&password=xxxx&options%5BwarnBeforePasswordExpired%5D=true&options%5BmultiOptionalFactorEnroll%5D=true
-----------------------------------------------------------
The response we got was:
Status: 400 - Bad Request
Headers: {
"date": "Fri, 03 Apr 2020 09:57:53 GMT",
"content-type": "application/json",
"transfer-encoding": "chunked",
"connection": "keep-alive",
"server": "nginx",
"public-key-pins-report-only": "pin-sha256="xxxx="; pin-sha256="xxxx="; pin-sha256="xx="; pin-sha256="xx="; max-age=60; report-uri="https://okta.report-uri.com/r/default/hpkp/reportOnly"",
"x-okta-request-id": "XocIofODizjCmQhVAXqrwQAABx4",
"x-xss-protection": "1; mode=block; report=https://okta.report-uri.com/r/d/xss/enforce",
"p3p": "CP="HONK"",
"x-content-type-options": "nosniff",
"set-cookie": [
"sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/"
]
}
Body: {
"errorCode": "invalid_client",
"errorSummary": "Invalid value for 'client_id' parameter.",
"errorLink": "invalid_client",
"errorId": "oaeQyL8o_7UTzmlwTXAl7ZNcQ",
"errorCauses": []
}
The error clearly says that your client_id is invalid.
Hi, Artem, thanks for sharing your progress!
I'm also getting similar error
status: 400
statusText: "Bad Request"
and
No Tenants Found error under the login form
```
CypressError: cy.request() failed on:
https://xxx.com/oauth2/default/v1/authorize?client_id=xxxx&redirect_uri=http://localhost:3003/callback&response_type=id_token&sessionToken=xxx
The response we received from your web server was:
400: Bad Request
This was considered a failure because the status code was not '2xx' or '3xx'.
If you do not want status codes to cause failures pass the option: 'failOnStatusCode: false'
I don't know.
Something wrong with your request. cliend_id or client-secret, maybe scope.
And I don't know what 'No Tenants Found error under the login form' means. It s something very specific to your app. Ask developers what does that mean.
Hi Artem, thanks for following this!
Also one additional question as for parameters: response_mode, state and nonce tokens. Where do we get that?
Currently I'm wondering if I get the right credentials for this requests. I'm using robot account credentials, but maybe I have to try regular user account.
Also very strange but running this request https://developer.okta.com/docs/reference/api/oidc/#authorize in postman (in compare with Cypress, where I get 400) gives 200 mostly every time, even if I remove required query parameters
From what I understand, your credentials (username and password) are correct, because you passed first API call to get session token but you have 400 with a second request. So something is wrong with configuration of the second call.
Try to paste the URL cypress was trying to hit to Postman. In the response body you should see the exact reason of 400 error. OKTA is pretty good on exception handling. it will tell you which parameter is wrong, then you can ask your developers for correct parameters for body request
I ran into a similar issue. I have tried the above solution but I'm getting HTTP 400 error. Not sure what went wrong in the request. To the best of my knowledge, all the request inputs I passed are accurate. Here are the error details
CypressError: cy.request() failed on:
https://{org_name}.okta.com/oauth2/default/v1/authorize
The response we received from your web server was:
> 400: Bad Request
This was considered a failure because the status code was not '2xx' or '3xx'.
If you do not want status codes to cause failures pass the option: 'failOnStatusCode: false'
----------------------------------------------------------
The request we sent was:
Method: GET
URL: https://{org_name}.okta.com/oauth2/default/v1/authorize
Headers: {
"Connection": "keep-alive",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36",
"accept-encoding": "gzip, deflate"
}
-----------------------------------------------------------
The response we got was:
Status: 400 - Bad Request
Headers: {
"date": "Wed, 06 May 2020 20:33:17 GMT",
"content-type": "text/html;charset=utf-8",
"transfer-encoding": "chunked",
"connection": "keep-alive",
"server": "nginx",
"x-okta-request-id": "XrMfDQTFvVHuwh2B82c09wAAAFo",
"content-security-policy-report-only": "default-src 'self' *.oktacdn.com meetearnest.okta.com; connect-src 'self' *.oktacdn.com *.mixpanel.com *.mapbox.com app.pendo.io data.pendo.io pendo-static-5634101834153984.storage.googleapis.com meetearnest.okta.com meetearnest-admin.okta.com meetearnest.kerberos.okta.com https://oinmanager.okta.com data:; script-src 'unsafe-inline' 'unsafe-eval' 'self' *.oktacdn.com; style-src 'unsafe-inline' 'self' *.oktacdn.com app.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com; frame-src 'self' login.okta.com meetearnest.okta.com meetearnest-admin.okta.com api-01aaa41c.duosecurity.com; img-src 'self' *.oktacdn.com meetearnest.okta.com *.tiles.mapbox.com *.mapbox.com app.pendo.io data.pendo.io cdn.pendo.io pendo-static-5634101834153984.storage.googleapis.com data: blob:; font-src data: 'self' *.oktacdn.com fonts.gstatic.com; report-uri https://okta.report-uri.com/r/d/csp/reportOnly; report-to csp-report",
"report-to": "{\"group\":\"csp-report\",\"max_age\":31536000,\"endpoints\":[{\"url\":\"https://okta.report-uri.com/r/d/csp/reportOnly\"}],\"include_subdomains\":true}",
"referrer-policy": "no-referrer",
"cache-control": "no-cache, no-store",
"pragma": "no-cache",
"expires": "0",
"x-content-type-options": "nosniff",
"content-language": "en",
"set-cookie": [
"JSESSIONID=FA046BB91376E8B5D3E8CAB68905D123; Path=/; Secure; HttpOnly",
"t=default; Path=/",
"sid=102SRp_oFiwTQGAXnVBKqR-Ug;Version=1;Path=/;Secure;HttpOnly;SameSite=None",
"proximity_2f34a3a02dc06cca9b45079bfba7578b=fJYYj4m7ETFuAcje13I4Rro3aNTo+VINkn9DUB4T2IIzF2BsY1lbl33jECsqk+zv24Hq+f/7YPTv/+MoRYTh7Bt+IVFp19+CdNRmbCmKbkiPLql/Y0ggK8tbYpolT4dN+pDxSmWN/tktTAmlIbBGIKLlFI9rt6spWO3FgWFJS8/xzVKoSjRHkxoqa2FarpEC;Version=1;Path=/;Max-Age=31536000;Secure;Expires=Thu, 06 May 2021 20:33:17 GMT;SameSite=None"
]
}
Body: <!DOCTYPE html><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="robots" content="noarchive"/><meta name="googlebot" content="noarchive"/><meta name="robots" content="noindex" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="apple-mobile-web-app-capable" content="yes"><meta name="googlebot" content="noindex" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="none" />
<title>Bad Request</title>
<link href="https://ok2static.oktacdn.com/assets/css/saasure-min.ebd6cbe04743a3b7641a947796d6f790.css" type="text/css" rel="stylesheet"/><link href="https://ok2static.oktacdn.com/assets/css/courage/courage.f3f915f36c6f40da8138a35b7f0b07fa.css" type="text/css" rel="stylesheet"/><link href="https://ok2static.oktacdn.com/assets/css/admin-overrides.e34f2be7cdb5db4b75a01570cb41cbf5.css" type="text/css" rel="stylesheet"/><script>var _M=["0ua8ftq89yvHCz9zn0x7_loggedIn","0uao1rxb7bRXvXOVT0x7_seen","0ua8ftq89yvHCz9zn0x7_seen","0uao1ry0d8ilkhQlV0x7_seen","0uao1rxn548V0XmjF0x7_seen","0ua8ftq8a4yrv7WsW0x7_seen","0uao1rxg7rvdaEO4n0x7_seen","0uao1ry6m...
Hi @Postavshik, am facing same issue in my application. we use OpenID as authentication.
Does OpenID authentication follow the same approach to resolve the login issue?
I need to write tests for an Okta SSO app. I tried the command referred here above but that solutions does a redirect across domains so is still not working for me. I am using okta v3 and openID, is there a a way okta to bypass multiple domains restrictions? This should be a test very easy to write and this restriction is adding unnecessary complications
@Postavshik I used the same solution but Im still getting the OKTA screen even after I see the 200 and 302 response code in time travel cypress.io.
really appreciate you help.
here is screenshot: https://ibb.co/BPC5tLc
I'm still digging into this here, but I've noticed that the pattern posted by @Postavshik (thanks BTW, it's been a life saver) works fine on electron and FireFox but no longer works on Chrome for me. I'm pretty sure that it used to but something's changed and while it certainly logs in, it no longer bypasses the okta login screen. I'll keep digging and post back if I work out what's going on.
Yeah... something changed with OKTA since I posted the solution for the first time. For some of our applications, we have exactly the same picture. The old solution not always works. But we found a new solution :))
Now, you need to set manually the session details from OKTA response into the browser local storage. I can show you the code from one of our apps, but you ll need to figure out the exact values for your app by looking into the local storage of your browser and configure everything accordingly.
You will need to create 3 JSON objects in your fixtures: okta-token-storage, persistroot, user.
Explore your current session data in the browser, to understand, what those JSON objects should be. I think they are unique for each app.
After this method is done, then just cy.visit('/') and you good to go )
So:
``
Cypress.Commands.add('loginWithOkta', () => {
const optionsSessionToken = {
method: 'POST',
url: 'https://OKTA_AUTH_URL/api/v1/authn',
body: {
username: YOUR USERNAME,
password: YOUR PASSWORD,
options: {
warnBeforePasswordExpired: 'true'
}
}
}
cy.request(optionsSessionToken).then((response) => {
const sessionToken = response.body.sessionToken;
cy.request({
method: 'GET',
url: 'https://OKTA_OAUTH2_URL/oauth2/default/v1/authorize?',
form: true,
followRedirect: false,
qs: {
client_id: 'YOUR CLIENT ID',
state: 'YOUR STATE TOKEN',
nonce: 'YOUR NONCE TOKEN',
redirect_uri:
REDIRECT_URL`,
response_mode: 'fragment',
response_type: 'id_token token',
scope: 'YOUR SCOPE',
sessionToken
}
})
.then((response) => {
const urlWithToken = response.headers.location.toString();
const accessToken_returned = urlWithToken
.substring(urlWithToken.indexOf('access_token', 1000))
.split('=')[1]
.split('&')[0];
const idToken_returned = urlWithToken
.substring(urlWithToken.indexOf('id_token'))
.split('=')[1]
.split('&')[0];
cy.fixture('okta-token-storage').then((storage) => {
storage.idToken.idToken = idToken_returned;
storage.accessToken.accessToken = accessToken_returned;
window.localStorage.setItem('okta-token-storage', JSON.stringify(storage));
});
cy.fixture('persistroot.json').then((storage) => {
window.localStorage.setItem('persist:root', JSON.stringify(storage));
});
cy.fixture('user.json').then((storage) => {
window.localStorage.setItem('VALUE FOR USER OF YOUR APP', JSON.stringify(storage));
});
});
});
});
I'm not sure that this is quite the same as my problem. Those look like the settings used by okta-auth-js and I'm using oidc-client instead, and I have nothing like that in my env when it works. This does make me wonder if there's something similar though that I need to do. Either way, I appreciate the follow up and it might well help someone else.
@Postavshik Thanks for updating your example! I think it's very close to working for me. Are you using a framework like React, Vue, etc?, and if so are you using something like @okta/okta-react
or @okta/okta-vue
in your code for authentication / protecting routes? I'm using `@okta/okta-react and I'm still having a problem with the redirecting that happens automatically through that library.
@danmolitor we are using Angular but for authorization, I don't remember that we are using something okta-angular specific. It;s just a normal OKTA flow I guess...
I've also faced the same issue gone through many discussions and finally I had a simple work around which will help. So, the main thing here is cypress is not allowing different domains the same test, so I've done the below one
it("Okta login", () => {
cy.visit(okta_url);
cy.wait(load_time);
cy.get("#idp-discovery-username").type(oktausername);
cy.get("#idp-discovery-submit").click();
cy.get("#okta-signin-password").type(oktapassword);
cy.get("#okta-signin-submit").click();
cy.wait(7000);
// as each test in cypress will create a seperate session of browser
// we need to preserve the cookies after logging in to okta
// these cookies will be used in next test by cypress
Cypress.Cookies.defaults({
preserve: /.*/,
});
});
it("Visit application page", () => {
// logging into application
cy.visit(application_url);
// clicking on sign-in, as okta cookies are already present from the previous test
// it won't redirect to okta now
cy.get("[href='/sso?next=%2Fhome']", { timeout: 7000 }).click();
});
After I get the accesstoken, I visit the url, but always get error in console. Who can help me?
Uncaught (in promise)
n {name: "AuthSdkError", message: "Token must be an Object with scopes, expiresAt, and an idToken or accessToken properties", errorCode: "INTERNAL", errorSummary: "Token must be an Object with scopes, expiresAt, and an idToken or accessToken properties", errorLink: "INTERNAL", 鈥
Most helpful comment
@jeffradom
If this is still an issue for you, I have figured out how to authenticate in OKTA with cypress and bypass the login and password screen. I have tested it on two applications in our company, everything works fine. So here is the implementation I did, not fancy but working: