Skip to content

Commit

Permalink
chore: run formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
VeeIsForVanana committed Jul 13, 2024
1 parent e1a84d3 commit ebefd13
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 70 deletions.
24 changes: 10 additions & 14 deletions src/lib/server/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const TaggedStudentsWithLabs = array(
const UserEmails = array(pick(User, ['email']));

export type AvailableLabs = InferOutput<typeof AvailableLabs>;
export type DesignatedSender = InferOutput<typeof DesignatedSender>
export type DesignatedSender = InferOutput<typeof DesignatedSender>;
export type QueriedFaculty = InferOutput<typeof QueriedFaculty>;
export type TaggedStudentsWithLabs = InferOutput<typeof TaggedStudentsWithLabs>;

Expand Down Expand Up @@ -377,8 +377,7 @@ export class Database implements Loggable {

@timed async getDesignatedSender() {
const sql = this.#sql;
const [first, ...rest] =
await sql`SELECT * FROM drap.designated_sender`
const [first, ...rest] = await sql`SELECT * FROM drap.designated_sender`;
strictEqual(rest.length, 0);

if (typeof first === 'undefined') return;
Expand All @@ -392,30 +391,27 @@ export class Database implements Loggable {

@timed async deleteDesignatedSender() {
const sql = this.#sql;
const count = await sql`DELETE FROM drap.designated_sender`
const count = await sql`DELETE FROM drap.designated_sender`;
return count;
}

@timed async initDesignatedSender(
email: DesignatedSender['email'],
) {
@timed async initDesignatedSender(email: DesignatedSender['email']) {
const sql = this.#sql;
const count = await sql`INSERT INTO drap.designated_sender (email) VALUES (${email})`
const count = await sql`INSERT INTO drap.designated_sender (email) VALUES (${email})`;
return count;
}

@timed async updateDesignatedSender(
email: DesignatedSender['email'],
expires_at: DesignatedSender['expires_at'],
access_token: DesignatedSender['access_token'],
refresh_token: DesignatedSender['refresh_token'] = null
refresh_token: DesignatedSender['refresh_token'] = null,
) {
const sql = this.#sql;
const [first, ...rest] =
refresh_token ?
await sql`UPDATE drap.designated_sender SET expires_at = ${expires_at}, access_token = ${access_token}, refresh_token = ${refresh_token} WHERE email = ${email} RETURNING *`
: await sql`UPDATE drap.designated_sender SET expires_at = ${expires_at}, access_token = ${access_token} WHERE email = ${email} RETURNING *`
notEqual(typeof first, 'undefined')
const [first, ...rest] = refresh_token
? await sql`UPDATE drap.designated_sender SET expires_at = ${expires_at}, access_token = ${access_token}, refresh_token = ${refresh_token} WHERE email = ${email} RETURNING *`
: await sql`UPDATE drap.designated_sender SET expires_at = ${expires_at}, access_token = ${access_token} WHERE email = ${email} RETURNING *`;
notEqual(typeof first, 'undefined');
strictEqual(rest.length, 0);
return parse(DesignatedSender, first);
}
Expand Down
85 changes: 41 additions & 44 deletions src/lib/server/email/email.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,74 @@
import { IdToken, TokenResponse } from "$lib/server/models/oauth";
import { createRemoteJWKSet, jwtVerify } from "jose";
import { parse, pick } from "valibot";
import { IdToken, TokenResponse } from '$lib/server/models/oauth';
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { parse, pick } from 'valibot';

import type { Database } from "$lib/server/database";
import GOOGLE from "$lib/server/env/google"
import { createTransport } from "nodemailer";
import type { Database } from '$lib/server/database';
import GOOGLE from '$lib/server/env/google';
import { createTransport } from 'nodemailer';

// this function refreshes the access token and updates the db accordingly
async function refreshAccessToken(refresh_token: string, email: string, db: Database) {
const fetchJwks = createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs'));

const body = new URLSearchParams({
refresh_token: refresh_token,
client_id: GOOGLE.OAUTH_CLIENT_ID,
client_secret: GOOGLE.OAUTH_CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: refresh_token,
client_id: GOOGLE.OAUTH_CLIENT_ID,
client_secret: GOOGLE.OAUTH_CLIENT_SECRET,
grant_type: 'refresh_token',
});

const res = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
});

const json = await res.json();

const { id_token, access_token } = parse(TokenResponse, json);

const { payload } = await jwtVerify(id_token, fetchJwks, {
issuer: 'https://accounts.google.com',
audience: GOOGLE.OAUTH_CLIENT_ID,
issuer: 'https://accounts.google.com',
audience: GOOGLE.OAUTH_CLIENT_ID,
});

const token = parse(pick(IdToken, ['exp']), payload);

db.updateDesignatedSender(
email,
token.exp,
access_token
)
db.updateDesignatedSender(email, token.exp, access_token);

return db.getDesignatedSender()
return db.getDesignatedSender();
}

// this function sends an email to the provided email address with the given body via nodemailer using the access token of the designated admin sender
export async function sendEmailTo(to: string, subject: string, body: string, db: Database) {
let credentials = await db.getDesignatedSender()
let credentials = await db.getDesignatedSender();

if (!credentials) throw Error();
if (!credentials.refresh_token) throw Error();

if (credentials?.expires_at < new Date()) credentials = await refreshAccessToken(credentials.refresh_token, credentials.email, db)


if (credentials?.expires_at < new Date())
credentials = await refreshAccessToken(credentials.refresh_token, credentials.email, db);

if (!credentials) throw Error();

const transporter = createTransport({
host: "smtp.gmail.com",
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: credentials.email,
clientId: GOOGLE.OAUTH_CLIENT_ID,
clientSecret: GOOGLE.OAUTH_CLIENT_SECRET,
refreshToken: credentials.refresh_token,
accessToken: credentials.access_token,
type: 'OAuth2',
user: credentials.email,
clientId: GOOGLE.OAUTH_CLIENT_ID,
clientSecret: GOOGLE.OAUTH_CLIENT_SECRET,
refreshToken: credentials.refresh_token,
accessToken: credentials.access_token,
},
});

transporter.sendMail({
from: credentials.email,
to,
subject,
text: body
})
}
from: credentials.email,
to,
subject,
text: body,
});
}
2 changes: 1 addition & 1 deletion src/lib/server/models/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const TokenResponse = object({
// Remaining lifetime in seconds.
expires_in: pipe(number(), safeInteger()),
// Refresh token, will not always be given with every TokenResponse (requires prompt=consent&access_type=offline)
refresh_token: optional(string())
refresh_token: optional(string()),
});

