Wrap up oAuth flow

This commit is contained in:
rijkvanzanten
2020-06-23 17:37:55 -04:00
parent f8d64dab39
commit 8e871904fb
3 changed files with 39 additions and 19 deletions

View File

@@ -5,6 +5,7 @@ import Joi from '@hapi/joi';
import * as AuthService from '../services/auth';
import grant from 'grant';
import getGrantConfig from '../utils/get-grant-config';
import getEmailFromProfile from '../utils/get-email-from-profile';
const router = Router();
@@ -19,6 +20,12 @@ router.post(
await loginSchema.validateAsync(req.body);
const { email, password } = req.body;
/**
* @TODO
* Make sure to validate the payload. AuthService.authenticate's password is optional which
* means there's a possible problem when req.body.password is undefined
*/
const token = await AuthService.authenticate(email, password);
return res.status(200).json({
@@ -31,15 +38,17 @@ router.use('/sso', session({ secret: process.env.SECRET, saveUninitialized: true
router.use(grant.express()(getGrantConfig()));
router.get('/sso/:provider/callback', (req, res) => {
console.log(req.session.grant);
router.get(
'/sso/:provider/callback',
asyncHandler(async (req, res) => {
const email = getEmailFromProfile(req.params.provider, req.session.grant.response.profile);
/**
* @TODO
*
*/
const token = await AuthService.authenticate(email);
res.send(req.session.grant);
});
return res.status(200).json({
data: { token },
});
})
);
export default router;

View File

@@ -2,7 +2,7 @@ import database from '../database';
import APIError, { ErrorCode } from '../error';
import jwt from 'jsonwebtoken';
export const authenticate = async (email: string, password: string) => {
export const authenticate = async (email: string, password?: string) => {
const user = await database
.select('id', 'password', 'role')
.from('directus_users')
@@ -13,8 +13,15 @@ export const authenticate = async (email: string, password: string) => {
throw new APIError(ErrorCode.INVALID_USER_CREDENTIALS, 'Invalid user credentials');
}
/** @TODO implement password hash */
if (password !== user.password) {
/**
* @NOTE
* This undefined check is on purpose so we can login through SSO without having to rely on
* password. However, this check might be a little tricky, as we don't want this login with just
* email to leak anywhere else.. We might have to make a dedicated "copy" of this function to
* signal the difference
*/
if (password !== undefined && password !== user.password) {
/** @TODO implement password hash checking */
throw new APIError(ErrorCode.INVALID_USER_CREDENTIALS, 'Invalid user credentials');
}

View File

@@ -1,24 +1,28 @@
import { get } from 'lodash';
// The path in JSON to fetch the email address from the profile.
const profileMap = {
github: 'email',
};
// Note: a lot of services use `email` as the path. We fall back to that as default, so no need to
// map it here
const profileMap = {};
/**
* Extract the email address from a given user profile coming from a providers API
*
* Falls back to OAUTH_<PROVIDER>_PROFILE_EMAIL if we don't have it preconfigured yet
* Falls back to OAUTH_<PROVIDER>_PROFILE_EMAIL if we don't have it preconfigured yet, and defaults
* to `email` if nothing is set
*
* This is used in the SSO flow to extract the users
*/
export default function getEmailFromProfile(provider: string, profile: Record<string, any>) {
const path =
profileMap[provider] || process.env[`OAUTH_${provider.toUpperCase()}_PROFILE_EMAIL`];
profileMap[provider] ||
process.env[`OAUTH_${provider.toUpperCase()}_PROFILE_EMAIL`] ||
'email';
const email = get(profile, path);
if (!path) {
throw new Error('Path to email in profile object is unknown.');
if (!email) {
throw new Error("Couldn't extract email address from SSO provider response");
}
return get(profile, path);
return email;
}