3

我想使用 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 调用

4

1 回答 1

5

我还没有找到使用 PKCE 本身以编程方式执行此操作的方法,但是使用 MSAL 2.0 库(@azure/msal-browser在 npm 上)我能够提前填写帐户缓存,因此它认为它已经登录。过程看起来像这个:

  1. 使用cy.taskROPC 流向 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();
    
  2. 将令牌转换为 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,
    });
    
  3. 用于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]);
            }
        });
    });
    

它超级脆弱,不是很漂亮,但它有效!赛普拉斯登录的用户当然需要禁用 MFA。

于 2020-08-19T16:04:33.567 回答