在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 文档