Skip to content

Commit

Permalink
Setup auth0
Browse files Browse the repository at this point in the history
  • Loading branch information
eirslett committed May 18, 2024
1 parent 027a1d6 commit f456cff
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 33 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist
node_modules
storybook-static
.env
62 changes: 43 additions & 19 deletions backend/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@ import path from 'path';
import { fileURLToPath } from 'url';
import { Router } from 'express';
import * as jose from 'jose';
import { OAuth2Client } from 'google-auth-library';
import { Issuer, generators } from 'openid-client';

import { isDevelopment } from './utils.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const CLIENT_ID = '273075937491-eb8o66k7dja2v1og8saorqhqnnc765ct.apps.googleusercontent.com';
const AUTH0_CLIENT_ID = 'gq575MGFWuiYcXpPTXNoJNYfAuoq19KD';

let jwks;

const secretsFile = '/secrets/unreed-production';
if (isDevelopment() && fs.existsSync(secretsFile)) {
if (fs.existsSync(secretsFile)) {
console.log(`Loading secrets from ${secretsFile}`);
const input = fs.readFileSync(secretsFile, 'utf-8');
const data = JSON.parse(input);
Expand All @@ -23,11 +24,25 @@ if (isDevelopment() && fs.existsSync(secretsFile)) {
process.env.DB_PASS = data.DB_PASS;
process.env.DB_NAME = data.DB_NAME;
process.env.GOOGLE_CLIENT_SECRET = data.GOOGLE_CLIENT_SECRET;
process.env.AUTH0_CLIENT_SECRET = data.AUTH0_CLIENT_SECRET;
jwks = data.JWKS;
} else {
jwks = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'keys.localhost.json')));
}

const auth0Issuer = await Issuer.discover(
'https://dev-fxrac16ih4t75q3f.us.auth0.com/.well-known/openid-configuration',
);

function getAuth0Client(req) {
return new auth0Issuer.Client({
client_id: AUTH0_CLIENT_ID,
client_secret: process.env.AUTH0_CLIENT_SECRET,
redirect_uris: [getRedirectUrl(req)],
response_types: ['code'],
});
}

const privateKeys = jose.createLocalJWKSet(jwks.public);
const privateKey = await jose.importJWK(jwks.private, 'ES256');

Expand All @@ -37,6 +52,7 @@ const audience = `urn:unreed-${isDevelopment() ? 'dev' : 'prod'}:audience`;
const maxAge = 2147483647;
const AUTH_COOKIE = 'UNREED_OIDC_TOKEN';
const REDIRECT_URI_COOKIE = 'UNREED_REDIRECT_URI';
const AUTH_NONCE_COOKIE = 'AUTH0_NONCE';

export const authRouter = new Router();

Expand All @@ -45,12 +61,16 @@ async function localhostLoginPage(req, res) {
}

async function productionLoginPage(req, res) {
const client = new OAuth2Client(CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, getRedirectUrl(req));
const authorizationUrl = client.generateAuthUrl({
scope: ['openid email'],
redirect_uri: getRedirectUrl(req),
const client = getAuth0Client(req);

const nonce = generators.nonce();

const authorizationUrl = client.authorizationUrl({
scope: 'openid email profile',
nonce,
});

res.cookie(AUTH_NONCE_COOKIE, nonce, { maxAge: 300000, path: '/', httpOnly: true });
res.redirect(authorizationUrl);
}

Expand Down Expand Up @@ -79,18 +99,17 @@ async function localhostCallbackPage(req, res) {
}

async function productionCallbackPage(req, res) {
const nonce = req.cookies[AUTH_NONCE_COOKIE];
try {
const code = req.query.code;
const client = new OAuth2Client(
CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
getRedirectUrl(req),
);
const googleToken = await client.getToken(code);
const googleClaims = jose.decodeJwt(googleToken.tokens.id_token);
const client = getAuth0Client(req);
const params = client.callbackParams(req);
const tokenSet = await client.callback(getRedirectUrl(req), params, {
nonce,
});
const auth0Claims = tokenSet.claims();

const claims = {
email: googleClaims.email,
email: auth0Claims.email,
};

const expirationTime = Date.now() + maxAge;
Expand All @@ -110,6 +129,7 @@ async function productionCallbackPage(req, res) {
secure: true,
});
res.clearCookie(REDIRECT_URI_COOKIE);
res.clearCookie(AUTH_NONCE_COOKIE);
res.redirect(req.cookies.UNREED_REDIRECT_URI ?? '/');
} catch (error) {
console.error(error);
Expand All @@ -119,9 +139,13 @@ async function productionCallbackPage(req, res) {
}
}

authRouter.get('/login', isDevelopment() ? localhostLoginPage : productionLoginPage);

authRouter.get('/login/callback', isDevelopment() ? localhostCallbackPage : productionCallbackPage);
if (isDevelopment()) {
authRouter.get('/login', localhostLoginPage);
authRouter.get('/login/callback', localhostCallbackPage);
} else {
authRouter.get('/login', productionLoginPage);
authRouter.get('/login/callback', productionCallbackPage);
}

export async function authMiddleware(req, res, next) {
const token = req.cookies[AUTH_COOKIE];
Expand Down
93 changes: 79 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
"clsx": "^2.1.1",
"cookie-parser": "^1.4.6",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"google-auth-library": "^9.9.0",
"jose": "^5.2.4",
"mysql": "^2.18.1",
"openid-client": "^5.6.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.22.3",
Expand Down
1 change: 1 addition & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import process from 'process';
import 'dotenv/config';
import { isDevelopment } from './backend/utils.js';
import { app } from './backend/app.js';

Expand Down

0 comments on commit f456cff

Please sign in to comment.