使用Cypress测试Angular项目Azure B2C登录后首次返回401错误
问题根因
- 核心是Azure B2C登录完成后跳回你的Angular应用域名时,Cypress的上下文切换比应用的鉴权逻辑执行快,Angular的路由守卫、HTTP拦截器还没拿到B2C返回的身份凭证(token、cookie)就发起了校验请求,所以返回401。
- 具体触发场景有两个:
- Azure B2C登录后的回调参数(code、id_token等)需要Angular的MSAL库处理完成后才会生成有效凭证,你现在的代码在点击
next按钮后没有等待这个处理逻辑执行完成,直接触发了后续请求。 - Cypress默认限制第三方cookie写入,Azure B2C下发的跨域凭证首次没有被正确同步到你的应用域名下,刷新后浏览器重新拉取了存储的凭证就正常了。
- Azure B2C登录后的回调参数(code、id_token等)需要Angular的MSAL库处理完成后才会生成有效凭证,你现在的代码在点击
解决方案
第一步:调整Cypress配置
首先在cypress.config.js里开启跨域相关配置,允许跨域cookie保存:
const { defineConfig } = require('cypress') module.exports = defineConfig({ e2e: { experimentalSessionAndOrigin: true, chromeWebSecurity: false, // 关闭跨域安全限制适配B2C跳转 } })
第二步:优化测试用例逻辑
不要直接在点击登录按钮后不做等待就继续操作,增加等待MSAL处理完成的逻辑:
describe('login', () => { beforeEach(() => { cy.clearAllCookies() cy.clearAllLocalStorage() cy.clearAllSessionStorage() }) it('get the access token test', () => { cy.visit('/') cy.get('button').click() // B2C登录页操作 cy.get('input#email').type('myemail@gmail.com') cy.get('input#password').type('mypassword') cy.get('button#next').click() // 等待跳转回应用域名,且MSAL完成token处理 cy.origin('你的Angular应用域名', () => { // 等待localStorage里出现MSAL的token,或者等待首页的特征元素出现 cy.window().its('localStorage.msal.idtoken', { timeout: 10000 }).should('exist') cy.get('.nav-user-name', { timeout: 10000 }).should('be.visible') }) // 后续业务操作逻辑 }) })
如果还是出现首次401,可以在登录完成后主动触发一次重载:
// 放在点击next之后的等待逻辑里 cy.reload()
第三步:更稳定的免UI登录方案
如果只是需要拿到鉴权状态跑后续业务测试,建议直接调用Azure B2C的密码授权流接口拿token,写入存储跳过UI登录:
// 封装全局登录命令 Cypress.Commands.add('loginByB2C', () => { cy.request({ method: 'POST', url: '你的B2C租户token端点', form: true, body: { grant_type: 'password', client_id: '你的应用clientId', scope: 'openid profile offline_access 你的API scope', username: 'myemail@gmail.com', password: 'mypassword', } }).then((res) => { // 把返回的token按照MSAL的存储格式写入localStorage window.localStorage.setItem('msal.idtoken', res.body.id_token) window.localStorage.setItem('msal.access_token', res.body.access_token) }) }) // 测试用例直接调用 it('get the access token test', () => { cy.loginByB2C() cy.visit('/') // 后续业务逻辑 })
内容的提问来源于stack exchange,提问作者Andy88
相关产品推荐
相关产品推荐

