更新oauth架构

This commit is contained in:
undefined committed 2020-08-03 20:10:05 +08:00
1 parent d184ca9ea1
commit b3af28dc43
7 files changed
+64 -132

No files matched your search

+4 -104
View File
@@ -1,4 +1,3 @@
import superagent from 'superagent';
import moment from 'moment-timezone';
import {
UserAlreadyExistError, InvalidTokenError, VerifyPasswordError,
@@ -22,12 +21,6 @@ import paginate from '../lib/paginate';
class UserLoginHandler extends Handler {
async get() {
const [loginWithGithub, loginWithGoogle] = await system.getMany(
['oauth.githubappid', 'oauth.googleappid'],
);
this.response.body = {
loginWithGithub, loginWithGoogle,
};
this.response.template = 'user_login.html';
}
@@ -222,109 +215,16 @@ class UserSearchHandler extends Handler {
}
class OauthHandler extends Handler {
async github() {
const [appid, [state]] = await Promise.all([
system.get('oauth.githubappid'),
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
]);
this.response.redirect = `https://github.com/login/oauth/authorize?client_id=${appid}&state=${state}`;
}
async google() {
const [appid, url, [state]] = await Promise.all([
system.get('oauth.googleappid'),
system.get('server.url'),
token.add(token.TYPE_OAUTH, 600, { redirect: this.request.referer }),
]);
this.response.redirect = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${appid}&response_type=code&redirect_uri=${url}oauth/google/callback&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&state=${state}`;
}
async get({ type }) {
if (type === 'github') await this.github();
else if (type === 'google') await this.google();
@param('type', Types.String)
async get(domainId: string, type: string) {
if (global.Hydro.lib[`oauth_${type}`]) await global.Hydro.lib[`oauth_${type}`].get.call(this);
}
}
class OauthCallbackHandler extends Handler {
@param('state', Types.String)
@param('code', Types.String)
async github(domainId: string, state: string, code: string) {
const [appid, secret, url, proxy, s] = await Promise.all([
system.get('oauth.githubappid'),
system.get('oauth.githubsecret'),
system.get('server.url'),
system.get('proxy'),
token.get(state, token.TYPE_OAUTH),
]);
const res = await superagent.post('https://github.com/login/oauth/access_token')
.proxy(proxy)
.send({
client_id: appid,
client_secret: secret,
code,
redirect_uri: `${url}oauth/github/callback`,
state,
})
.set('accept', 'application/json');
if (res.body.error) {
throw new UserFacingError(
res.body.error, res.body.error_description, res.body.error_uri,
);
}
const t = res.body.access_token;
const userInfo = await superagent.get('https://api.github.com/user')
.proxy(proxy)
.set('User-Agent', 'Hydro-OAuth')
.set('Authorization', `token ${t}`);
const ret = {
email: userInfo.body.email,
bio: userInfo.body.bio,
uname: [userInfo.body.name, userInfo.body.login],
};
this.response.redirect = s.redirect;
await token.del(s._id, token.TYPE_OAUTH);
return ret;
}
@param('state', Types.String)
@param('code', Types.String)
@param('error', Types.String)
async google(domainId: string, state: string, code: string, error: string) {
if (error) throw new UserFacingError(error);
const [
[appid, secret, url, proxy],
s,
] = await Promise.all([
system.getMany([
'oauth.googleappid', 'oauth.googlesecret', 'server.url', 'proxy',
]),
token.get(state, token.TYPE_OAUTH),
]);
const res = await superagent.post('https://oauth2.googleapis.com/token')
.proxy(proxy)
.send({
client_id: appid,
client_secret: secret,
code,
grant_type: 'authorization_code',
redirect_uri: `${url}oauth/google/callback`,
});
const payload = global.Hydro.lib.jwt.decode(res.body.id_token);
await token.del(state, token.TYPE_OAUTH);
this.response.redirect = s.redirect;
return {
email: payload.email,
uname: [payload.given_name, payload.name, payload.family_name],
viewLang: payload.locale.replace('-', '_'),
};
}
async get(args: any) {
let r;
// @ts-ignore
if (args.type === 'github') r = await this.github(args);
// @ts-ignore
else if (args.type === 'google') r = await this.google(args);
if (global.Hydro.lib[`oauth_${args.type}`]) r = await global.Hydro.lib[`oauth_${args.type}`].callback(args);
else throw new UserFacingError('Oauth type');
const udoc = await user.getByEmail('system', r.email);
if (udoc) {
-5
View File
@@ -129,11 +129,6 @@ SystemSetting(
Setting('setting_session', 'session.secure', null, false, 'boolean', 'session.secure'),
Setting('setting_session', 'session.saved_expire_seconds', null, 3600 * 24 * 30, 'number', 'Saved session expire seconds'),
Setting('setting_session', 'session.unsaved_expire_seconds', null, 3600 * 3, 'number', 'Unsaved session expire seconds'),
Setting('setting_oauth', 'oauth.githubappid', null, null, 'text', 'Github Oauth AppID'),
Setting('setting_oauth', 'oauth.githubsecret', null, null, 'text', 'Github Oauth Secret', null, FLAG_SECRET),
Setting('setting_oauth', 'oauth.googleappid', null, null, 'text', 'Google Oauth ClientID', null),
Setting('setting_oauth', 'oauth.googlesecret', null, null, 'text', 'Google Oauth Secret', null, FLAG_SECRET),
Setting('setting_proxy', 'proxy', null, null, 'text', 'Proxy Server URL'),
Setting('setting_storage', 'db.ver', null, 1, 'number', 'Database version', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'user', null, 1, 'number', 'User Counter', null, FLAG_DISABLED | FLAG_HIDDEN),
);
+9 -1
View File
@@ -4,7 +4,7 @@ import path from 'path';
import os from 'os';
import http from 'http';
import moment from 'moment-timezone';
import { isSafeInteger, Dictionary } from 'lodash';
import { isSafeInteger, Dictionary, filter } from 'lodash';
import { ObjectID } from 'mongodb';
import Koa from 'koa';
import morgan from 'koa-morgan';
@@ -232,6 +232,8 @@ export class Handler {
domain: DomainDoc;
loginMethods: any;
constructor(ctx: Koa.Context) {
this.ctx = ctx;
this.request = {
@@ -377,6 +379,12 @@ export class Handler {
}
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
this.UIContext.csrfToken = this.csrfToken;
this.loginMethods = filter(Object.keys(global.Hydro.lib), (str) => str.startsWith('oauth_'))
.map((key) => ({
id: key.split('_')[1],
icon: global.Hydro.lib[key].icon,
text: global.Hydro.lib[key].text,
}));
}
async finish() {
+46 -2
View File
@@ -2,8 +2,8 @@ import fs from 'fs';
import cluster from 'cluster';
import path from 'path';
import * as superagent from 'superagent';
import proxyAgent from 'proxy-agent';
import serialize from 'serialize-javascript';
import proxy from 'superagent-proxy';
import Lru from 'lru-cache';
declare global {
@@ -41,7 +41,51 @@ export function lrucache(target: any, funcName: string, obj: any) {
return obj;
}
proxy(superagent);
namespace Proxy {
function getUrl() {
return this._url;
}
function setUrl(v: string) {
this._url = v;
// eslint-disable-next-line no-use-before-define
proxy.call(this, this._proxyUri);
}
function setupUrl(req: superagent.Request) {
const desc = Object.getOwnPropertyDescriptor(req, 'url');
if (desc.get === getUrl && desc.set === setUrl) return;
// @ts-ignore
req._url = req.url;
desc.get = getUrl;
desc.set = setUrl;
delete desc.value;
delete desc.writable;
Object.defineProperty(req, 'url', desc);
}
function proxy(uri: string) {
if (!uri) return this;
setupUrl(this);
const agent = proxyAgent(uri);
if (agent) this.agent(agent);
this._proxyUri = uri;
return this;
}
export function setup(agent, uri = '') {
const Request = agent.Request;
if (Request) {
Request.prototype.proxy = proxy;
return agent;
}
return proxy.call(agent, uri);
}
}
declare module 'superagent' {
interface Request {
proxy(url: string): this;
}
}
Proxy.setup(superagent);
if (!cluster.worker) {
// @ts-ignore