Skip to content

Commit

Permalink
Merge pull request #711 from asuc-octo/frontend-rewrite
Browse files Browse the repository at this point in the history
Merge frontend work into GQL branch
  • Loading branch information
mathhulk authored Jun 25, 2024
2 parents 7e730ef + 419ed5b commit 6b54422
Show file tree
Hide file tree
Showing 530 changed files with 18,982 additions and 29,773 deletions.
6,985 changes: 5,024 additions & 1,961 deletions backend/package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@graphql-codegen/introspection": "2.2.1",
"@graphql-codegen/typescript": "^2.7.4",
"@graphql-codegen/typescript-resolvers": "^2.7.4",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.12",
"@types/express-session": "^1.17.6",
"@types/helmet": "0.0.46",
Expand All @@ -54,6 +55,7 @@
"@graphql-tools/schema": "^10.0.0",
"@graphql-tools/utils": "^10.0.7",
"axios": "^1.5.1",
"compression": "^1.7.4",
"connect-redis": "^7.1.1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
Expand Down
2 changes: 1 addition & 1 deletion backend/src/bootstrap/loaders/apollo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin
import { ApolloServerPluginCacheControl } from '@apollo/server/plugin/cacheControl';
import { KeyValueCache, KeyValueCacheSetOptions } from '@apollo/utils.keyvaluecache';
import responseCachePlugin from '@apollo/server-plugin-response-cache';
import { RedisClientType, createClient } from 'redis';
import { RedisClientType } from 'redis';

class RedisCache implements KeyValueCache {
client: RedisClientType;
Expand Down
3 changes: 3 additions & 0 deletions backend/src/bootstrap/loaders/express.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import cors from "cors";
import helmet from "helmet";
import type { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import compression from "compression";
import { RedisClientType } from "redis";

import passportLoader from "./passport";
import { config } from "../../config";

export default async (app: Application, server: ApolloServer, redis: RedisClientType) => {
app.use(compression());

// Body parser only needed during POST on the graphQL path
app.use(json());

Expand Down
189 changes: 120 additions & 69 deletions backend/src/bootstrap/loaders/passport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,61 +20,109 @@ const LOGOUT_ROUTE = "/logout";

// route need to be added as authorized origins/redirect uris in google cloud console
const LOGIN_REDIRECT = config.backendPath + "/login/redirect";

const SUCCESS_REDIRECT = config.backendPath + config.graphqlPath;
const SUCCESS_REDIRECT = "/";
const FAILURE_REDIRECT = config.backendPath + "/fail";

const SCOPE = ['profile', 'email']
const SCOPE = ["profile", "email"];

const CACHE_PREFIX = 'user-session:'
const CACHE_PREFIX = "user-session:";

export default async (app: Application, redis: RedisClientType) => {
// init
app.use(session({
secret: config.SESSION_SECRET,
name: 'sessionId',
resave: false,
saveUninitialized: false,
cookie: {
secure: !config.isDev,
httpOnly: true,
maxAge: 1000 * 60 * 60, // 1 hour
sameSite: 'lax',
},
store: new RedisStore({
client: redis,
prefix: CACHE_PREFIX,
}),
rolling: true,
}));
app.use(
session({
secret: config.SESSION_SECRET,
name: "sessionId",
resave: false,
saveUninitialized: false,
cookie: {
secure: !config.isDev,
httpOnly: true,
maxAge: 1000 * 60 * 60, // 1 hour
sameSite: "lax",
},
store: new RedisStore({
client: redis,
prefix: CACHE_PREFIX,
}),
rolling: true,
})
);
app.use(passport.initialize());
app.use(passport.session());

// routes
app.get(LOGIN_ROUTE, (req, res, next) => {
// check if user is already logged in
if (req.isAuthenticated()) {
res.redirect(SUCCESS_REDIRECT);
} else {
next();
const authenticated = req.isAuthenticated();

const { redirect_uri: redirectURI } = req.query;

const parsedRedirectURI =
typeof redirectURI === "string" && redirectURI.startsWith("/")
? redirectURI
: null;

if (authenticated) {
res.redirect(parsedRedirectURI ?? SUCCESS_REDIRECT);

return;
}

const authenticator = passport.authenticate("google", {
scope: SCOPE,
accessType: "offline",
prompt: "consent",
state: parsedRedirectURI
? Buffer.from(
JSON.stringify({ redirectURI: parsedRedirectURI })
).toString("base64")
: undefined,
});

authenticator(req, res, next);
});
app.get(
LOGIN_REDIRECT_ROUTE,
passport.authenticate("google", {
failureRedirect: FAILURE_REDIRECT,
}),
(req, res) => {
const { state } = req.query;

let parsedRedirectURI;

try {
const { redirectURI } = JSON.parse(
Buffer.from(state as string, "base64").toString()
);

parsedRedirectURI =
typeof redirectURI === "string" && redirectURI.startsWith("/")
? redirectURI
: undefined;
} catch {
// Do nothing
}

res.redirect(parsedRedirectURI ?? SUCCESS_REDIRECT);
}
}, passport.authenticate('google', {
scope: SCOPE,
accessType: 'offline',
prompt: 'consent',
}));
app.get(LOGIN_REDIRECT_ROUTE, passport.authenticate('google', {
failureRedirect: FAILURE_REDIRECT,
// failureMessage: "failed",
successRedirect: SUCCESS_REDIRECT,
}));
app.post(LOGOUT_ROUTE, (req, res) => {
);
app.get(LOGOUT_ROUTE, (req, res) => {
req.logout((err) => {
if (err) {
res.redirect(FAILURE_REDIRECT);
} else {
res.redirect(SUCCESS_REDIRECT);

return;
}

const { redirect_uri: redirectURI } = req.query;

const parsedRedirectURI =
typeof redirectURI === "string" && redirectURI.startsWith("/")
? redirectURI
: null;

res.redirect(parsedRedirectURI ?? SUCCESS_REDIRECT);
});
});

Expand All @@ -85,36 +133,39 @@ export default async (app: Application, redis: RedisClientType) => {
passport.deserializeUser((user: any, done) => {
done(null, user);
});
passport.use(new GoogleStrategy.Strategy({
clientID: config.GOOGLE_CLIENT_ID,
clientSecret: config.GOOGLE_CLIENT_SECRET,
callbackURL: LOGIN_REDIRECT,
scope: SCOPE,
state: true,
}, async (accessToken, refreshToken, profile, done) => {
const email = profile.emails?.[0].value;

// null check for type safety
if (!email) {
return done(null, false, { message: 'No email found' });
}
passport.use(
new GoogleStrategy.Strategy(
{
clientID: config.GOOGLE_CLIENT_ID,
clientSecret: config.GOOGLE_CLIENT_SECRET,
callbackURL: LOGIN_REDIRECT,
},
async (accessToken, refreshToken, profile, done) => {
const email = profile.emails?.[0].value;

let user = await UserModel.findOne({ email });

if (!user) {
user = new UserModel({
email,
google_id: profile.id,
username: profile.displayName,
first_name: profile.name?.givenName || '',
last_name: profile.name?.familyName || '',
// refresh_token: refreshToken, <-------------- currently not needed.
});
}
// null check for type safety
if (!email) {
return done(null, false, { message: "No email found" });
}

user.last_login = new Date();
const doc = await user.save();
let user = await UserModel.findOne({ email });

done(null, doc);
}));
}
if (!user) {
user = new UserModel({
email,
google_id: profile.id,
username: profile.displayName,
first_name: profile.name?.givenName || "",
last_name: profile.name?.familyName || "",
// refresh_token: refreshToken, <-------------- currently not needed.
});
}

user.last_login = new Date();
const doc = await user.save();

done(null, doc);
}
)
);
};
Loading

0 comments on commit 6b54422

Please sign in to comment.