Add passportJS and add strategy and routing for a provider.

This commit is contained in:
Raghunandan
2020-05-18 21:31:08 +02:00
parent 8793912e7e
commit 6757352a79
6 changed files with 450 additions and 164 deletions
+8 -2
View File
@@ -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');
},
);
+138 -25
View File
@@ -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';