const UnixTimeSecs = pipe(
Expand Down
11 changes: 3 additions & 8 deletions src/routes/oauth/callback/+server.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,9 @@ export async function GET({ fetch, locals: { db }, cookies, url: { searchParams

// Check if this session is for handling a new designated_sender, first delete sole designated_sender then complete the designated_sender
if (pending.is_new_sender) {
await db.deleteDesignatedSender()
await db.initDesignatedSender(token.email)
await db.updateDesignatedSender(
token.email,
token.exp,
access_token,
refresh_token
)
await db.deleteDesignatedSender();
await db.initDesignatedSender(token.email);
await db.updateDesignatedSender(token.email, token.exp, access_token, refresh_token);
}

// Insert user as uninitialized by default
Expand Down
8 changes: 5 additions & 3 deletions src/routes/oauth/login/+server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export async function GET({ locals: { db }, cookies, url: { searchParams } }) {
if (user !== null) redirect(302, '/');
}

const { session_id, nonce, expiration } = await db.generatePendingSession();
const isNewSender = Boolean(searchParams.get('new_sender'));

const { session_id, nonce, expiration } = await db.generatePendingSession(isNewSender);
cookies.set('sid', session_id, { path: '/', httpOnly: true, sameSite: 'lax', expires: expiration });

const hashedSessionId = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(session_id));
Expand All @@ -22,8 +24,8 @@ export async function GET({ locals: { db }, cookies, url: { searchParams } }) {
hd: 'up.edu.ph',
access_type: isNewSender ? 'offline' : 'online',
response_type: 'code',
scope: OAUTH_SCOPE_STRING.concat(isNewSender ? ' https://mail.google.com/' : '' ),
prompt: isNewSender ? 'consent' : ''
scope: OAUTH_SCOPE_STRING.concat(isNewSender ? ' https://mail.google.com/' : ''),
prompt: isNewSender ? 'consent' : '',
});

redirect(302, `https://accounts.google.com/o/oauth2/v2/auth?${params}`);
Expand Down

0 comments on commit ebefd13

Please sign in to comment.