82 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			82 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Handlers } from "$fresh/server.ts";
 | |
| import { create, getNumericDate } from "https://deno.land/x/djwt@v2.2/mod.ts";
 | |
| import { oauth2Client } from "@lib/auth.ts";
 | |
| import { getCookies, setCookie } from "@std/http/cookie";
 | |
| import { codeChallengeMap } from "./login.ts";
 | |
| import { GITEA_SERVER, JWT_SECRET, SESSION_DURATION } from "@lib/env.ts";
 | |
| import { GiteaOauthUser } from "@lib/types.ts";
 | |
| import { BadRequestError } from "@lib/errors.ts";
 | |
| import { db } from "@lib/db/sqlite.ts";
 | |
| import { userTable } from "@lib/db/schema.ts";
 | |
| import { eq } from "drizzle-orm";
 | |
| 
 | |
| export const handler: Handlers = {
 | |
|   async GET(request) {
 | |
|     if (!JWT_SECRET) {
 | |
|       throw new BadRequestError();
 | |
|     }
 | |
| 
 | |
|     // Exchange the authorization code for an access token
 | |
|     const cookies = getCookies(request.headers);
 | |
| 
 | |
|     const stored = codeChallengeMap.get(cookies["code_challenge"]);
 | |
|     if (!stored) {
 | |
|       throw new BadRequestError();
 | |
|     }
 | |
| 
 | |
|     const { codeVerifier, redirect } = stored;
 | |
| 
 | |
|     const tokens = await oauth2Client.code.getToken(request.url, {
 | |
|       codeVerifier,
 | |
|     });
 | |
| 
 | |
|     // Use the access token to make an authenticated API request
 | |
|     const userInfo = `${GITEA_SERVER}/login/oauth/userinfo`;
 | |
|     const userResponse = await fetch(userInfo, {
 | |
|       headers: {
 | |
|         Authorization: `token ${tokens.accessToken}`,
 | |
|       },
 | |
|     });
 | |
| 
 | |
|     const oauthUser = await userResponse.json() as GiteaOauthUser;
 | |
| 
 | |
|     let user = await db.select().from(userTable).where(
 | |
|       eq(userTable.name, oauthUser.name),
 | |
|     ).limit(1).then((users) => users[0]);
 | |
| 
 | |
|     if (!user) {
 | |
|       const res = await db.insert(userTable).values({
 | |
|         id: crypto.randomUUID(),
 | |
|         email: oauthUser.email,
 | |
|         name: oauthUser.name,
 | |
|       }).returning();
 | |
|       user = res[0];
 | |
|     }
 | |
| 
 | |
|     const jwt = await create({ alg: "HS512", type: "JWT" }, {
 | |
|       id: user.id,
 | |
|       name: user.name,
 | |
|       exp: getNumericDate(SESSION_DURATION),
 | |
|     }, JWT_SECRET);
 | |
| 
 | |
|     const headers = new Headers({
 | |
|       location: redirect || "/",
 | |
|     });
 | |
| 
 | |
|     setCookie(headers, {
 | |
|       name: "session_cookie",
 | |
|       value: jwt,
 | |
|       path: "/",
 | |
|       maxAge: SESSION_DURATION,
 | |
|       httpOnly: false,
 | |
|       secure: true,
 | |
|       sameSite: "Lax",
 | |
|     });
 | |
| 
 | |
|     return new Response(null, {
 | |
|       headers,
 | |
|       status: 302,
 | |
|     });
 | |
|   },
 | |
| };
 |