【教程】浏览器扩展中实现一键登录 Google(2)

本文接续前一篇 【教程】浏览器扩展中实现一键登录 Google(1),重点介绍代码相关的部分。

SSO 简介

为防止有些同学不了解 SSO,这里简单介绍一下 SSO 的基本原理:

  1. 在自己的网站/工具上启动登录流程
  2. 跳转到第三方网站(Single Site)进行登录
  3. 登录完成之后,被重定向回到自己的网站/工具,回来的地址会包含一些 token
  4. 拿 token 去第三方网站校验,如果成功,将用户信息写入本地系统
  5. 之后就可以用本地的用户信息进行交互

使用 SSO 的好处是:

  1. 减少用户心智负担,用户只需要在一个网站管理自己的用户信息即可
  2. 应用开发者不再需要费劲开发关于账号安全的功能

使用 chrome.identity API 整合登录功能

Chrome Extension API 提供两个登录方式:

  1. getAuthToken() 获得基于 Google 账号的 auth token,可以用来访问特定的 Google 服务,比如 Google Drive,在第三方浏览器比如 Edge 里不可用。
  2. 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 作为用户体系的基础。

要避开几个坑

这里有几个坑必须小心。

  1. 前面在 Google Cloud 里创建凭据时必须创建“Web应用”,“Chrome 扩展”针对的是 getAuthToken()
  2. Google Cloud 创建应用之后,要等很久才能用,我也不知道为什么。Google Cloud 给出的提示是 10 分钟到数小时,我建议一次配置好之后,至少等待 4 个小时再去开发,可以避免很多意外
  3. 不要使用 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 开发有什么问题,欢迎留言评论讨论。


感谢几位赞助商支持我创作技术分享,也请大家多多使用我的分享链接注册使用他们的服务。在顶部导航的“各种代理”里就能找到链接。谢谢大家。

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

,

来自

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据