前端接入 Google OAuth 2.0 三方授权登录

因海外业务拓展,项目需要支持 Google、Facebook、Twitter、Line、Apple 这些第三方授权登录。目前第三方的授权登录有很多方案,且都有官方提供 SDK 方便接入。但由于我这个项目需要同时支持H5、iOS、Android,所以选择在不接入官方提供的 SDK 的情况下,配合后端为 Web 网页实施基于浏览器的授权登录

本文围绕 Google OAuth 2.0 展开。当然,不同厂商的流程有些许区别,文章也会有所提及。

OAuth 2.0 简介

OAuth 即 Open Authorization,是"开放授权"的简写,它是一个关于授权(authorization)的开放网络标准。一句话,OAuth2.0 是一种授权协议。

无论使用哪种三方平台,最重要的一步就是获取三方授权的 token,当然不同的厂商叫法不一样,有的叫 access_token,有的叫 oauth_token,还有的叫 code,无所吊谓。关键是拿到这个凭证后,你就有权限继续去三方获取用户的昵称、头像、动态、好友列表之类的信息。当然,权限也是有范围和有效期的。

对 OAuth 2.0 感兴趣的,可以看一些社区相关文章:

前端接入 Google

首先,我们要去谷歌开发者备案,创建项目并按提示填写信息。其中有两步是开发过程中会用到的,一个是 创建 OAuth 客户端 ID,该步骤会生成 client_id。还有一个是 已获授权的重定向 URI,该步骤能在授权成功后,携带 token 重定向你设置的 URI 上。

具体后台配置推荐直接看这篇文章 👉 谷歌OAuth2.0开发的正确配置步骤

Google Web 端应用流程

整个 OAuth 的授权登录流程如下:

我们着重看下和 OAuth 相关的第二、三两步(其他几步都是后端给出 api 接口,前端调用即可)。

Google OAuth 2.0 参数

在访问 Google 授权登录页之前,需要准备以下必传网址参数。

参数 是否必传 说明
client_id Y 客户端 ID
redirect_uri Y 完成授权后,API 服务器会将用户重定向到该地址
response_type Y JS 设为 'token'
scope Y 授权范围
state N(推荐) 维护授权请求和授权服务器状态的任何字符串值,防止 CSRF 攻击

response_type:设为 'token' 的意思是,授权后的 token 令牌将以 access_token=abcxxx 键值对的格式返回,并且会以 fragments 网址片段(即 location.hash,不是 location.search)的形式拼接到重定向的 URI 后面。

scope:这个授权范围比较重要,令人吐槽的是官方文档说了一大堆,示例里还写错了(不管用),我们这里应该填写 https://www.googleapis.com/auth/userinfo.profile。至于我是怎么找到的,可以看下面[OAuth 2.0 Playground](#OAuth 2.0 Playground "#OAuth-2.0-Playground")的演示。

state:防止 redirect_uri 被别人猜到,确保请求和响应源自同一浏览器,从而防范 CSRF 跨站请求伪造等攻击。前端可以使用 uuid通用唯一识别码)来生成 state。

其他可选参数,详见 👉 获取 OAuth 2.0 访问令牌

OAuth 2.0 Playground

如需查看关于将 OAuth 2.0 与 Google 搭配使用(包括使用您自己的客户端凭据的选项)的交互式演示,请试用 OAuth 2.0 Playground。

官网给了个 Playground 演练场,我们可以在里面找到对应的授权服务,模拟授权登录场景,Google 会显示出对应的响应信息,方便我们进行调试,这一点开始很贴心的。

step 1 选择授权 API

选择 Google OAuth 2 API V2,并按需要勾选授权范围,我们这里全部勾选,并点击 Authorize APIs 看看会发生什么。

随后,会拉起 Google 登录页面,我们登录即可。

step 2 生成 access_token

调用 API 后,会来到第二步,右边会显示刚刚的请求响应信息。主要看下 Request,会发现其中的 scope,我们解析出来看看是什么:

js 复制代码
decodeURIComponent('scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid')

// outout:
'scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+openid'

可以看到是由三部分拼接而成,对应我们第一步中选中的 emailprofileopenid

所以,这就是我为什么说文档里写的示例不管用的原因了,文档示例如下:

js 复制代码
https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

它给的是 https://www.googleapis.com/auth/drive.metadata.readonly,但其实也不能说它错,只能说它没有生效,因为这里面有一个增量授权的问题:

