I am currently trying to implement a Cypress e2e test which is involving Azure B2C AD as external identity provider. So first I tried to visit the login page just as a normal user would, with:
cy.get('#loginButton').click();
// this will visit:
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/authorize?...
cy.wait(2000);
cy.get('input').first().type(email).should('have.value', email);
cy.get('[id="password"]').type(password).should('have.value', password);
cy.get('#next').click();
// should now redirect me to my app with the access token in my local storage
But then i get the following error:
{statusCode: 401, message: "Unauthorized"}
While doing it myself inside the browser works perfectly fine. Cypress cannot do it. Then I learned, that Cypress cannot accept state from external websites and I need to get the login token with cy.request() and inject it in my application:
https://github.com/cypress-io/cypress/issues/1342
Tutorial: Azure AD Authentication in Cypress Tests with MSAL
The Problem here is, that they used:
https://login.microsoftonline.com/${Cypress.env('AZURE_TENANT_ID')}/oauth2/v2.0/token
but my app uses:
https://${Cypress.env('AZURE_TENANT_NAME')}.b2clogin.com/${Cypress.env('AZURE_TENANT_NAME')}.onmicrosoft.com/${Cypress.env('AZURE_AD_LOGIN_POLICY_NAME')}/oauth2/v2.0/token
I tried to adapt the tutorial request, but it does not work for me:
Cypress.Commands.add('login', () => {
cy.request({
method: 'POST',
url: `https://${Cypress.env('AZURE_AD_AUTH_TENANT')}.b2clogin.com/${Cypress.env('AZURE_AD_AUTH_TENANT')}.onmicrosoft.com/${Cypress.env('AZURE_AD_AUTH_LOGIN_POLICY_NAME')}/oauth2/v2.0/Token`,
form: true,
body: {
grant_type: 'password',
client_id: `${Cypress.env('AZURE_AD_AUTH_CLIENT_ID')}`,
client_secret: Cypress.env('AZURE_AD_AUTH_CLIENT_SECRET'),
scope: 'openid%20offline_access',
username: Cypress.env('AZURE_USERNAME'),
password: Cypress.env('AZURE_PASSWORD')
},
}).then((response) => {
injectTokens(response);
});
This request yields:
{
"error": "invalid_request",
"error_description": "AADB2C90055: The scope 'openid offline_access' provided in request must specify a resource, such as 'https://example.com/calendar.read'.\r\nCorrelation ID: 147e543e-7b12-4349-8917-ad7d97b4b7cd\r\nTimestamp: 2022-12-08 11:40:05Z\r\n"
}
Sadly I have no clue what this error means, which URL need i to provide? Which is the permission a user has to login to the website? I just want the token so i can visit the website as authentificated user with Cypress.
I know here are some posts with suggestions on how to approach this, but no a single one works for my url, with the ".b2clogin.com" inside, this works diffrent i guess.
Here are the 3 exposed urls i can call:
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/authorize
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{loginPolicy}/SelfAsserted
https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{loginPolicy}/oauth2/v2.0/token
I tried to reproduce the same in my environment via Postman and got below results:
I registered one web application in my B2C tenant and added API permissions as below:
In Manifest, make sure to enable implicit flow like below:
Now, I created one resource owner user flow like below:
When I tried to get the token giving same parameters via Postman, I got same error as you like below:
POST https://b2ctenantname.b2clogin.com/b2ctenantname.onmicrosoft.com/<ROPC_policyname>/oauth2/v2.0/token
grant_type:password
client_id:<App_ID>
client_secret:<secret>
scope:openid offline_access
username:<UPN_of_B2C user>
password:xxxxxxxxxxxx
Response:
To resolve the error, you need to include your Application ID in scope
parameter.
I generated the tokens successfully by changing scope like below:
POST https://b2ctenantname.b2clogin.com/b2ctenantname.onmicrosoft.com/<ROPC_policyname>/oauth2/v2.0/token
grant_type:password
client_id:<App_ID>
client_secret:<secret>
scope:openid <App_ID> offline_access
username:<UPN_of_B2C user>
password:xxxxxxxxxxxx
Response:
If you want to get both access and id tokens, then include response_type
parameter like below:
POST https://b2ctenantname.b2clogin.com/b2ctenantname.onmicrosoft.com/<ROPC_policyname>/oauth2/v2.0/token
grant_type:password
client_id:<App_ID>
client_secret:<secret>
scope:openid <App_ID> offline_access
response_type: token id_token
username:<UPN_of_B2C user>
password:xxxxxxxxxxxx
Response:
In your scenario, change value of scope
parameter in your code by including your Application ID along with openid and offline_access.
Reference:
Set up a resource owner password credentials flow - Azure AD B2C | Microsoft