标签: chrome.identity

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

    【教程】浏览器扩展中实现一键登录 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 开发有什么问题,欢迎留言评论讨论。


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

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

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

    本文分享我最近开发 AutomateGPT 扩展时集成 Google SSO 的经验。除了 Google 外,我还用到 Supabase 提供的用户管理与登录功能。

    本来想一篇博客搞定,没想到写着写着就超长了,那就拆成两篇吧,哈哈。

    内容简介

    开发浏览器扩展的时候,我们有时候需要建立用户体系,以便更好的服务用户。此时我们有多个选择:

    1. 让用户使用用户名密码登录,提供完整的注册、登录、验证邮箱、忘记密码等
    2. 让用户使用 SSO 登录

    很明显,第二种更好,因为我们不需要建立用户注册、校验等一系列功能,只要用第三方提供的用户身份标记就好。这样一来可以减少我们的开发成本,二来可以利用现有的互联网基建,对用户来说也更省事。

    作为面向 ChatGPT 用户的辅助应用,AutomateGPT 选择用户体系时,我很自然的选择了 Google。

    吐槽

    Google 是个让人又爱又恨的公司,一方面他们提供大量免费的互联网基建,给我们开发带来巨大帮助;另一方面,由于摊子铺的太大,各种年久失修或者考虑不周,导致我们经常要自己踩坑自己摸索才能最终完成想要的作品。

    这次也是。Google 提供的浏览器扩展开发文档包含一些错误和遗漏;Google Cloud 又是完全的黑盒,导致我折腾了将近两天才把这项功能做好。

    行吧,闲言少叙,下面开始正文。

    在 Chrome Web Store 里占个坑

    因为 Google SSO 要跟 Extension ID 绑定在一起,所以我们要先去 Chrome Web Store 里占个坑,保证以后扩展无论安装在哪里,都是统一的 ID。

    创建浏览器扩展项目

    我先假定大家都有一些扩展开发经验,可以自行创建浏览器扩展。如果你需要从零学起,我刚好做过一期相关视频,可以方便你快速上手:

    肉山小教程-浏览器扩展开发-快速入门_哔哩哔哩_bilibili

    创建完毕,在本地调试没问题之后,就打包,上传 Chrome Web Store。此时我们不需要添加太多功能,只要能加载,能看到效果就行,并不是最终发布,所以不用太担心。

    CWS 里创建应用

    假定各位读者已经拥有 Google 账号,那么就请进入开发者信息中心(Developer Dashboard)。

    接着点击“上传新内容“,上传刚才准备好的压缩包,稍等片刻,CWS 会帮我们创建一个应用,此时就能看到应用 ID 了,大约如图所示:

    具体的 ID 不重要,点击图中红框的 View public key,将 key 复制下来,粘贴到插件的 manifest.json 里,记得要把换行删掉:

    {
    "key": "刚才复制的 key,不能有换行符"
    }

    但是 manifest.json 加了 key 之后,为生产环境构建发版时可能会出问题(因为本地没有私钥,无法信任公钥)。此时,如果你用了我以前推荐的 CRXJS Vite Plugin,就很简单,判断一下当前环境即可:

    export default defineManifest(async function (env) {
    return {
    ...process.env.NODE_ENV === 'development' && {
    key: '刚才复制的 key,不能有换行符',
    },
    };
    });

    所以,推荐使用 Vite + CRXJS Vite Plugin 构建浏览器扩展开发环境。如果你还不会使用这个插件,建议看我另一个视频:

    2024 浏览器扩展开发必备:CRXJS Vite Plugin_哔哩哔哩_bilibili

    为了验证效果,可以在浏览器里删除之前导入的测试扩展,然后重新加载改好 key 的版本。如果之后看到的 ID 与你在 CWS 开发者中心看到的一致,那就说明操作成功。

    在 Google Cloud 里完成配置

    接下来,我们需要在 Google Cloud 里完成配置。如果你已经有项目,那么沿用之前的项目也没问题。如果没有的话,那就新建一个。目前 Google Cloud 还是免费的,新用户更是有 $300 的试用额度,可以放心体验。

    同样,我假定各位读者已经拥有 Google 账号,那么打开 Google Cloud Console,并按照向导指引创建即可。

    创建完成,点击左上角的菜单按钮,从里面选择“API和服务”,然后,选择“OAuth 同意屏幕”,跟随引导创建 OAuth 同意屏幕。注意,因为新应用默认采用白名单,为了之后普通用户能够正常注册登录,最好把应用设置为“已发布”。

    然后,进入“凭据”页面,添加登录凭证。点上面的“创建凭据“,然后选”OAuth 客户端 ID”。注意,虽然 Google 给我们准备了”Chrome 扩展程序“这个选项,但是不能选,要选择”Web 应用“。

    然后在”已获授权的重定向 URI”里填入 https://{刚才获得的 Extension ID}.chromiumapp.org,创建凭证。然后你会得到一个形如 xxxx-ooooo.apps.googleusercontent.com 的客户端 ID。

    在 Supabase 里注册应用

    用户登录免不了用到一些通用功能,比如用户管理、用户信息存储等等。开发这些功能免不了需要很多工作量,我觉得自己做并不合算。所以我建议大家选用一些第三方服务。

    我目前比较喜欢使用 Supabase,大概有几点优势:

    1. 免费 500MB 数据库和若干存储、带宽
    2. 很好的整合了用户授权体系,支持常见平台的 SSO
    3. 各种常见框架都有 SDK,包括我最常用的 Nuxt
    4. 行级权限管理,方便作为 Serverless 数据库使用
    5. PostgreSQL,插件生态丰富,接入 pgvector 就能作为矢量数据库

    简而言之,下限有保证,上限有空间,非常推荐。使用 Supabase 集成 Google SSO 非常简单。另外,因为 Supabase 的界面比较友好,不像 Google Cloud 那么反人类,我就不截图了,相信大家很容易找到。

    1. 注册账号
    2. 创建项目
    3. Authentication > Providers
    4. Enable Google,然后将上一步获得的客户端 ID 填入 “Authorized Client IDs (for Android, One Tap, and Chrome extensions) ”里面即可

    小结

    本文主要介绍了开发环境的配置。下篇博客会讲解如何写代码,并解释整个流程的原理,方便大家适配其它 SSO provider。

    如果大家对浏览器扩展开发有什么问题和想法,欢迎留言提问、讨论。


    AutomateGPT 是我们最近开发的 ChatGPT 增强扩展,方便大家更好的使用 ChatGPT,充分利用包月的价值。它能够帮你重复执行多个 prompt;也可以分解大文件,拆成块依次处理(开发中)。可以用在大文本翻译、批量生成内容、网站分析等领域。欢迎大家使用,反馈问题和意见,谢谢。