标签: Chrome扩展

Chrome扩展

  • 在Chrome扩展中使用Parse Platform-集成Gitee登录

    国内提供Oauth认证服务的好少,使用第三方登录降低了认证服务的复杂度,也降低了用户的决策难度,这里用Gitee作为一个示例。应该适用于大多数的Web应用,只有客户端获取authorization_code部分的代码可能不一致。Parse官方文档里的自定义认证写的语焉不详,折腾了好久。

    服务端

    1. Parse Server配置认证信息

    const gitee = require('./auth/gitee');
    
    {
      auth: {
       gitee: {
         module: gitee, // OR object,
         client_id: "", // Gitee第三方应用ID
         client_secret: "", // Gitee第三方应用的密钥
       }
      }
    }

    2. 实现自定义登录(AuthAdapter)

    主要需要实现两个函数:validateAuthData和validateAppId。

    const qs = require('querystring')
    const Parse = require('parse/node')
    
    async function getAuthData(code, redirect_uri, options) {
        const token = await codeToToken(code, redirect_uri, options)
        const user = await userInfo(token.token_type, token.access_token)
    
        return {
            id: user.id,
            access_token: token.access_token
        }
    }
    
    async function codeToToken(code, redirect_uri, options) {
        const url = `https://gitee.com/oauth/token`
        const query = qs.stringify({
            grant_type: 'authorization_code',
            code,
            redirect_uri,
            client_id: options.client_id,
            client_secret: options.client_secret
        })
        const res = await fetch(`${url}?${query}`, { method: 'POST' })
    
        return await res.json()
    }
    
    async function userInfo(type, token) {
        const url = 'https://gitee.com/api/v5/user'
    
        const res = await fetch(url, {
            method: 'GET',
            headers: {
                Authorization: `${type} ${token}`
            }
        })
    
        return await res.json()
    }
    
    // Returns a promise that fulfills if this user id is valid.
    async function validateAuthData(authData, options) {
        if (!authData.id) {
            throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少id字段');
        }
        if (!authData.access_token) {
            throw new Parse.Error(Parse.Error.OTHER_CAUSE, '缺少access_token字段');
        }
    
        const user = await userInfo('bearer', authData.access_token)
        if (user && String(user.id) === String(authData.id)) {
            return
        }
    
        throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'authData is invalid for this user.');
    }
    
    // Returns a promise that fulfills iff this app id is valid.
    function validateAppId(appIds, authData, options) {
        console.log('validateAppId:', appIds, authData, options)
        return Promise.resolve();
    }
    
    module.exports = {
        validateAuthData,
        validateAppId,
        getAuthData
    }

    3. 添加一个云函数,用于获取authData

    const gitee = require('../auth/gitee')
    const config = require('./config')
    
    Parse.Cloud.define("giteeAuthData", async (request) => {
        return await gitee.getAuthData(request.params.code, request.params.redirect_uri, config.auth.gitee)
    })

    客户端

    async function login() {
            const redirectUrl = chrome.identity.getRedirectURL()
            logger.log('redirect_url:', redirectUrl)
    
            // 获取认证码
            const authUrl = await _oauthCode(redirectUrl)
            if (authUrl === undefined) {
                throw new Error('login failed')
            }
            const url = new URL(authUrl)
            const code = url.searchParams.get('code')
            if (code == null) {
                throw new Error('login failed')
            }
            logger.log('code:', code)
    
            // 获取认证信息
            const authData = await _oauthData(code, redirectUrl)
    
            user.value = await Parse.User.logInWith('gitee', {
                authData
            })
    }
    
    
    async function _oauthData(code: string, redirect_uri: string) {
            const authData = await Parse.Cloud.run('giteeAuthData', {
                code,
                redirect_uri
            })
            logger.log('authData:', authData)
    
            return authData
    }

    参考

    [1] Parse 自定义认证文档地址, Parse Server Guide | Parse (parseplatform.org)

    [2] Gitee Oauth文档,Gitee OAuth 文档

  • 使用typescript开发chrome扩展

    记录一下使用typescript开发chrome扩展的相关配置。

    1. 安装依赖

    必定需要用到的开发依赖项:

    • chrome-types
    • copy-webpack-plugin
    • ts-loader
    • typescript
    • webpack-cli
    npm install chrome-types webpack-cli ts-loader typescript copy-webpack-plugin --save-dev

    2. 打包配置

    2.1 首先创建ts配置文件

    npx tsc --init

    2.2 创建webpack打包配置文件

    const path = require('path');
    const CopyPlugin = require("copy-webpack-plugin");
    
    module.exports = {
      mode: 'production',
      entry: {
        index: {
          import: './src/index.ts',
          filename: 'index.js'
        },
        background: {
          import: './src/background.ts',
          filename: 'background.js'
        }
      },
      plugins: [
        new CopyPlugin({
          patterns: [
            { from: "public", to: "" },
          ],
        }),
      ],
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            use: 'ts-loader',
            exclude: /node_modules/,
          },
        ],
      },
      resolve: {
        extensions: ['.tsx', '.ts', '.js'],
      },
      output: {
        clean: true
      },
    };

    3. 配置打包命令

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "build": "webpack build"
      }

    4. 基本目录结构截图

    chrome使用typescript目录结构
    chrome使用typescript目录结构
  • Google Chrome扩展开发

    Chrome扩展开发者控制台

    1. 需要支付一次性的5美元
    2. 需要使用非国内卡

    参考:

    1. Chrome开发者网站