packages/api/src/services/passport.ts (53 lines of code) (raw):
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth2';
import type { VerifyCallback } from 'passport-google-oauth2';
import { ExtractJwt, Strategy as JwtStrategy } from 'passport-jwt';
import { TranscriptionConfig } from '@guardian/transcription-service-backend-common';
const validateEmail = (email: string) => {
// https://stackoverflow.com/questions/46155/whats-the-best-way-to-validate-an-email-address-in-javascript
return !!email.match(
// eslint-disable-next-line no-useless-escape
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
);
};
export const checkAuth = passport.authenticate('jwt', { session: false });
export const initPassportAuth = (config: TranscriptionConfig) => {
passport.use(
new GoogleStrategy(
{
clientID: config.auth.clientId,
clientSecret: config.auth.clientSecret,
callbackURL: `${config.app.rootUrl}/api/auth/oauth-callback`,
},
function (
accessToken: string,
refreshToken: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
profile: any,
done: VerifyCallback,
) {
// Need to cast to any since the type definitions for this library are broken. Great.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const profileAny = profile as any;
if (
profileAny._json.domain === 'guardian.co.uk' ||
validateEmail(profileAny._json.email)
) {
done(null, profile);
} else {
done(
new Error(
'Your Google account is not authorised to use transcription-service. Contact prod.eng.investigations@guardian.co.uk for help',
),
);
}
},
),
);
passport.use(
new JwtStrategy(
{
secretOrKey: config.app.secret,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
},
function (jwt, done) {
// We can optionally do more validation here
// For now will assume if the JWT token is valid then we're good.
done(null, jwt);
},
),
);
};