diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 55a36e0320..60a1add323 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -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; diff --git a/src/services/auth.ts b/src/services/auth.ts index 1cb6cacf40..5e3ebc402e 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -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'); } diff --git a/src/utils/get-email-from-profile.ts b/src/utils/get-email-from-profile.ts index b67eee5f9a..7d529c7172 100644 --- a/src/utils/get-email-from-profile.ts +++ b/src/utils/get-email-from-profile.ts @@ -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__PROFILE_EMAIL if we don't have it preconfigured yet + * Falls back to OAUTH__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) { 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; }