在登录时,应用可能会请求 openidprofile 范围来执行基本登录,然后在第一次请求保存时混合请求 https://www.googleapis.com/auth/drive.file 范围。

所以,在我理解授权登录还需要额外指定 userinfo.profileopenid,所以不能只填写文档示例中的链接,而应该填写 Playground 中给出的链接。但是 userinfo.profileopenid 额外的授权范围会最终追加到 auth/drive.file 中。

最后,我们点击 exchange authorization code for tokens,Playground 就会给我们生成 access_token 以及其他信息。

模拟结束,其实到这里就已经成功了。

哈希片段获取access_token

从 Google 那边授权成功后,会返回到前端指定的 redirect_uri 页面中,我们可以从 URL 地址的哈希片段(注意是 hash,不是 search)中拿到 access_token,并传给后端。

js 复制代码
// utils:获取 location.search/hash 中的值
const searchParams = (search, ...keys) => {
    search = search.replace(/#|\?/, '');
    const params = new URLSearchParams(search);

    return flatMap(keys).reduce((prev, key) => {
        prev[key] = params.get(key) || undefined;
        return prev;
    }, {});
};

// redirect_uri 页面
export default {
    created() {
        // 成功
        if (/code|token/.test(this.$route.fullPath)) {
            const tokenInfo = this.getAccessToken();
            // 拿着 tokenInfo 请求后端接口······
        }
    },
    methods: {
       // 获取 access_token
        getAccessToken() {
            const { access_token: googleToken } = searchParams(
                this.$route.hash,
                'access_token'
            );
            return { googleToken };
        }
    }
}

错误处理

如果 Google 授权失败,那么根据文档说明,会携带 error 字段:

js 复制代码
https://oauth2.example.com/callback#error=access_denied
js 复制代码
// redirect_uri 页面
export default {
    created() {
        // 成功···见上
        // 失败:
        const { error } = searchParams(this.$route.hash, 'error');
        if (error) {
            // 处理失败······
        }
    }
}

Facebook、Line、Apple 配置大全

最后,我们来看看其他几个三方都需要哪些参数,主要区别在于参数名称、返回方式(hash 还是 search)、返回字段名称等,仅供参考。

三方 授权请求地址 请求参数 响应类型 成功响应 失败响应
Google accounts.google.com/o/oauth2/v2... client_id redirect_uri response_type: 'token' scope: 'www.googleapis.com/auth/userin...' hash 片段 access_token error
Facebook www.facebook.com/v17.0/dialo... client_id redirect_uri response_type: 'token' hash 片段 access_token error_description
Apple appleid.apple.com/auth/author... client_id redirect_uri response_type: 'code id_token' response_mode: 'fragment' hash 片段 code id_token error
Line access.line.me/dialog/oaut... client_id redirect_uri response_type: 'code' search 片段 code error_description errorMessage 等

其中,Line 有点特别。在获取到 code 后,还需要拿着 code 调用接口换取 access_token

bash 复制代码
curl -v -X POST https://api.line.me/v2/oauth/accessToken \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=b5fd32eacc791df' \
-d 'redirect_uri=https%3A%2F%2Fexample.com%2Fauth' \
-d 'client_id=12345' \
-d 'client_secret=d6524edacc8742aeedf98f'
json 复制代码
{
    "access_token": "bNl4YEFPI/hjFWhTqexp4MuEw5YPs7qhr6dJDXKwNPuLka...",
    "expires_in": 2591977,
    "refresh_token": "8iFFRdyxNVNLWYeteMMJ",
    "scope": "P",
    "token_type": "Bearer"
}

这一步,只能交给后端来处理,前端会有跨域问题。

此外,针对表格还有几点需要说明:

  1. 这里仅罗列必传参数,完整参数还请查阅参考链接中的官方文档;
  2. 有些三方返回方式是由参数 response_type 决定而非是固定的。比如 Facebook,如果 response_type=code,那么所包含的响应数据为网址参数形式(search),而不会是网址片段形式(hash);
  3. Twitter 我的项目中用的是 1.0,所以就不写入其中了,不过 2.0 的文档也给在了下面。

总结

本文以 Google OAuth 2.0 为例,给大家分享了前端不使用 SDK 时,接入三方授权的流程与步骤。其实步骤并不是很繁琐,善于查阅文档并通晓其使用流程就能正确接入三方服务了。

参考资料

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax