Skip to content

Commit

Permalink
Merge branch 'gql' into term-scrape
Browse files Browse the repository at this point in the history
  • Loading branch information
mathhulk committed Jun 25, 2024
2 parents 1d571a2 + 6b54422 commit d6ba4bb
Show file tree
Hide file tree
Showing 527 changed files with 14,079 additions and 27,728 deletions.
393 changes: 322 additions & 71 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 @@ -29,6 +29,7 @@
"@graphql-codegen/introspection": "4.0.3",
"@graphql-codegen/typescript": "^4.0.7",
"@graphql-codegen/typescript-resolvers": "^4.1.0",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.17",
"@types/express-session": "^1.18.0",
"@types/helmet": "0.0.48",
Expand All @@ -48,6 +49,7 @@
"@apollo/server-plugin-response-cache": "^4.1.3",
"@graphql-tools/schema": "^10.0.4",
"@graphql-tools/utils": "^10.2.2",
"compression": "^1.7.4",
"connect-redis": "^7.1.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
Expand Down
9 changes: 4 additions & 5 deletions backend/src/bootstrap/loaders/express.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +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
) => {
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 d6ba4bb

Please sign in to comment.