谷歌授权登录注册调研
一、背景
鉴于项目需要面向海外用户,希望增加Google账户一键授权来注册/登陆产品,以获得更好的用户体验
三、登录流程
四、正确的接入姿势
1. google控制台配置
相关资料:google控制台配置最佳实践 google控制台
2. client端代码接入
参考官方javacript接入文档,同时支持HTML方式和Javascript方式的接入,经实践,vue项目更加推荐使用javascript方式接入(HTML接入存在js脚本加载顺序导致登录框初始化失败等问题,需要更复杂的处理)。 以下是以组件方式注入
o 主要逻辑包括:
-
加载谷歌平台库
-
初始化谷歌登录按钮+登录弹窗
-
添加用户登录回调
-
用户通过google授权成功后将结果传给BFF处理
xml
<template lang="pug">
div.ya-login-google
div#google-login
</template>
<script>
import { userLoginByGoogle } from '@/query/user.js'
import { googleLogin } from 'config/google.conf.js'
export default {
name: 'GoogleLogin',
data () {
return {
options: {
client_id: googleLogin['GOOGLE_CLIENT_ID'],
callback: this.handleCredentialResponse
}
}
},
mounted () {
//b.初始化谷歌登录按钮+登录弹窗
this.loginPopup()
this.renderButton()
},
methods: {
/**
* @description: google授权成功回调
* @param {object} response 验证回调结果
*/
async handleCredentialResponse (response) {//c.添加用户授权成功回调
//d.用户通过google授权成功后将结果传给BFF处理
this.$gqlRequest({
...userLoginByGoogle,
variables: {
googleCredential: JSON.stringify(response)
}
}).then(res => {
// eslint-disable-next-line no-console
console.log(res)
})
},
/**
* @description: google登录弹窗
*/
loginPopup () {
window.google.accounts.id.initialize(this.options)
window.google.accounts.id.prompt((notification) => {
if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
// try next provider if OneTap is not displayed or skipped
}
})
},
/**
* @description: 登录按钮展示和配置
*/
renderButton () {
window.google.accounts.id.renderButton(document.getElementById('google-login'), {
theme: 'outline',
size: 'large',
width: 400,
text: 'continue_with'
})
},
// 撤销身份认证
revoke () {
window.google.accounts.id.revoke('user@google.com', done => {})
}
}
}
</script>
google凭证响应回包包含的字段:
场地 | |
---|---|
credential |
此字段是返回的 ID Token。 |
select_by |
此字段设置选择凭据的方式。 |
bash
解析credential获得用户信息
curl https://oauth2.googleapis.com/tokeninfo?id_token=XXX
注意:虽然这里前端是可以通过解析credential 获得用户信息。但是不要将解析后的用户信息直接传递给后端接口,因为后端程序无法确定这个ID是用户真正通过登录得到的,还是其他某人的ID。所以正确使用token的姿势是不解析并直接将它传给后端去解析。
3. BFF端接入
主要步骤: 1、使用google服务 验证client 产生的Token 2、处理验证结果
csharp
loginByGoogle: async (_, args, { dataSources, ctx }) => {
//1、通过google服务验证登录信息,包括有效期等等
const googleCredential = args?.googleCredential || {}
const { credential, clientId } = JSON.parse(googleCredential)
const client = new OAuth2Client(clientId)
async function verify () {
const ticket = await client.verifyIdToken({
idToken: credential,
audience: clientId // Specify the CLIENT_ID of the app that accesses the backend
})
const { payload } = ticket
return payload
}
const data = await verify().catch()
//2、处理登录。包括旧用户关联,新用户注册
//3、页面跳转
return {
data,
'goto': 'login success !!!! redirect somewhere.....'
}
},
关于credential(Token) 这个Token是一个使用谷歌私钥签名的 JWT Token。是 base64 编码的 JSON Web 令牌 (JWT) 字符串的 ID 令牌 解析 JWT Token 的库:更推荐使用google官方的client.verifyIdToken 。
json
//JWT Token 就是一个字符串,它由 3 部分组成:头部,数据,签名。 头部是一个JSON对象,用于描述签名算法。
header
{
"alg": "RS256",
"kid": "f05415b13acb9590f70df862765c655f5a7a019e", // JWT signature
"typ": "JWT"
}
//数据也是一个JSON对象,保存公共数据和用户数据。签名是前两部分的校验和,用于确保该 Token 未被修改。
//3 个部分通过 Base64 + HMAC(常用的算法之一) 算法合并产生出一个字符串。
payload
{
"iss": "https://accounts.google.com", // The JWT's issuer
"nbf": 161803398874,
"aud": "314159265-pi.apps.googleusercontent.com", // Your server's client ID
"sub": "3141592653589793238", // The unique ID of the user's Google Account
"hd": "gmail.com", // If present, the host domain of the user's GSuite email address
"email": "elisa.g.beckett@gmail.com", // The user's email address
"email_verified": true, // true, if Google has verified the email address
"azp": "314159265-pi.apps.googleusercontent.com",
"name": "Elisa Beckett",
// If present, a URL to user's profile picture
"picture": "https://lh3.googleusercontent.com/a-/e2718281828459045235360uler",
"given_name": "Elisa",
"family_name": "Beckett",
"iat": 1596474000, // Unix timestamp of the assertion's creation time
"exp": 1596477600, // Unix timestamp of the assertion's expiration time
"jti": "abc161803398874def"
}
如果校验通过,就可以获取到用户相关的数据
o sub
是用户的唯一ID
o email 是用户的 Email
o name
是用户的名字
o picture
是用户的头像
验证通过后,按照不同情况的处理的建议
o 未注册的电子邮件地址:如果需要,可以显示一个注册用户界面 (UI),允许用户提供其他个人资料信息,又或者直接创建用户,用户自行更新个人信息。
o 对应用户已经存在:调用后端登录接口,保存登录状态,前端跳转对应页面
到此为至,已完成谷歌登录的接入。总结接入的步骤包括:
1.google官网先配置,先创建 ClientID
2.加载谷歌平台脚本
3.添加谷歌登录按钮
4.登录回调处理
5.与后端API接口通信
6.后端解析 ID Token 中的用户数据
7.处理数据返回结果
官方js方式接入文档 :developers.google.com/identity/gs...
其他问题:
1、服务器暂时无法正常请求Google服务
Q1:解决办法
BFF端使用代理
javascript
const gaxios = require('gaxios')
const HttpsProxyAgent = require('https-proxy-agent')
gaxios.instance.defaults = {
agent: new HttpsProxyAgent(`${proxy_url}`)
}
测试代理是否生效
curl -x方式
perl
$ curl -x http://127.0.0.1:7890 https://www.googleapis.com
nodejs方式:
javascript
gaxios.request({
agent: new HttpsProxyAgent('http://127.0.0.1:7890')
url: `https://oauth2.googleapis.com/tokeninfo?id_token=${url}`
}).then(res => {
console.log('success', res.data)
}).catch(err => {
console.log('error', err)
})