removed oAuth endpoint from dispatch, started oAuth endpoints for moodle

This commit is contained in:
PxlLoewe
2025-02-25 23:23:18 +01:00
parent 4c35257cf0
commit b81bab1dc2
7 changed files with 137 additions and 182 deletions

View File

@@ -1,43 +0,0 @@
'use client';
import { useSearchParams } from 'next/navigation';
import { Service } from '../page';
import { generateToken } from './action';
export const Authorize = ({ service }: { service: Service }) => {
const searchParams = useSearchParams();
const legitimeUrl = service.approvedUrls.some((url) =>
searchParams.get('redirect_uri')?.startsWith(url)
);
if (!legitimeUrl)
return (
<div className="card-body">
<h1 className="text-4xl font-bold">Unerlaubter Zugriff</h1>
<p>Du greifst von einem ncith genehmigtem Server auf diese URL zu</p>
</div>
);
return (
<form className="card-body" onSubmit={(e) => e.preventDefault()}>
<h1 className="text-4xl font-bold">Zugriff zulassen</h1>
<p>
Die Anwendung <strong>{service.name}</strong> möchte auf deine Daten
zugreifen.
</p>
<div className="space-x-4">
<button type="button" className="btn">
Verweigern
</button>
<button
type="submit"
className="btn btn-primary"
onClick={async () => {
const code = await generateToken(service);
window.location.href = `${searchParams.get('redirect_uri')}?code=${code}`;
}}
>
Zulassen
</button>
</div>
</form>
);
};

View File

@@ -1,24 +0,0 @@
'use server';
import { getServerSession } from '../../../api/auth/[...nextauth]/auth';
import { Service } from '../page';
import { PrismaClient } from '@repo/db';
const prisma = new PrismaClient();
export const generateToken = async (service: Service) => {
const session = await getServerSession();
if (!session) return null;
const accessToken = Array.from({ length: 10 }, () =>
Math.floor(Math.random() * 10)
).join('');
const code = await prisma.oAuthToken.create({
data: {
clientId: service.id,
userId: session.user.id,
accessToken: accessToken,
},
});
return code;
};

View File

