本文接续前一篇 【教程】浏览器扩展中实现一键登录 Google(1),重点介绍代码相关的部分。
SSO 简介
为防止有些同学不了解 SSO,这里简单介绍一下 SSO 的基本原理:
- 在自己的网站/工具上启动登录流程
- 跳转到第三方网站(Single Site)进行登录
- 登录完成之后,被重定向回到自己的网站/工具,回来的地址会包含一些 token
- 拿 token 去第三方网站校验,如果成功,将用户信息写入本地系统
- 之后就可以用本地的用户信息进行交互
使用 SSO 的好处是:
- 减少用户心智负担,用户只需要在一个网站管理自己的用户信息即可
- 应用开发者不再需要费劲开发关于账号安全的功能
使用 chrome.identity
API 整合登录功能
Chrome Extension API 提供两个登录方式:
getAuthToken()
获得基于 Google 账号的 auth token,可以用来访问特定的 Google 服务,比如 Google Drive,在第三方浏览器比如 Edge 里不可用。launchWebAuthFlow()
使用上述 SSO 流程,在任何支持 SSO 的网站里完成登录。
因为我并不打算和 Chrome 深度绑定,所以自然,我们要用第二种。
由于我们要在浏览器扩展中 SSO,所以无法接受登录之后的重定向,所以上述步骤的(3)会变成在扩展里拿到 access_token,然后去 Supabase 换取用户身份信息,并存储在本地。
manifest.json
首先,我们要修改 manifest.json,将需要的 oauth2
信息添加进去。因为我用 CRXJS Vite Plugin,所以这里直接用 TS 来写就好:
{
permissions: [
'identity',
],
oauth2: {
'client_id': 'XXXX-XXXXXX.apps.googleusercontent.com',
// 我们只需要 openid 和 email 就够用了
'scopes': [
'openid',
'email',
],
},
}
Login 方法
接下来,完成登录函数,代码写出来大约是这样:
async function doLogin(): Promise<void> {
// 获取 manifest.json 内容,方便重用代码
const manifest = chrome.runtime.getManifest();
let url = new URL('<https://accounts.google.com/o/oauth2/auth>');
if (!manifest?.oauth2?.scopes) return;
url.searchParams.set('client_id', manifest.oauth2.client_id);
url.searchParams.set('response_type', 'id_token');
url.searchParams.set('access_type', 'offline');
url.searchParams.set('redirect_uri', `https://${chrome.runtime.id}.chromiumapp.org`);
url.searchParams.set('scope', manifest.oauth2.scopes.join(' '));
let redirectedTo;
try {
// 开启 SSO 弹窗,获取重定向地址
redirectedTo = await chrome.identity.launchWebAuthFlow({
url: url.href,
interactive: true,
});
} catch (e) {
return;
}
url = new URL(redirectedTo);
const params = new URLSearchParams(url.hash.replace(/^#/, ''));
// 用 token 去 supabase 进行验证
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: params.get('id_token') as string,
});
if (error) {
return;
}
userStore.setUser(data.user);
}
登录一定需要后端验证,但是在 Supabase SDK 的帮助下,我们可以完全跳过这部分开发。Supabase SDK 甚至贴心的提供了 signInWithIdToken()
,所以我强烈推荐各位独立开发者使用 Supabase 作为用户体系的基础。
要避开几个坑
这里有几个坑必须小心。
- 前面在 Google Cloud 里创建凭据时必须创建“Web应用”,“Chrome 扩展”针对的是
getAuthToken()
- Google Cloud 创建应用之后,要等很久才能用,我也不知道为什么。Google Cloud 给出的提示是 10 分钟到数小时,我建议一次配置好之后,至少等待 4 个小时再去开发,可以避免很多意外
- 不要使用
getRedirectURL()
,它会在末尾多带一个/
,导致登录失败。我也不知道为啥会有这么蠢的问题,但是调试几个小时之后,我不得不认为的确是这个问题……
给没有 Google 账户的用户提供兜底
总有一些用户没有 Google 账号,所以一般来说我们会给他们提供常规的邮箱+密码登录的方式作为兜底。
因为使用 Supabase,所以这里的功能实现也比较简单。Supabase 默认开启 Email provider,直接在代码增加一个登录函数即可。因为我们暂时不验证用户邮箱,所以在一个函数里帮用户完成注册/登录。表单我就不贴了,两个文本框一个按钮,很简单。
async function doLoginViaForm(): Promise<void> {
try {
const user = await userStore.register(email.value, password.value);
userStore.setUser(user);
} catch (e) {
const msg = (e as Error).message || Object.toString.call(e);
// if the user is already registered, we will try login
if (msg === 'User already registered') {
try {
const user = await userStore.login(email.value, password.value);
userStore.setUser(user);
} catch (e) {
message.value = (e as Error).message || Object.toString.call(e);
}
} else {
message.value = msg;
}
}
}
总结
至此,在浏览器扩展里集成 Google SSO,实现一键登录的功能就完成了。这个过程本身不复杂,但是有几个坑,还有不少易混淆的地方,我也花了不少时间在上面。希望我的这篇分享对大家有帮助。如果大家对浏览器扩展开发、Serverless 数据库使用、Vue 开发有什么问题,欢迎留言评论讨论。
感谢几位赞助商支持我创作技术分享,也请大家多多使用我的分享链接注册使用他们的服务。在顶部导航的“各种代理”里就能找到链接。谢谢大家。
欢迎吐槽,共同进步