有没有一种方法可以通过 PKCE 流程上的赛普拉斯以编程方式登录以使用 Azure AD?
Is there a way to programatically login to using AzureAD with Cypress on PKCE flow?
我想使用 cypress.js (https://www.cypress.io/) 来验证自己(React 应用程序)。有没有办法用 PKCE 以编程方式完成它?当我阅读和研究所有示例时 - 所有示例都使用隐式流
我尝试使用像 https://www.npmjs.com/package/react-adal
这样的解决方案,但没有成功,因为它需要一个隐式流程 turned on
我也在尝试这个:https://xebia.com/blog/how-to-use-azure-ad-single-sign-on-with-cypress/ 没有成功
我希望以编程方式在 cypress 中登录并将用户信息和 access_token 保存到 sessionStorage 以便能够执行另一个 api 调用
我还没有找到使用 PKCE 本身以编程方式执行此操作的方法,但是使用 MSAL 2.0 库(@azure/msal-browser
on npm)我能够提前填充帐户缓存,所以它认为它已经登录了。过程如下所示:
- 使用
cy.task
,使用 ROPC 流向 Azure AD 发送请求以获取令牌。
const scopes = [
'openid',
'profile',
'user.read',
'email',
'offline_access' // needed to get a refresh token
];
const formdata = new URLSearchParams({
'grant_type': 'password',
'scope': scopes.join(' '),
'client_info': 1, // returns an extra token that MSAL needs
'client_id': aadClientId,
'client_secret': aadClientSecret,
'username': aadUsername,
'password': aadPassword,
});
const response = await fetch(`https://login.microsoft.com/${aadTenantId}/oauth2/v2.0/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formdata.toString(),
});
const tokens = await response.json();
- 将令牌转换为 MSAL 想要的缓存条目(基于在真实浏览器中观察)
// The token tells us how many seconds until expiration;
// MSAL wants to know the timestamp of expiration.
const cachedAt = Math.round(new Date().getTime()/1000);
const expiresOn = cachedAt + tokens.expires_in;
const extendedExpiresOn = cachedAt + tokens.ext_expires_in;
// We can pull the rest of the data we need off of the ID token body
const id_token = JSON.parse(Buffer.from(tokens.id_token.split('.')[1], 'base64').toString('utf-8'));
const clientId = id_token.aud;
const tenantId = id_token.tid;
const userId = id_token.oid;
const name = id_token.name;
const username = id_token.preferred_username;
const environment = 'login.windows.net'; // ♂️
const homeAccountId = `${userId}.${tenantId}`;
const cacheEntries = {};
// client info
cacheEntries[`${homeAccountId}-${environment}-${tenantId}`] = JSON.stringify({
authorityType: 'MSSTS',
clientInfo: tokens.client_info,
environment,
homeAccountId,
localAccountId: userId,
name,
realm: tenantId,
username,
});
// access token
cacheEntries[`${homeAccountId}-${environment}-accesstoken-${clientId}-${tenantId}-${token.scope}`] = JSON.stringify({
cachedAt: cachedAt.toString(),
clientId,
credentialType: "AccessToken",
environment,
expiresOn: expiresOn.toString(),
extendedExpiresOn: extendedExpiresOn.toString(),
homeAccountId,
realm: tenantId,
secret: tokens.access_token,
target: tokens.scope,
});
// id token
cacheEntries[`${homeAccountId}-${environment}-idtoken-${clientId}-${tenantId}-`] = JSON.stringify({
clientId,
credentialType: "IdToken",
environment,
homeAccountId,
realm: tenantId,
secret: tokens.id_token,
});
// refresh token
cacheEntries[`${homeAccountId}-${environment}-refreshtoken-${clientId}--`] = JSON.stringify({
clientId,
credentialType: "RefreshToken",
environment,
homeAccountId,
secret: tokens.refresh_token,
});
- 使用
cy.window
将它们存储在 sessionStorage 或 localStorage 中,具体取决于您如何配置 MSAL。
cy.task('login').then(cacheEntries => {
cy.window().then(window => {
for (let entry in cacheEntries) {
window.sessionStorage.setItem(entry, cacheEntries[entry]);
}
});
});
它超级脆弱而且不是很漂亮,但它确实有效!当然,Cypress 登录的用户需要禁用 MFA。
我想使用 cypress.js (https://www.cypress.io/) 来验证自己(React 应用程序)。有没有办法用 PKCE 以编程方式完成它?当我阅读和研究所有示例时 - 所有示例都使用隐式流
我尝试使用像 https://www.npmjs.com/package/react-adal
这样的解决方案,但没有成功,因为它需要一个隐式流程 turned on
我也在尝试这个:https://xebia.com/blog/how-to-use-azure-ad-single-sign-on-with-cypress/ 没有成功
我希望以编程方式在 cypress 中登录并将用户信息和 access_token 保存到 sessionStorage 以便能够执行另一个 api 调用
我还没有找到使用 PKCE 本身以编程方式执行此操作的方法,但是使用 MSAL 2.0 库(@azure/msal-browser
on npm)我能够提前填充帐户缓存,所以它认为它已经登录了。过程如下所示:
- 使用
cy.task
,使用 ROPC 流向 Azure AD 发送请求以获取令牌。const scopes = [ 'openid', 'profile', 'user.read', 'email', 'offline_access' // needed to get a refresh token ]; const formdata = new URLSearchParams({ 'grant_type': 'password', 'scope': scopes.join(' '), 'client_info': 1, // returns an extra token that MSAL needs 'client_id': aadClientId, 'client_secret': aadClientSecret, 'username': aadUsername, 'password': aadPassword, }); const response = await fetch(`https://login.microsoft.com/${aadTenantId}/oauth2/v2.0/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: formdata.toString(), }); const tokens = await response.json();
- 将令牌转换为 MSAL 想要的缓存条目(基于在真实浏览器中观察)
// The token tells us how many seconds until expiration; // MSAL wants to know the timestamp of expiration. const cachedAt = Math.round(new Date().getTime()/1000); const expiresOn = cachedAt + tokens.expires_in; const extendedExpiresOn = cachedAt + tokens.ext_expires_in; // We can pull the rest of the data we need off of the ID token body const id_token = JSON.parse(Buffer.from(tokens.id_token.split('.')[1], 'base64').toString('utf-8')); const clientId = id_token.aud; const tenantId = id_token.tid; const userId = id_token.oid; const name = id_token.name; const username = id_token.preferred_username; const environment = 'login.windows.net'; // ♂️ const homeAccountId = `${userId}.${tenantId}`; const cacheEntries = {}; // client info cacheEntries[`${homeAccountId}-${environment}-${tenantId}`] = JSON.stringify({ authorityType: 'MSSTS', clientInfo: tokens.client_info, environment, homeAccountId, localAccountId: userId, name, realm: tenantId, username, }); // access token cacheEntries[`${homeAccountId}-${environment}-accesstoken-${clientId}-${tenantId}-${token.scope}`] = JSON.stringify({ cachedAt: cachedAt.toString(), clientId, credentialType: "AccessToken", environment, expiresOn: expiresOn.toString(), extendedExpiresOn: extendedExpiresOn.toString(), homeAccountId, realm: tenantId, secret: tokens.access_token, target: tokens.scope, }); // id token cacheEntries[`${homeAccountId}-${environment}-idtoken-${clientId}-${tenantId}-`] = JSON.stringify({ clientId, credentialType: "IdToken", environment, homeAccountId, realm: tenantId, secret: tokens.id_token, }); // refresh token cacheEntries[`${homeAccountId}-${environment}-refreshtoken-${clientId}--`] = JSON.stringify({ clientId, credentialType: "RefreshToken", environment, homeAccountId, secret: tokens.refresh_token, });
- 使用
cy.window
将它们存储在 sessionStorage 或 localStorage 中,具体取决于您如何配置 MSAL。cy.task('login').then(cacheEntries => { cy.window().then(window => { for (let entry in cacheEntries) { window.sessionStorage.setItem(entry, cacheEntries[entry]); } }); });
它超级脆弱而且不是很漂亮,但它确实有效!当然,Cypress 登录的用户需要禁用 MFA。