Skip to main content
Cypress应用

Google身份验证

info
您将学习到
  • 如何设置Cypress来测试Google身份验证
  • 如何在Cypress中配置Google应用凭证
  • 如何创建自定义Google身份验证命令
  • 如何为测试适配Google应用

我们将使用的测试技术是通过 Google OAuth 2.0 Playground 创建一个刷新令牌,该令牌可以在测试阶段兑换为访问令牌和ID令牌。

Google项目和应用设置

首先需要一个Google项目。如果还没有项目,可以使用 Google Cloud Console创建一个。更多信息请参考 Google Cloud APIs入门指南

接下来,使用Google API控制台 为您的Web应用创建凭证。 在顶部导航中点击创建凭证并选择OAuth客户端ID

创建OAuth客户端ID页面,输入以下内容:

  • 应用类型: Web应用
  • 名称: 您的Web应用名称
  • 授权JavaScript来源: http://localhost:3000
  • 授权重定向URI: http://localhost:3000/callbackhttps://developers.google.com/oauthplayground

保存后,记下客户端ID客户端密钥。您可以在 Google API凭证 页面的"OAuth 2.0客户端ID"下找到这些信息。

使用Google OAuth 2.0 Playground创建测试凭证

info

此过程生成的刷新令牌与认证的Google用户唯一对应。 必须为每个测试用户重复此过程。

访问 Google OAuth 2.0 Playground。 点击右上角的图标显示 OAuth 2.0配置面板。在此面板中设置以下内容:

  • OAuth流程: 服务器端
  • 访问类型: 离线
  • 勾选使用您自己的OAuth凭证
  • OAuth客户端ID: 您的Google应用客户端ID
  • OAuth客户端密钥: 您的Google应用客户端密钥

步骤1(选择并授权API)下选择应用所需的Google API, 至少包括Google OAuth2 API v2下的 https://www.googleapis.com/auth/userinfo.profile端点。点击授权API

接下来,使用测试Google用户账号登录。您将被重定向回 Google OAuth 2.0 Playground步骤2(用授权码兑换令牌)。点击 用授权码兑换令牌按钮。

您将进入步骤3(配置API请求)。记下返回的 刷新令牌以供测试使用。

在Cypress中设置Google应用凭证

为了在测试中使用测试用户凭证,我们需要配置 Cypress使用.env文件中设置的Google环境变量。

.env
REACT_APP_GOOGLE_CLIENTID = 'your-client-id'
REACT_APP_GOOGLE_CLIENT_SECRET = 'your-client-secret'
GOOGLE_REFRESH_TOKEN = 'your-refresh-token'
const { defineConfig } = require('cypress')
// 从.env文件填充process.env
require('dotenv').config()

module.exports = defineConfig({
env: {
googleRefreshToken: process.env.GOOGLE_REFRESH_TOKEN,
googleClientId: process.env.REACT_APP_GOOGLE_CLIENTID,
googleClientSecret: process.env.REACT_APP_GOOGLE_CLIENT_SECRET,
},
})

Google身份验证的自定义命令

接下来,我们将编写一个名为loginByGoogleApi的命令,以编程方式 登录Google并在localStorage中设置一个包含 认证用户详情的项,我们将在应用代码中使用它来验证 测试中的认证状态。

loginByGoogleApi命令将执行以下步骤:

  1. 使用来自 Google OAuth 2.0 Playground 的刷新令牌执行编程登录,将刷新令牌兑换为 access_token
  2. 使用返回的access_token获取Google用户资料
  3. 最后将googleCypress localStorage项设置为包含访问令牌 和用户资料
cypress/support/commands.js
Cypress.Commands.add('loginByGoogleApi', () => {
cy.log('登录Google')
cy.request({
method: 'POST',
url: 'https://www.googleapis.com/oauth2/v4/token',
body: {
grant_type: 'refresh_token',
client_id: Cypress.env('googleClientId'),
client_secret: Cypress.env('googleClientSecret'),
refresh_token: Cypress.env('googleRefreshToken'),
},
}).then(({ body }) => {
const { access_token, id_token } = body

cy.request({
method: 'GET',
url: 'https://www.googleapis.com/oauth2/v3/userinfo',
headers: { Authorization: `Bearer ${access_token}` },
}).then(({ body }) => {
cy.log(body)
const userItem = {
token: id_token,
user: {
googleId: body.sub,
email: body.email,
givenName: body.given_name,
familyName: body.family_name,
imageUrl: body.picture,
},
}

window.localStorage.setItem('googleCypress', JSON.stringify(userItem))
cy.visit('/')
})
})
})

