Skip to content

Commit

Permalink
Refactor auth routes into separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
humphd committed Apr 16, 2021
1 parent 42224d8 commit 2eb90e0
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 140 deletions.
4 changes: 1 addition & 3 deletions src/api/auth/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ const RedisStore = require('connect-redis')(session);
// Setup SAML SSO-based Authentication
require('./authentication');

const routes = require('./routes');

const service = new Satellite({
router: routes,
router: require('./routes'),
beforeRouter(app) {
// Initialize and use Session and Passport middleware on the app. In production
// we use Redis for session storage, and in-memory otherwise.
Expand Down
137 changes: 0 additions & 137 deletions src/api/auth/src/routes.js

This file was deleted.

13 changes: 13 additions & 0 deletions src/api/auth/src/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { Router } = require('@senecacdot/satellite');
const { errors } = require('celebrate');

const router = Router();

router.use('/login', require('./login'));
router.use('/logout', require('./logout'));
router.use('/sp', require('./saml-metadata'));

// Let Celebrate handle validation errors
router.use(errors());

module.exports = router;
64 changes: 64 additions & 0 deletions src/api/auth/src/routes/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { Router, logger, createError } = require('@senecacdot/satellite');
const passport = require('passport');

const { createToken } = require('../token');
const {
validateRedirectAndStateParams,
validateRedirectUriOrigin,
captureAuthDetailsOnSession,
} = require('../middleware');

const router = Router();

/**
* /login allows users to authenticate with the external SAML SSO provider.
* The caller needs to provide a request_uri query param to indicate where
* we should redirect
*/
router.get(
'/',
validateRedirectAndStateParams(),
validateRedirectUriOrigin(),
captureAuthDetailsOnSession(),
passport.authenticate('saml')
);

/**
* /login/callback is where the external SAML SSO provider will redirect
* users upon successful login. We redirect the user back to wherever
* they were trying to go, along with their original state unmodified.
*/
router.post('/callback', passport.authenticate('saml'), (req, res, next) => {
// We should have a login object in the session. If not, something's wrong.
if (!req.session.authDetails) {
logger.warn('/login/callback route hit without valid login session details');
next(createError(400, `unexpected access of /login/callback`));
return;
}

const { redirectUri, state } = req.session.authDetails;
delete req.session.authDetails;
logger.debug({ redirectUri, state }, 'processing /login/callback');

// Create a token for this user, setting their authorization roles
const { user } = req;
const token = createToken(
user.email,
user.firstName,
user.lastName,
user.displayName,
user.roles,
user.avatarUrl
);

let url = `${redirectUri}?access_token=${token}`;
// Add the state we received before, if it was given at all
if (state) {
url += `&state=${state}`;
}

// TODO: send back expires info too?
res.redirect(url);
});

module.exports = router;
70 changes: 70 additions & 0 deletions src/api/auth/src/routes/logout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const { Router, logger, createError } = require('@senecacdot/satellite');
const passport = require('passport');

const {
validateRedirectAndStateParams,
validateRedirectUriOrigin,
captureAuthDetailsOnSession,
} = require('../middleware');

const router = Router();

/**
* /logout/callback is where the external SAML SSO provider will redirect
* users upon successful logout. We support both GET and POST, since different
* IdPs do it different ways.
*/
function logout() {
return (req, res, next) => {
// We should have a logout object in the session. If not, something's wrong.
if (!req.session.authDetails) {
logger.warn('/logout/callback route hit without valid logout session details');
next(createError(400, `unexpected access of /logout/callback`));
return;
}

const { redirectUri, state } = req.session.authDetails;
delete req.session.authDetails;
logger.debug({ redirectUri, state }, 'processing /logout/callback');

// Clear session passport.js user info
req.logout();

// Add the state on the redirect if present
const url = state ? `${redirectUri}?state=${state}` : redirectUri;
res.redirect(url);
};
}

router.get('/callback', logout());
router.post('/callback', logout());

/**
* /logout allows users to use Single Logout and clear login tokens
* from their passport.js session.
*/
router.get(
'/',
validateRedirectAndStateParams(),
validateRedirectUriOrigin(),
captureAuthDetailsOnSession(),
// passport.authenticate('saml'),
(req, res, next) => {
try {
// eslint-disable-next-line no-underscore-dangle
passport._strategy('saml').logout(req, (error, requestUrl) => {
if (error) {
logger.error({ error }, 'logout error - unable to generate logout URL');
next(createError(500, `unable to logout`));
} else {
res.redirect(requestUrl);
}
});
} catch (error) {
logger.error({ error }, 'logout error');
next(createError(500, `unable to logout`));
}
}
);

module.exports = router;
16 changes: 16 additions & 0 deletions src/api/auth/src/routes/saml-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { Router } = require('@senecacdot/satellite');

const { samlMetadata } = require('../authentication');

const router = Router();

/**
* Provide SAML Metadata endpoint for our Service Provider's Entity ID.
* The naming is {host}/sp, for example: http://localhost/v1/auth/sp
*/
router.get('/', (req, res) => {
res.type('application/xml');
res.status(200).send(samlMetadata());
});

module.exports = router;

0 comments on commit 2eb90e0

Please sign in to comment.