Skip to content

Commit

Permalink
sso working
Browse files Browse the repository at this point in the history
  • Loading branch information
potts99 committed Dec 1, 2023
1 parent f1bafb1 commit 4f2a69a
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 74 deletions.
115 changes: 114 additions & 1 deletion apps/api/src/controllers/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import axios from "axios";
import bcrypt from "bcrypt";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import jwt from "jsonwebtoken";
Expand Down Expand Up @@ -137,6 +138,118 @@ export function authRoutes(fastify: FastifyInstance) {
}
);

// Checks if a user is SSO or password
fastify.post(
"/api/v1/auth/sso/check",
async (request: FastifyRequest, reply: FastifyReply) => {
let { email } = request.body as {
email: string;
};

let user = await prisma.user.findUnique({
where: { email },
});

if (!user) {
reply.code(401).send({
message: "Invalid email",
success: false,
});
}

const authtype = await prisma.config.findMany({
where: {
sso_active: true,
},
});

const provider = await prisma.provider.findMany();
const oauth = provider[0];

console.log(authtype);

if (authtype.length === 0) {
reply.code(200).send({
success: true,
message: "SSO not enabled",
oauth: false,
});
}

const url = "https://github.com/login/oauth/authorize";

reply.send({
oauth: true,
success: true,
ouath_url: `${url}?client_id=${oauth.clientId}&redirect_uri=${oauth.redirectUri}&state=${email}&login=${email}`,
});
}
);

fastify.get(
"/api/v1/auth/sso/login/callback",
async (request: FastifyRequest, reply: FastifyReply) => {
const { code, state } = request.query as { code: string; state: string };

const data = await axios.post(
`https://github.com/login/oauth/access_token`,
{
client_id: "6b0c6bf8f44a4e0fa153",
client_secret: "4967e55b5f98e0ed189072b0584ef2a2a16e673b",
code: code,
redirect_uri: "http://localhost:5003/api/v1/auth/sso/login/check",
},
{
headers: {
Accept: "application/json",
},
}
);

const access_token = data.data;

if (access_token) {
let user = await prisma.user.findUnique({
where: { email: state },
});

if (!user) {
reply.code(401).send({
message: "Invalid email",
});
}

var b64string = process.env.SECRET;
var buf = new Buffer(b64string!, "base64"); // Ta-da

let token = jwt.sign(
{
data: { id: user!.id },
},
buf,
{ expiresIn: "8h" }
);

await prisma.session.create({
data: {
userId: user!.id,
sessionToken: token,
expires: new Date(Date.now() + 8 * 60 * 60 * 1000),
},
});

reply.send({
token,
success: true,
});
} else {
reply.status(403).send({
success: false,
});
}
}
);

// Delete a user
fastify.delete(
"/api/v1/auth/user/:id",
Expand Down Expand Up @@ -177,7 +290,7 @@ export function authRoutes(fastify: FastifyInstance) {

if (!user) {
reply.code(401).send({
message: "Invalid email or password",
message: "Invalid user",
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- You are about to drop the `Account` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `VerificationToken` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "Account" DROP CONSTRAINT "Account_userId_fkey";

-- AlterTable
ALTER TABLE "Provider" ADD COLUMN "redirectUri" TEXT;

-- DropTable
DROP TABLE "Account";

-- DropTable
DROP TABLE "VerificationToken";
30 changes: 1 addition & 29 deletions apps/api/src/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,7 @@ model Provider {
active Boolean
issuer String?
tenantId String?
}

model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
refresh_token_expires_in Int?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
redirectUri String?
}

model Session {
Expand Down Expand Up @@ -69,19 +50,10 @@ model User {
Team Team? @relation(fields: [teamId], references: [id])
teamId String?
Comment Comment[]
Account Account[]
Session Session[]
TimeTracking TimeTracking[]
}

model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}

model Team {
id String @id @default(uuid())
createdAt DateTime @default(now())
Expand Down
6 changes: 2 additions & 4 deletions apps/api/src/prisma/seed.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

async function main() {
const admin = await prisma.user.upsert({
await prisma.user.upsert({
where: { email: "[email protected]" },
update: {},
create: {
Expand All @@ -15,7 +15,7 @@ async function main() {
},
});

const internal = await prisma.client.upsert({
await prisma.client.upsert({
where: { email: `[email protected]` },
update: {},
create: {
Expand All @@ -26,8 +26,6 @@ async function main() {
active: true,
},
});

console.log({ admin, internal });
}

main()
Expand Down
5 changes: 3 additions & 2 deletions apps/client/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }: any) {

if (router.asPath.slice(0, 5) === "/auth") {
return (
<SessionProvider>
<>
<Notifications position="top-right" />
<Component {...pageProps} />
</SessionProvider>
</>
);
}

Expand Down
108 changes: 70 additions & 38 deletions apps/client/pages/auth/login.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,67 @@
import { notifications } from "@mantine/notifications";
import { setCookie } from "cookies-next";
import { useRouter } from "next/router";
import { useState } from "react";

import { useUser } from "../../store/session";

export default function Login({}) {
const router = useRouter();

const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [status, setStatus] = useState("idle");

const { setUser } = useUser();
const [auth, setAuth] = useState("oauth");

async function postData() {
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
})
.then((res) => res.json())
.then((res) => {
console.log(res);
if (res.user) {
setCookie("session", res.token);
setUser(res.user);
if (res.user.firstLogin) {
router.push("/onboarding");
if (auth === "oauth") {
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/sso/check`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
})
.then((res) => res.json())
.then(async (res) => {
if (res.success && res.oauth) {
router.push(res.ouath_url);
} else {
router.push("/");
if (!res.success) {
notifications.show({
title: "Error",
message:
"There was an error logging in, please try again. If this issue persists, please contact support via the discord.",
color: "red",
autoClose: 5000,
});
} else {
setAuth("password");
}
}
}
});
});
} else {
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
})
.then((res) => res.json())
.then(async (res) => {
if (res.user) {
setCookie("session", res.token);
if (res.user.firstLogin) {
router.push("/onboarding");
} else {
router.push("/");
}
} else {
notifications.show({
title: "Error",
message:
"There was an error logging in, please try again. If this issue persists, please contact support via the discord.",
color: "red",
autoClose: 5000,
});
}
});
}
}

return (
Expand Down Expand Up @@ -75,25 +105,27 @@ export default function Login({}) {
</div>
</div>

<div>
<label
htmlFor="password"
className="block text-sm font-medium text-gray-700"
>
Password
</label>
<div className="mt-1">
<input
id="password"
name="password"
type="password"
autoComplete="password"
required
onChange={(e) => setPassword(e.target.value)}
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm"
/>
{auth !== "oauth" && (
<div>
<label
htmlFor="password"
className="block text-sm font-medium text-gray-700"
>
Password
</label>
<div className="mt-1">
<input
id="password"
name="password"
type="password"
autoComplete="password"
required
onChange={(e) => setPassword(e.target.value)}
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm"
/>
</div>
</div>
</div>
)}

{/* <div className="flex items-center justify-between">
<div className="flex items-center">
Expand Down
Loading

0 comments on commit 4f2a69a

Please sign in to comment.