正确设置Google应用、配置必要的环境变量 并实现loginByGoogleApi命令后,我们就可以在测试应用时 通过Google进行认证。以下是通过 Google以用户身份登录、完成引导流程并退出的测试。

info
试一试

该测试的 可运行版本真实世界应用(RWA)中。

auth.cy.js
describe('Google', function () {
beforeEach(function () {
cy.task('db:seed')
cy.loginByGoogleApi()
})

it('显示引导页面', function () {
cy.contains('开始使用').should('be.visible')
})
})

为测试适配Google应用

info
注意

前面的部分重点介绍了Cypress测试中推荐的Google身份验证实践。 要使用此实践,假设您正在测试一个适当构建或适配使用Google的应用。

以下部分提供了构建或适配应用以使用Google身份验证的指导。

真实世界应用(RWA)被 使用,并为React SPA和Express后端提供了配置和可运行代码。

前端使用 react-google-login组件, 后端使用express-jwt来 验证Google提供的JWT。

info

启动 Cypress Real World App时使用yarn dev:google命令。

适配后端

为了验证来自前端的API请求,我们安装 express-jwtjwks-rsa并配置对 来自Google的JWT的验证。

backend/helpers.ts
import jwt from 'express-jwt'
import jwksRsa from 'jwks-rsa'

dotenv.config()
const googleJwtConfig = {
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'https://www.googleapis.com/oauth2/v3/certs',
}),
// 验证受众和颁发者
audience: process.env.REACT_APP_GOOGLE_CLIENTID,
issuer: 'accounts.google.com',
algorithms: ['RS256'],
}

接下来,我们将定义一个Express中间件函数,用于在路由中 验证前端API请求发送的Google JWT 作为Bearer令牌。

backend/helpers.ts
// ...
export const checkJwt = jwt(googleJwtConfig).unless({ path: ['/testData/*'] })

定义此辅助函数后,我们可以全局应用它到所有路由:

backend/app.ts
// 初始导入 ...
import { checkJwt } from './helpers'

// ...
if (process.env.REACT_APP_GOOGLE) {
app.use(checkJwt)
}
// 路由 ...

适配前端

我们需要更新前端React应用以允许通过 Google进行认证。如上所述,前端使用 react-google-login组件 来执行登录。

首先,我们创建一个AppGoogle.tsx容器来渲染通过Google认证的 应用。该组件与App.tsx组件相同,但增加了 一个GoogleLogin组件替代原来的注册和登录组件。

添加了一个useGoogleLogin钩子来发送带有usertoken对象的GOOGLE事件,与现有的认证层 (authMachine.ts)一起工作。

containers/AppGoogle.tsx
// 初始导入 ...
import { GoogleLogin, useGoogleLogin } from 'react-google-login'
// ...
const AppGoogle = () => {
// ...
useGoogleLogin({
clientId: process.env.REACT_APP_GOOGLE_CLIENTID!,
onSuccess: (res) => {
authService.send('GOOGLE', { user: res.profileObj, token: res.tokenId })
},
cookiePolicy: 'single_host_origin',
isSignedIn: true,
})
// ...
const isLoggedIn =
isAuthenticated &&
(authState.matches('authorized') ||
authState.matches('refreshing') ||
authState.matches('updating'))
return (
<div className={classes.root}>
// ...
{authState.matches('unauthorized') && (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<GoogleLogin
clientId={process.env.REACT_APP_GOOGLE_CLIENTID!}
buttonText="登录"
cookiePolicy={'single_host_origin'}
/>
</div>
</Container>
)}
</div>
)
}
export default AppGoogle

接下来,我们更新入口点(index.tsx)以有条件地加载 AppGoogle组件,如果我们在启动应用时将REACT_APP_GOOGLE 环境变量设置为true

src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Router } from 'react-router-dom'
import { history } from './utils/historyUtils'
import App from './containers/App'
import AppGoogle from './containers/AppGoogle'
import { createMuiTheme, ThemeProvider } from '@material-ui/core'
const theme = createMuiTheme({
palette: {
secondary: {
main: '#fff',
},
},
})
ReactDOM.render(
<Router history={history}>
<ThemeProvider theme={theme}>
{process.env.REACT_APP_GOOGLE ? <AppGoogle /> : <App />}
</ThemeProvider>
</Router>,
document.getElementById('root')
)