Skip to content

Commit

Permalink
feat: integrate gitlab and refactor frontend (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijna-Raghavendra authored Feb 29, 2024
2 parents 205d338 + 04b5d1c commit f8bdb0c
Show file tree
Hide file tree
Showing 28 changed files with 496 additions and 129 deletions.
5 changes: 4 additions & 1 deletion src/backend/.env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
GITHUB_OAUTH_CLIENT_ID=...
GITHUB_OAUTH_CLIENT_SECRET=...
GITLAB_OAUTH_CLIENT_ID=...
GITLAB_OAUTH_CLIENT_SECRET=...
MONGO_API_KEY=...
MONGO_APP_ID=...
SENTRY_DSN=...
SENTRY_DSN=...
FRONTEND=...
81 changes: 66 additions & 15 deletions src/backend/auth/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,74 @@ import { checkUser } from "../db.ts";
import { checkJWT, createJWT } from "../utils/jwt.ts";

async function githubAuth(ctx: Context, id: string, secret: string) {
await authenticateAndCreateJWT(ctx, id, secret, "github");
}

async function gitlabAuth(
ctx: Context,
id: string,
secret: string,
frontend: string,
) {
await authenticateAndCreateJWT(ctx, id, secret, "gitlab", frontend);
}

async function authenticateAndCreateJWT(
ctx: Context,
id: string,
secret: string,
provider: string,
frontend = "",
) {
if (!ctx.request.hasBody) {
ctx.throw(415);
}
const code = await ctx.request.body().value;
console.log(code);
const rootUrl = new URL("https://github.com/login/oauth/access_token");
const oauthUrl = provider === "github"
? "https://github.com/login/oauth/access_token"
: provider === "gitlab"
? "https://gitlab.com/oauth/token"
: null;

if (oauthUrl === null) {
ctx.response.body = "Unsupported provider";
return;
}

if (code !== null) {
rootUrl.search = new URLSearchParams({
client_id: id,
client_secret: secret,
code,
}).toString();
const rootUrl = new URL(oauthUrl);
rootUrl.search = provider === "github"
? new URLSearchParams({
client_id: id,
client_secret: secret,
code,
}).toString()
: provider === "gitlab"
? new URLSearchParams({
client_id: id,
client_secret: secret,
code,
grant_type: "authorization_code",
redirect_uri: `${frontend}/login`,
}).toString()
: "";

const resp = await fetch(rootUrl.toString(), {
method: "POST",
headers: {
"Accept": "application/json",
Accept: "application/json",
},
});

const body = await resp.json();
const { status, githubId } = await checkUser(body.access_token);

const { status, userId } = await checkUser(body.access_token, provider);

ctx.response.headers.set("Access-Control-Allow-Origin", "*");

if (status.matchedCount == 1) {
const id_jwt = await createJWT(githubId);
Sentry.captureMessage("User " + githubId + " logged in", "info");
const id_jwt = await createJWT(provider, userId);
Sentry.captureMessage("User " + userId + " logged in", "info");
ctx.response.body = id_jwt;
} else {
ctx.response.body = "not authorized";
Expand All @@ -37,13 +80,21 @@ async function githubAuth(ctx: Context, id: string, secret: string) {
}
}

async function githubId(ctx: Context) {
async function handleJwtAuthentication(ctx: Context) {
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
if (!ctx.request.hasBody) {
ctx.throw(415);
}
const jwt_token = await ctx.request.body().value;
ctx.response.body = await checkJWT(jwt_token);
const body = await ctx.request.body().value;
let document;
try {
document = JSON.parse(body);
} catch (e) {
document = body;
}
const jwt_token = document.jwt_token;
const provider = document.provider;
ctx.response.body = await checkJWT(provider, jwt_token);
}

export { githubAuth, githubId };
export { githubAuth, gitlabAuth, handleJwtAuthentication };
12 changes: 6 additions & 6 deletions src/backend/db.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import getGithubUser from "./utils/github-user.ts";
import getProviderUser from "./utils/get-user.ts";
import DfContentMap from "./types/maps_interface.ts";

const DATA_API_KEY = Deno.env.get("MONGO_API_KEY")!;
Expand Down Expand Up @@ -27,17 +27,17 @@ const MONGO_URLs = {
};

// Function to update access token on db if user exists
async function checkUser(accessToken: string) {
const githubId = await getGithubUser(accessToken);
async function checkUser(accessToken: string, provider: string) {
const userId = await getProviderUser(accessToken, provider);

const query = {
collection: "user_auth",
database: DATABASE,
dataSource: DATA_SOURCE,
filter: { "githubId": githubId },
filter: { [`${provider}Id`]: userId },
update: {
$set: {
"githubId": githubId,
[`${provider}Id`]: userId,
"authToken": accessToken,
},
},
Expand All @@ -47,7 +47,7 @@ async function checkUser(accessToken: string) {

const status_resp = await fetch(MONGO_URLs.update.toString(), options);
const status = await status_resp.json();
return { status, githubId };
return { status, userId };
}

// Get all content maps corresponding to user
Expand Down
2 changes: 2 additions & 0 deletions src/backend/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { Session } from "https://deno.land/x/[email protected]/mod.ts";
import { create, verify } from "https://deno.land/x/[email protected]/mod.ts";
import { exec } from "https://deno.land/x/[email protected]/mod.ts";
import * as Sentry from "npm:@sentry/node";
import { oakCors } from "https://deno.land/x/[email protected]/mod.ts";

export {
Application,
Context,
create,
exec,
isHttpError,
oakCors,
Router,
Sentry,
Session,
Expand Down
11 changes: 8 additions & 3 deletions src/backend/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { addMaps, deleteMaps, getMaps } from "./db.ts";
async function getSubdomains(ctx: Context) {
const author = ctx.request.url.searchParams.get("user");
const token = ctx.request.url.searchParams.get("token");
if (author != await checkJWT(token!)) {
const provider = ctx.request.url.searchParams.get("provider");
if (author != await checkJWT(provider!, token!)) {
ctx.throw(401);
}
const data = await getMaps(author);
Expand All @@ -27,13 +28,15 @@ async function addSubdomain(ctx: Context) {
}
const copy = { ...document };
const token = document.token;
const provider = document.provider;
delete document.token;
delete document.provider;
delete document.port;
delete document.build_cmds;
delete document.stack;
delete document.env_content;
delete document.static_content;
if (document.author != await checkJWT(token)) {
if (document.author != await checkJWT(provider, token)) {
ctx.throw(401);
}
const success: boolean = await addMaps(document);
Expand Down Expand Up @@ -71,8 +74,10 @@ async function deleteSubdomain(ctx: Context) {
}
const author = document.author;
const token = document.token;
const provider = document.provider;
delete document.token;
if (author != await checkJWT(token)) {
delete document.provider;
if (author != await checkJWT(provider, token)) {
ctx.throw(401);
}
const data = await deleteMaps(document);
Expand Down
26 changes: 21 additions & 5 deletions src/backend/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@ import {
Application,
Context,
isHttpError,
oakCors,
Router,
Sentry,
Session,
Status,
} from "./dependencies.ts";
import { githubAuth, githubId } from "./auth/github.ts";
import {
githubAuth,
gitlabAuth,
handleJwtAuthentication,
} from "./auth/github.ts";
import { addSubdomain, deleteSubdomain, getSubdomains } from "./main.ts";

const router = new Router();
const app = new Application();
const PORT = 7000;

const id: string = Deno.env.get("GITHUB_OAUTH_CLIENT_ID")!;
const secret: string = Deno.env.get("GITHUB_OAUTH_CLIENT_SECRET")!;
const githubClientId: string = Deno.env.get("GITHUB_OAUTH_CLIENT_ID")!;
const githubClientSecret: string = Deno.env.get("GITHUB_OAUTH_CLIENT_SECRET")!;
const gitlabClientId: string = Deno.env.get("GITLAB_OAUTH_CLIENT_ID")!;
const gitlabClientSecret: string = Deno.env.get("GITLAB_OAUTH_CLIENT_SECRET")!;
const dsn: string = Deno.env.get("SENTRY_DSN")!;
const frontend: string = Deno.env.get("FRONTEND")!;

Sentry.init({
dsn: dsn,
Expand Down Expand Up @@ -44,12 +52,20 @@ app.use(async (ctx: Context, next) => {
app.use(Session.initMiddleware());

router
.post("/auth/github", (ctx) => githubAuth(ctx, id, secret))
.post("/auth/jwt", (ctx) => githubId(ctx))
.post(
"/auth/github",
(ctx) => githubAuth(ctx, githubClientId, githubClientSecret),
)
.post(
"/auth/gitlab",
(ctx) => gitlabAuth(ctx, gitlabClientId, gitlabClientSecret, frontend),
)
.post("/auth/jwt", (ctx) => handleJwtAuthentication(ctx))
.get("/map", (ctx) => getSubdomains(ctx))
.post("/map", (ctx) => addSubdomain(ctx))
.post("/mapdel", (ctx) => deleteSubdomain(ctx));

app.use(oakCors());
app.use(router.routes());
app.use(router.allowedMethods());
app.listen({ port: PORT });
Expand Down
1 change: 0 additions & 1 deletion src/backend/utils/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export default function dockerize(
dockerfile =
"FROM node:latest \n WORKDIR /app \n COPY ./package*.json . \n RUN npm install \n COPY . ." +
build_cmds_mapped + `\n EXPOSE ${port} \n` + execute_cmd;
console.log(port);
}
return dockerfile.toString();
}
37 changes: 37 additions & 0 deletions src/backend/utils/get-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export default async function getProviderUser(
accessToken: string,
provider: string,
) {
let apiUrl = "";
let authorizationHeader = "";

if (provider === "github") {
apiUrl = "https://api.github.com/user";
authorizationHeader = `Bearer ${accessToken}`;
} else if (provider === "gitlab") {
apiUrl = "https://gitlab.com/api/v4/user";
authorizationHeader = `Bearer ${accessToken}`;
} else {
throw new Error("Unsupported provider");
}

const user_resp = await fetch(apiUrl, {
headers: {
Authorization: authorizationHeader,
},
});

if (user_resp.status !== 200) {
throw new Error(`Failed to fetch user data from ${provider}`);
}

const user = await user_resp.json();

if (provider === "github") {
return user.login;
} else if (provider === "gitlab") {
return user.username;
} else {
throw new Error("Unsupported provider");
}
}
9 changes: 0 additions & 9 deletions src/backend/utils/github-user.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/backend/utils/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ const key = await crypto.subtle.generateKey(
["sign", "verify"],
);

async function createJWT(githubId: string) {
async function createJWT(provider: string, githubId: string) {
const token = await create({ alg: "HS512", typ: "JWT" }, {
githubId: githubId,
[`${provider}Id`]: githubId,
}, key);
return token;
}

async function checkJWT(token: string) {
async function checkJWT(provider: string, token: string) {
try {
const payload = await verify(token, key);
return payload.githubId!;
return payload[`${provider}Id`]!;
} catch (error) {
return "not verified";
}
Expand Down
7 changes: 5 additions & 2 deletions src/frontend/.env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
VITE_APP_GITHUB_OAUTH_CLIENT_ID=...
VITE_APP_GITHUB_OAUTH_CLIENT_SECRET=...
VITE_APP_GITHUB_OAUTH_REDIRECT_URL=http://localhost:XXXX/login
VITE_APP_BACKEND_PORT=XXXX
VITE_APP_GITHUB_OAUTH_REDIRECT_URL=.../login
VITE_APP_GITLAB_OAUTH_CLIENT_ID=...
VITE_APP_GITLAB_OAUTH_CLIENT_SECRET=...
VITE_APP_GITLAB_OAUTH_REDIRECT_URL=.../login
VITE_APP_BACKEND=...
Binary file added src/frontend/public/df-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/github-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/frontend/public/gitlab-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 8 additions & 5 deletions src/frontend/src/components/404.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<template>
<div id="container">
<h1>404</h1>
<h2>Page Not Found</h2>
<h4>The page you're looking for doesn't exist</h4>
</div>
<div id="container">
<h1>404</h1>
<h2>Page Not Found</h2>
<h4>The page you're looking for doesn't exist</h4>
</div>
</template>
<script>
export default {};
</script>
Loading

0 comments on commit f8bdb0c

Please sign in to comment.