Add passportJS and add strategy and routing for a provider.
This commit is contained in:
@@ -20,16 +20,22 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"express-session": "1.17.1",
|
||||
"fs-extra": "^9.0.0",
|
||||
"helmet": "^3.22.0",
|
||||
"morgan": "^1.10.0",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
"yn": "^4.0.0",
|
||||
"passport": "0.4.1",
|
||||
"passport-google-oauth20": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.5",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"tsc-watch": "^4.2.3"
|
||||
"tsc-watch": "^4.2.3",
|
||||
"@types/passport": "1.0.3",
|
||||
"@types/express-session": "1.17.0",
|
||||
"@types/passport-google-oauth20": "2.0.3"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export const providers = [
|
||||
{
|
||||
provider: 'google',
|
||||
options: {
|
||||
clientID: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
callbackURL: 'http://localhost:7000/auth/google/handler/frame',
|
||||
passReqToCallback: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// provider: 'github',
|
||||
// options: {
|
||||
// clientID: process.env.GITHUB_CLIENT_ID!,
|
||||
// clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
||||
// callbackURL: 'http://localhost:7000/auth/github/handler/frame',
|
||||
// passReqToCallback: true,
|
||||
// },
|
||||
// },
|
||||
];
|
||||
@@ -1,13 +1,25 @@
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import passport from 'passport';
|
||||
|
||||
export const router = Router();
|
||||
router.get('/', async (_req: express.Request, res: express.Response) => {
|
||||
res.send('initiate auth');
|
||||
router.get('/start', (req, res, next) => {
|
||||
passport.authenticate('google', {
|
||||
scope: ['profile', 'email'],
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
state: '8745634875963',
|
||||
})(req, res, next);
|
||||
});
|
||||
|
||||
router.get('/login', async (_req: express.Request, res: express.Response) => {
|
||||
res.send('google provider login');
|
||||
router.get('/handler/frame', passport.authenticate('google'), function (
|
||||
req,
|
||||
res,
|
||||
) {
|
||||
console.log('DEBUG: req.session.passport.user', req.session?.passport.user);
|
||||
console.log('DEBUG: req.params =', req.params);
|
||||
console.log('DEBUG: req.url =', req.url);
|
||||
res.send('yay!');
|
||||
});
|
||||
|
||||
router.get('/logout', async (_req: express.Request, res: express.Response) => {
|
||||
@@ -15,8 +27,8 @@ router.get('/logout', async (_req: express.Request, res: express.Response) => {
|
||||
});
|
||||
|
||||
router.get(
|
||||
'/handler/frame',
|
||||
async (req: express.Request, res: express.Response) => {
|
||||
res.send('google provider frame response handler');
|
||||
'/refreshToken',
|
||||
async (_req: express.Request, res: express.Response) => {
|
||||
res.send('google provider refreshToken');
|
||||
},
|
||||
);
|
||||
|
||||
@@ -16,44 +16,157 @@
|
||||
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import passport from 'passport';
|
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||
import { Logger } from 'winston';
|
||||
import { router as googleAuthRouter } from './../providers/google/router';
|
||||
import { router as githubAuthRouter } from './../providers/google/router';
|
||||
import { providers } from './../providers/config';
|
||||
|
||||
export interface RouterOptions {
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
type AuthProviderHandlers = {
|
||||
start(
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
): Promise<any>;
|
||||
handle(
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
): express.Response<any>;
|
||||
refresh?(
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
): Promise<any>;
|
||||
logout(
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
): express.Response<any>;
|
||||
};
|
||||
|
||||
type AuthProvider = {
|
||||
[key: string]: {
|
||||
makeStrategy(options: any): passport.Strategy;
|
||||
makeRouter(handlers: AuthProviderHandlers): express.Router;
|
||||
};
|
||||
};
|
||||
|
||||
type AuthInfo = {
|
||||
profile: passport.Profile;
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
expiresAt?: number;
|
||||
};
|
||||
|
||||
type AuthResponse = {
|
||||
type: string;
|
||||
payload: AuthInfo;
|
||||
};
|
||||
|
||||
const postMessageResponse = (res: express.Response, data: AuthResponse) => {
|
||||
const jsonData = JSON.stringify(data);
|
||||
const base64Data = Buffer.from(jsonData, 'utf8').toString('base64');
|
||||
|
||||
res.setHeader('X-Frame-Options', 'sameorigin');
|
||||
res.end(`
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
(window.opener || window.parent).postMessage(JSON.parse(atob('${base64Data}')), location.origin)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
};
|
||||
|
||||
const GoogleAuthProviderHandler: AuthProviderHandlers = {
|
||||
start(req, res, next) {
|
||||
return passport.authenticate('google', {
|
||||
scope: ['profile', 'email'],
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
state: '8745634875963',
|
||||
})(req, res, next);
|
||||
},
|
||||
handle(req, res, next) {
|
||||
return passport.authenticate('google', function (_, user) {
|
||||
postMessageResponse(res, {
|
||||
type: 'oauth-result',
|
||||
payload: user,
|
||||
});
|
||||
})(req, res, next);
|
||||
},
|
||||
logout(_req, res, _next) {
|
||||
return res.send('logout!');
|
||||
},
|
||||
};
|
||||
|
||||
const providerFactories: AuthProvider = {
|
||||
google: {
|
||||
makeStrategy(options: any): passport.Strategy {
|
||||
return new GoogleStrategy(options, function (
|
||||
_req: any,
|
||||
accessToken: any,
|
||||
refreshToken: any,
|
||||
profile: any,
|
||||
cb: any,
|
||||
) {
|
||||
cb(undefined, { profile, accessToken, refreshToken });
|
||||
});
|
||||
},
|
||||
makeRouter(handlers: AuthProviderHandlers): express.Router {
|
||||
const router = Router();
|
||||
router.get('/start', handlers.start);
|
||||
router.get('/handler/frame', handlers.handle);
|
||||
router.get('/logout', handlers.logout);
|
||||
if (handlers.refresh) {
|
||||
router.get('/refreshToken', handlers.refresh);
|
||||
}
|
||||
return router;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function makeProvider(config: any) {
|
||||
const provider = config.provider;
|
||||
const providerFactory = providerFactories[provider];
|
||||
const strategy: passport.Strategy = providerFactory.makeStrategy(
|
||||
config.options,
|
||||
);
|
||||
const providerRouter = providerFactory.makeRouter(GoogleAuthProviderHandler);
|
||||
return { provider, strategy, providerRouter };
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const logger = options.logger.child({ plugin: 'auth' });
|
||||
const router = Router();
|
||||
|
||||
for (const providerConfig of providers) {
|
||||
const { provider, strategy, providerRouter } = makeProvider(providerConfig);
|
||||
passport.use(strategy);
|
||||
|
||||
router.use(`/${provider}`, providerRouter);
|
||||
}
|
||||
|
||||
passport.serializeUser(function (user, done) {
|
||||
done(null, user);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function (user, done) {
|
||||
done(null, user);
|
||||
});
|
||||
|
||||
router.use(passport.initialize());
|
||||
router.use(passport.session());
|
||||
|
||||
router.get('/ping', async (_req, res) => {
|
||||
res.status(200).send('pong');
|
||||
});
|
||||
|
||||
router.use('/:provider', authProviderSwitcher);
|
||||
|
||||
const app = express();
|
||||
app.set('logger', logger);
|
||||
app.use(router);
|
||||
|
||||
return app;
|
||||
return router;
|
||||
}
|
||||
|
||||
const authProviderSwitcher = async (
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction,
|
||||
) => {
|
||||
const provider = req.params.provider;
|
||||
if (provider === 'google') {
|
||||
return googleAuthRouter(req, res, next);
|
||||
} else if (provider === 'github') {
|
||||
return githubAuthRouter(req, res, next);
|
||||
} else {
|
||||
res.send('No such provider');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@ import compression from 'compression';
|
||||
import cors from 'cors';
|
||||
import express from 'express';
|
||||
import helmet from 'helmet';
|
||||
import passport from 'passport';
|
||||
import session from 'express-session';
|
||||
import { Logger } from 'winston';
|
||||
import { createRouter } from './router';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user