mirror of https://github.com/msgbyte/tailchat
feat: add fim plugin and add github oauth strategy
parent
8fc96b7918
commit
b64d037b60
@ -0,0 +1,14 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
externalDeps: [
|
||||||
|
'react',
|
||||||
|
'react-router',
|
||||||
|
'axios',
|
||||||
|
'styled-components',
|
||||||
|
'zustand',
|
||||||
|
'zustand/middleware/immer',
|
||||||
|
],
|
||||||
|
pluginRoot: path.resolve(__dirname, './web'),
|
||||||
|
outDir: path.resolve(__dirname, '../../public'),
|
||||||
|
};
|
@ -0,0 +1,20 @@
|
|||||||
|
import { db } from 'tailchat-server-sdk';
|
||||||
|
const { getModelForClass, prop, modelOptions, TimeStamps } = db;
|
||||||
|
|
||||||
|
@modelOptions({
|
||||||
|
options: {
|
||||||
|
customName: 'p_fim',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class Fim extends TimeStamps implements db.Base {
|
||||||
|
_id: db.Types.ObjectId;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FimDocument = db.DocumentType<Fim>;
|
||||||
|
|
||||||
|
const model = getModelForClass(Fim);
|
||||||
|
|
||||||
|
export type FimModel = typeof model;
|
||||||
|
|
||||||
|
export default model;
|
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "tailchat-plugin-fim",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"author": "moonrailgun",
|
||||||
|
"description": "Unified identity authentication",
|
||||||
|
"license": "MIT",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"build:web": "ministar buildPlugin all",
|
||||||
|
"build:web:watch": "ministar watchPlugin all"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "18.0.20",
|
||||||
|
"mini-star": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"got": "^11.8.3",
|
||||||
|
"tailchat-server-sdk": "*"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { TcService, TcDbService, TcPureContext } from 'tailchat-server-sdk';
|
||||||
|
import type { FimDocument, FimModel } from '../models/fim';
|
||||||
|
import { strategies } from '../strategies';
|
||||||
|
import type { StrategyType } from '../strategies/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Federated Identity Management
|
||||||
|
*
|
||||||
|
* Unified identity authentication
|
||||||
|
*/
|
||||||
|
interface FimService extends TcService, TcDbService<FimDocument, FimModel> {}
|
||||||
|
class FimService extends TcService {
|
||||||
|
get serviceName() {
|
||||||
|
return 'plugin:com.msgbyte.fim';
|
||||||
|
}
|
||||||
|
|
||||||
|
onInit() {
|
||||||
|
// this.registerLocalDb(require('../models/fim').default);
|
||||||
|
|
||||||
|
strategies
|
||||||
|
.filter((strategy) => strategy.checkAvailable())
|
||||||
|
.map((strategy) => {
|
||||||
|
const action = this.buildStrategyAction(strategy);
|
||||||
|
const name = strategy.name;
|
||||||
|
this.registerAction(`${name}.url`, action.url);
|
||||||
|
this.registerAction(`${name}.redirect`, action.redirect);
|
||||||
|
|
||||||
|
this.registerAuthWhitelist([`/${name}/url`, `/${name}/redirect`]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buildStrategyAction(strategy: StrategyType) {
|
||||||
|
return {
|
||||||
|
url: async (ctx: TcPureContext) => {
|
||||||
|
return strategy.getUrl();
|
||||||
|
},
|
||||||
|
redirect: async (ctx: TcPureContext<{ code: string }>) => {
|
||||||
|
const code = ctx.params.code;
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
throw new Error(JSON.stringify(ctx.params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return strategy.getUserInfo(code);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FimService;
|
@ -0,0 +1,54 @@
|
|||||||
|
import { config } from 'tailchat-server-sdk';
|
||||||
|
import type { StrategyType } from './types';
|
||||||
|
import got from 'got';
|
||||||
|
|
||||||
|
const clientInfo = {
|
||||||
|
id: process.env.FIM_GITHUB_ID,
|
||||||
|
secret: process.env.FIM_GITHUB_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
const authorize_uri = 'https://github.com/login/oauth/authorize';
|
||||||
|
const access_token_uri = 'https://github.com/login/oauth/access_token';
|
||||||
|
const userinfo_uri = 'https://api.github.com/user';
|
||||||
|
const redirect_uri = `${config.apiUrl}/api/plugin:com.msgbyte.fim/github/redirect`;
|
||||||
|
|
||||||
|
export const GithubStrategy: StrategyType = {
|
||||||
|
name: 'github',
|
||||||
|
checkAvailable: () => !!clientInfo.id && !!clientInfo.secret,
|
||||||
|
getUrl: () => {
|
||||||
|
return `${authorize_uri}?client_id=${clientInfo.id}&redirect_uri=${redirect_uri}`;
|
||||||
|
},
|
||||||
|
getUserInfo: async (code) => {
|
||||||
|
console.log('authorization code:', code);
|
||||||
|
|
||||||
|
const tokenResponse = await got(access_token_uri, {
|
||||||
|
method: 'POST',
|
||||||
|
searchParams: {
|
||||||
|
client_id: clientInfo.id,
|
||||||
|
client_secret: clientInfo.secret,
|
||||||
|
code: code,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
accept: 'application/json',
|
||||||
|
},
|
||||||
|
}).json<{ access_token: string }>();
|
||||||
|
|
||||||
|
const accessToken = tokenResponse.access_token;
|
||||||
|
console.log(`access token: ${accessToken}`);
|
||||||
|
|
||||||
|
const result = await got(userinfo_uri, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
accept: 'application/json',
|
||||||
|
Authorization: `token ${accessToken}`,
|
||||||
|
},
|
||||||
|
}).json<{ id: number; name: string; email: string; avatar_url: string }>();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: String(result.id),
|
||||||
|
nickname: result.name,
|
||||||
|
email: result.email,
|
||||||
|
avatar: result.avatar_url,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,3 @@
|
|||||||
|
import { GithubStrategy } from './github';
|
||||||
|
|
||||||
|
export const strategies = [GithubStrategy];
|
@ -0,0 +1,11 @@
|
|||||||
|
export interface StrategyType {
|
||||||
|
name: string;
|
||||||
|
checkAvailable: () => boolean;
|
||||||
|
getUrl: () => string;
|
||||||
|
getUserInfo: (code: string) => Promise<{
|
||||||
|
id: string;
|
||||||
|
nickname: string;
|
||||||
|
email: string;
|
||||||
|
avatar: string;
|
||||||
|
}>;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "Federated Identity Management",
|
||||||
|
"name": "com.msgbyte.fim",
|
||||||
|
"url": "{BACKEND}/plugins/com.msgbyte.fim/index.js",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"author": "moonrailgun",
|
||||||
|
"description": "Unified identity authentication",
|
||||||
|
"requireRestart": true
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@plugins/com.msgbyte.fim",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Unified identity authentication",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"sync:declaration": "tailchat declaration github"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/styled-components": "^5.1.26",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"styled-components": "^5.3.6"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
console.log('Plugin Federated Identity Management is loaded');
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"importsNotUsedAsValues": "error"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
declare module '@capital/common';
|
||||||
|
declare module '@capital/component';
|
Loading…
Reference in New Issue