Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: add base pages and auth #14

Merged
merged 11 commits into from
May 8, 2024
53 changes: 53 additions & 0 deletions pnpm-lock.yaml

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

8 changes: 8 additions & 0 deletions ui/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export default defineNuxtConfig({
"nuxt-icon",
],

nitro: {
esbuild: {
options: {
target: "esnext",
},
},
},

colorMode: {
preference: "light", // default value of $colorMode.preference
fallback: "light", // fallback value if not system preference found
Expand Down
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"lucia": "^3.2.0",
"md-editor-v3": "^4.13.5",
"mongodb": "^6.5.0",
"mongoose": "^8.3.3",
"notivue": "^2.4.4",
"nuxt": "3.11.1",
"oslo": "^1.2.0",
Expand Down
131 changes: 72 additions & 59 deletions ui/server/routes/login/github/callback.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,89 @@ import { OAuth2RequestError } from "arctic";
import { DatabaseUser, generateIdFromEntropySize } from "lucia";
import clientPromise from "~/server/utils/mongodb";
import { github } from "~/server/utils/auth";
import mongoose from "mongoose";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Test coverage needed for mongoose usage in GitHub callback.

Ensure there are tests that cover the integration of mongoose in the GitHub callback logic, particularly how it interacts with the database operations.


export default defineEventHandler(async (event) => {
const client = await clientPromise;
await client.connect();
const client = await clientPromise;
await client.connect();

const db = client.db();
const query = getQuery(event);
const code = query.code?.toString() ?? null;
const state = query.state?.toString() ?? null;
const storedState = getCookie(event, "github_oauth_state") ?? null;
if (!code || !state || !storedState || state !== storedState) {
throw createError({
status: 400
});
}
const db = client.db();
const query = getQuery(event);
const code = query.code?.toString() ?? null;
const state = query.state?.toString() ?? null;
const storedState = getCookie(event, "github_oauth_state") ?? null;
if (!code || !state || !storedState || state !== storedState) {
throw createError({
status: 400,
});
}

try {
const tokens = await github.validateAuthorizationCode(code);
try {
const tokens = await github.validateAuthorizationCode(code);

const githubUserResponse = await fetch("https://api.github.com/user", {
headers: {
Authorization: `Bearer ${tokens.accessToken}`
}
});
const githubUser: GitHubUser = await githubUserResponse.json();
const githubUserResponse = await fetch("https://api.github.com/user", {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
});
const githubUser: GitHubUser = await githubUserResponse.json();

// Replace this with your own DB client.
const existingUser = await db.collection("user").findOne({ github_id: githubUser.id });
// Replace this with your own DB client.
const existingUser = await db
.collection("users")
.findOne({ github_id: githubUser.id });

// console.log("EXISTING USER: " + JSON.stringify(existingUser))
// const { _id } = existingUser;
// console.log("EXISTING USER ID: " + _id)
if (existingUser) {
const session = await lucia.createSession(existingUser._id, {});
// Update the session in the DB
// Replace this with your own DB client.
// await db.collection("session").updateOne({ _id: session.id }, { $set: { _id: _id } });
// console.log("SESSION: " + JSON.stringify(session));
appendHeader(event, "Set-Cookie", lucia.createSessionCookie(session.id).serialize());
return sendRedirect(event, "/profile");
}
// console.log("EXISTING USER: " + JSON.stringify(existingUser))
// console.log("EXISTING USER ID: " + _id)
if (existingUser) {
const { _id } = existingUser;
const session = await lucia.createSession(existingUser._id, {});
// Update the session in the DB
// Replace this with your own DB client.
await db
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code_refinement): Consider the implications of direct database manipulations within route handlers.

Directly manipulating the database within route handlers can lead to tightly coupled code that is harder to maintain. Consider abstracting database interactions to a separate layer or service.

Suggested change
await db
await updateSessionInDB(session.id, _id);
async function updateSessionInDB(sessionId, userId) {
return db.collection("session").updateOne({ _id: sessionId }, { $set: { _id: userId } });
}

.collection("session")
.updateOne({ _id: session.id }, { $set: { _id: _id } });
// console.log("SESSION: " + JSON.stringify(session));
appendHeader(
event,
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return sendRedirect(event, "/profile");
}

const userId = generateIdFromEntropySize(10); // 16 characters long
const userId = generateIdFromEntropySize(10); // 16 characters long

// Replace this with your own DB client.
await db.collection("user").insertOne({
_id: userId,
github_id: githubUser.id,
username: githubUser.login
});
// Replace this with your own DB client.
await db.collection("users").insertOne({
_id: userId,
github_id: githubUser.id,
username: githubUser.login,
});

const session = await lucia.createSession(userId, {});
appendHeader(event, "Set-Cookie", lucia.createSessionCookie(session.id).serialize());
return sendRedirect(event, "/profile");
} catch (e) {
// the specific error message depends on the provider
if (e instanceof OAuth2RequestError) {
// invalid code
throw createError({
status: 400
});
}
throw createError({
status: 500,
});
}
const session = await lucia.createSession(userId, {});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): No tests for session creation failure scenarios.

It's important to test how the system behaves if session creation fails. This could involve mocking the session creation to throw an error and asserting the error handling in the application.

Suggested change
const session = await lucia.createSession(userId, {});
try {
const session = await lucia.createSession(userId, {});
} catch (error) {
console.error("Failed to create session:", error);
throw new Error("Session creation failed");
}

appendHeader(
event,
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return sendRedirect(event, "/profile");
} catch (e) {
// the specific error message depends on the provider
if (e instanceof OAuth2RequestError) {
// invalid code
throw createError({
status: 400,
});
}
throw createError({
status: 500,
message: e.message,
});
}
});

interface GitHubUser {
id: string;
login: string;
id: string;
login: string;
}

Loading