@@ -1,26 +0,0 @@
import { Authorize } from "./_components/Authorize";
export const services = [
{
id: "123456",
service: "dispatch",
name: "Leitstellendisposition",
approvedUrls: ["http://localhost:3001"],
},
];
export type Service = (typeof services)[number];
export default async ({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) => {
const { service: serviceId } = await searchParams;
const service = services.find((service) => service.id === serviceId);
if (!service) {
return <div>Service not found</div>;
}
return <Authorize service={service} />;
};

View File

@@ -1,48 +1,48 @@
'use client'; "use client";
import { redirect, useSearchParams } from 'next/navigation'; import { redirect, useSearchParams } from "next/navigation";
import { Service } from '../page'; import { Service } from "../page";
import { generateToken } from './action'; import { generateToken } from "./action";
import { useSession } from 'next-auth/react'; import { useSession } from "next-auth/react";
export const Authorize = ({ service }: { service: Service }) => { export const Authorize = ({ service }: { service: Service }) => {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const legitimeUrl = service.approvedUrls.some((url) => const legitimeUrl = service.approvedUrls.some((url) =>
searchParams.get('redirect_uri')?.startsWith(url) searchParams.get("redirect_uri")?.startsWith(url),
); );
const { data: session } = useSession(); const { data: session } = useSession();
console.log(session); console.log(session);
if (!session) if (!session)
redirect('/login?redirect=' + encodeURIComponent(window.location.href)); redirect("/login?redirect=" + encodeURIComponent(window.location.href));
if (!legitimeUrl) if (!legitimeUrl)
return ( return (
<div className="card-body"> <div className="card-body">
<h1 className="text-4xl font-bold">Unerlaubter Zugriff</h1> <h1 className="text-4xl font-bold">Unerlaubter Zugriff</h1>
<p>Du greifst von einem nicht genehmigtem Server auf diese URL zu</p> <p>Du greifst von einem nicht genehmigtem Server auf diese URL zu</p>
</div> </div>
); );
return ( return (
<form className="card-body" onSubmit={(e) => e.preventDefault()}> <form className="card-body" onSubmit={(e) => e.preventDefault()}>
<h1 className="text-4xl font-bold">Zugriff zulassen</h1> <h1 className="text-4xl font-bold">Zugriff zulassen</h1>
<p> <p>
Die Anwendung <strong>{service.name}</strong> möchte auf deine Daten Die Anwendung <strong>{service.name}</strong> möchte auf deine Daten
zugreifen. zugreifen.
</p> </p>
<div className="space-x-4"> <div className="space-x-4">
<button type="button" className="btn"> <button type="button" className="btn">
Verweigern Verweigern
</button> </button>
<button <button
type="submit" type="submit"
className="btn btn-primary" className="btn btn-primary"
onClick={async () => { onClick={async () => {
const code = await generateToken(service); const code = await generateToken(service);
window.location.href = `${searchParams.get('redirect_uri')}?code=${code?.accessToken}`; window.location.href = `${searchParams.get("redirect_uri")}?code=${code?.accessToken}`;
}} }}
> >
Zulassen Zulassen
</button> </button>
</div> </div>
</form> </form>
); );
}; };

View File

@@ -1,32 +1,39 @@
import { Authorize } from './_components/Authorize'; import { Authorize } from "./_components/Authorize";
export const services = [ export const services = [
{ {
id: '123456', id: "1",
service: 'dispatch', service: "dispatch",
name: 'Leitstellendisposition', name: "Leitstellendisposition",
approvedUrls: ['http://localhost:3001'], approvedUrls: ["http://localhost:3001"],
}, },
{ {
id: '789456', id: "2",
service: 'desktop', service: "desktop",
name: 'Desktop client', name: "Desktop client",
approvedUrls: ['var'], approvedUrls: ["var"],
}, },
{
id: "3",
secret: "d0f3e4e4",
service: "moodle",
name: "Moodle",
approvedUrls: ["https://moodle.virtualairrescue.com"],
},
]; ];
export type Service = (typeof services)[number]; export type Service = (typeof services)[number];
export default async ({ export default async ({
searchParams, searchParams,
}: { }: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>; searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) => { }) => {
const { service: serviceId } = await searchParams; const { service: serviceId } = await searchParams;
const service = services.find((service) => service.id === serviceId); const service = services.find((service) => service.id === serviceId);
if (!service) { if (!service) {
return <div>Service not found</div>; return <div>Service not found</div>;
} }
return <Authorize service={service} />; return <Authorize service={service} />;
}; };

View File

@@ -1,28 +1,48 @@
import { PrismaClient } from '@repo/db'; import { prisma, PrismaClient } from "@repo/db";
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from "next/server";
import { sign } from 'jsonwebtoken'; import { sign } from "jsonwebtoken";
export const GET = async (req: NextRequest) => { export const GET = async (req: NextRequest) => {
const client = new PrismaClient(); const client = new PrismaClient();
const accessToken = req.nextUrl.searchParams.get('token'); const accessToken =
if (!accessToken) req.nextUrl.searchParams.get("token") ||
return new Response('No access token provided', { status: 400 }); req.nextUrl.searchParams.get("code");
const accessRequest = await client.oAuthToken.findFirst({ const client_id = req.nextUrl.searchParams.get("client_id");
where: { const client_secret = req.nextUrl.searchParams.get("client_secret");
accessToken: accessToken,
},
include: {
user: true,
},
});
if (!accessRequest)
return new Response('Access token not found', { status: 404 });
const jwt = sign(accessRequest.user, process.env.NEXTAUTH_SECRET as string, { if (!accessToken)
expiresIn: '30d', return new Response("No access token provided", { status: 400 });
});
return Response.json({ if (!client_id)
user: accessRequest.user, return new Response("No client ID token provided", { status: 400 });
jwt,
}); const accessRequest = await client.oAuthToken.findFirst({
where: {
accessToken: accessToken,
clientId: client_id,
},
include: {
user: true,
},
});
if (!accessRequest)
return new Response("Access token not found", { status: 404 });
if (new Date().getTime() - accessRequest?.createdAt.getTime() > 60 * 1000) {
await prisma.oAuthToken.delete({
where: {
id: accessRequest.id,
},
});
return new Response("Code expired", { status: 400 });
}
const jwt = sign(accessRequest.user, process.env.NEXTAUTH_SECRET as string, {
expiresIn: "30d",
});
return Response.json({
user: accessRequest.user,
jwt,
});
}; };

View File

@@ -0,0 +1,21 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "../auth/[...nextauth]/auth";
import { prisma } from "@repo/db";
export const GET = async (req: NextRequest) => {
const session = await getServerSession();
if (!session) {
return {
status: 401,
body: "Unauthorized",
};
}
const user = await prisma.user.findUnique({
where: {
id: session.user.id,
},
});
return NextResponse.json(user);
};