update
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
class Cache {
|
||||
constructor() {
|
||||
this.kv = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param {boolean?} noOverride
|
||||
*/
|
||||
set(key, value, noOverride) {
|
||||
if (noOverride && key in this.kv) {
|
||||
throw new Error("key exists");
|
||||
}
|
||||
|
||||
this.kv[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @return {string?}
|
||||
*/
|
||||
get(key) {
|
||||
return this.kv[key] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} keys
|
||||
*/
|
||||
del(...keys) {
|
||||
for (const key of keys) {
|
||||
delete this.kv[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cache = null;
|
||||
|
||||
exports.new = function () {
|
||||
if (cache) throw new Error("cache already initiate");
|
||||
cache = new Cache();
|
||||
return cache;
|
||||
};
|
||||
|
||||
exports.get = function () {
|
||||
if (!cache) throw new Error("cache not initiate");
|
||||
return cache;
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
const knex = require('knex');
|
||||
const config = require('src/config/index.js');
|
||||
|
||||
const pool = knex({
|
||||
client: 'pg',
|
||||
connection: {
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
host: config.database.host,
|
||||
port: config.database.port,
|
||||
database: config.database.dbname,
|
||||
},
|
||||
pool: {
|
||||
max: config.database.pool_max,
|
||||
min: config.database.pool_min,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = pool;
|
||||
@@ -1,48 +0,0 @@
|
||||
const IORedis = require("ioredis");
|
||||
const config = require("src/config/index.js");
|
||||
|
||||
class Redis extends IORedis {
|
||||
constructor() {
|
||||
let { prefix } = config.redis;
|
||||
const { host, port, password, db } = config.redis;
|
||||
if (prefix && !/:$/.test(prefix)) prefix += ":";
|
||||
super({
|
||||
host,
|
||||
port,
|
||||
password,
|
||||
db,
|
||||
});
|
||||
|
||||
this.prefix = prefix;
|
||||
|
||||
const self = this;
|
||||
// key pattern functions
|
||||
this.Key = {
|
||||
/**
|
||||
* SSO 登入暫存
|
||||
* @param {string} s state
|
||||
* @return {string}
|
||||
*/
|
||||
ssoLoginCache: (s) => self.getKeyWithPrefix(`sso-login:${s}`),
|
||||
/**
|
||||
* 儲存 Token
|
||||
* @param {string} s state
|
||||
* @return {string}
|
||||
*/
|
||||
userToken: (s) => self.getKeyWithPrefix(`token:${s}`),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* combine key and prefix
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
getKeyWithPrefix(s) {
|
||||
if (typeof s !== "string") throw new Error("input key not a string");
|
||||
|
||||
return `${this.prefix}${s}`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Redis();
|
||||
+44
-27
@@ -1,9 +1,9 @@
|
||||
const joi = require('joi');
|
||||
const url = require('url');
|
||||
const querystring = require('querystring');
|
||||
const got = require('got');
|
||||
const config = require('src/config/index.js');
|
||||
const { jwt } = require('src/utils/pkgs.js');
|
||||
const joi = require("joi");
|
||||
const url = require("url");
|
||||
const querystring = require("querystring");
|
||||
const got = require("got");
|
||||
const config = require("src/config/index.js");
|
||||
const { jwt } = require("src/utils/pkgs.js");
|
||||
|
||||
const mod = {};
|
||||
module.exports = mod;
|
||||
@@ -11,14 +11,14 @@ module.exports = mod;
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
mod.getAuthURL = state => {
|
||||
mod.getAuthURL = (state) => {
|
||||
const input = joi
|
||||
.object({
|
||||
authorized_endpoint: joi.string().required(),
|
||||
token_endpoint: joi.string().required(),
|
||||
client_id: joi.string().required(),
|
||||
client_secret: joi.string().required(),
|
||||
state: joi.string().allow('', null).default(''),
|
||||
state: joi.string().allow("", null).default(""),
|
||||
})
|
||||
.unknown()
|
||||
.validate({ ...config.sso, state });
|
||||
@@ -29,12 +29,12 @@ mod.getAuthURL = state => {
|
||||
*/
|
||||
const { value } = input;
|
||||
|
||||
const redirectUri = new url.URL('/oauth/redirect', config.server.url);
|
||||
const redirectUri = new url.URL("/oauth/redirect", config.server.url);
|
||||
|
||||
const qs = {
|
||||
client_id: value.client_id,
|
||||
scope: 'openid',
|
||||
response_type: 'code',
|
||||
scope: "offline_access",
|
||||
response_type: "code",
|
||||
redirect_uri: redirectUri.toString(),
|
||||
};
|
||||
if (value.state) qs.state = state;
|
||||
@@ -53,13 +53,20 @@ mod.getLogoutURL = () => {
|
||||
.unknown()
|
||||
.validate({ ...config.sso });
|
||||
if (input.error) throw new Error(input.error.message);
|
||||
const redirectUri = new url.URL('/oauth/redirect', config.server.url);
|
||||
const redirectUri = new url.URL("/oauth/redirect", config.server.url);
|
||||
|
||||
const qs = { state: 'logout', redirect_uri: redirectUri.toString() };
|
||||
const qs = { state: "logout", redirect_uri: redirectUri.toString() };
|
||||
|
||||
return `${input.value.logout_endpoint}?${querystring.stringify(qs)}`;
|
||||
};
|
||||
|
||||
mod.getUserInfo = async (token) => {
|
||||
const input = joi
|
||||
.object()
|
||||
.unknown()
|
||||
.validateAsync({ ...config.sso, token });
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef SSOAccount
|
||||
* @property {string} access_token
|
||||
@@ -90,7 +97,7 @@ mod.getToken = async (code, state) => {
|
||||
*/
|
||||
const { value } = input;
|
||||
|
||||
const redirectUri = new url.URL('/oauth/redirect', config.server.url);
|
||||
const redirectUri = new url.URL("/oauth/redirect", config.server.url);
|
||||
|
||||
const qs = {
|
||||
client_id: value.client_id,
|
||||
@@ -98,32 +105,42 @@ mod.getToken = async (code, state) => {
|
||||
redirect_uri: redirectUri.toString(),
|
||||
code: value.code,
|
||||
client_session_state: value.state,
|
||||
grant_type: 'authorization_code',
|
||||
grant_type: "authorization_code",
|
||||
};
|
||||
|
||||
const resp = await got.default.post(value.token_endpoint, {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
body: querystring.stringify(qs),
|
||||
responseType: 'json',
|
||||
responseType: "json",
|
||||
});
|
||||
|
||||
const { body } = resp;
|
||||
if (!body) throw new Error('resopnse body empty');
|
||||
if (!body) throw new Error("resopnse body empty");
|
||||
|
||||
const { id_token: idToken, access_token: accessToken, refresh_token: refreshToken } = body;
|
||||
if (!idToken) throw new Error('get id token fail');
|
||||
const {
|
||||
id_token: idToken,
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
} = body;
|
||||
// if (!idToken) throw new Error("get id token fail");
|
||||
|
||||
const decoded = jwt.decode(idToken);
|
||||
if (!decoded || typeof decoded !== 'object') throw new Error('jwt decode fail');
|
||||
console.log('decoded ::: ', decoded)
|
||||
console.log('body ::: ', body)
|
||||
// const decoded = jwt.decode(idToken);
|
||||
// if (!decoded || typeof decoded !== "object")
|
||||
// throw new Error("jwt decode fail");
|
||||
// console.log("decoded ::: ", decoded);
|
||||
|
||||
const decoded = jwt.decode(accessToken);
|
||||
// decode access token
|
||||
console.log("token ::: ", jwt.decode(accessToken));
|
||||
|
||||
console.log("body ::: ", body);
|
||||
// @ts-ignore
|
||||
const { preferred_username: preferredUsername } = decoded;
|
||||
if (!preferredUsername) throw new Error('id token field missing');
|
||||
if (!preferredUsername) throw new Error("id token field missing");
|
||||
|
||||
const displayName = `${decoded.family_name ?? ''}${decoded.given_name ?? ''}`;
|
||||
const displayName = `${decoded.family_name ?? ""}${decoded.given_name ?? ""}`;
|
||||
|
||||
/** @type {SSOAccount} */
|
||||
const ssoAccount = {
|
||||
@@ -132,7 +149,7 @@ mod.getToken = async (code, state) => {
|
||||
user_id: decoded.sub,
|
||||
username: preferredUsername.toLowerCase(),
|
||||
display_name: displayName ?? preferredUsername,
|
||||
email: decoded.email ?? '',
|
||||
email: decoded.email ?? "",
|
||||
};
|
||||
|
||||
return ssoAccount;
|
||||
|
||||
Reference in New Issue
Block a user