Remove vercel auth

This commit is contained in:
Jared Palmer 2023-05-22 10:32:06 -04:00
parent 9323d90a36
commit 1af7fb3dfc
11 changed files with 2 additions and 320 deletions

View file

@ -1,11 +1,11 @@
"use server";
import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { getServerSession } from "@/lib/session/get-server-session";
import { revalidatePath } from "next/cache";
export async function removeChat({ id, path }: { id: string; path: string }) {
const session = await getServerSession();
const session = await auth();
const userId = session?.user?.email;
if (!userId || !id) {

View file

@ -1,62 +0,0 @@
import { NextRequest, NextResponse } from 'next/server';
import { serialize } from 'cookie';
import { createSession } from '@/lib/session/create';
import { encryptJWECookie } from '@/lib/cookies/encrypt-jwe';
import ms from 'ms';
import { userSessionCookieName } from '@/lib/session/constants';
export const runtime = 'edge';
const sharedTokenSecret = process.env.SHARE_TOKEN_ENDPOINT_SECRET;
const cookieTTL = ms('1y');
export async function GET(req: NextRequest) {
const { origin, searchParams } = req.nextUrl;
const shareableToken = searchParams.get('shareableToken');
if (!sharedTokenSecret || !shareableToken) {
// TODO: Make an error page
return NextResponse.json({ error: 'Invalid request' }, { status: 500 });
}
const query = new URLSearchParams({
sharedTokenSecret,
});
const response = await fetch(
`https://api.vercel.com/user/tokens/shared/${shareableToken}?${query.toString()}`
);
const { token } = (await response.json()) as { token: string };
const session = await createSession(token);
const cookieValue = await encryptJWECookie(session, '1y');
const next = req.cookies.get('auth-next')?.value;
return NextResponse.redirect(new URL(next ?? '/', origin), {
headers: [
[
'set-cookie',
serialize(userSessionCookieName, cookieValue, {
path: '/',
maxAge: cookieTTL / 1000,
expires: new Date(Date.now() + cookieTTL),
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
}),
],
// There's currently an issue with adding more than one cookie to a response: https://github.com/vercel/next.js/issues/46579
// the `auth-next` cookie will expire in 10 min anyway
// [
// 'set-cookie',
// serialize('auth-next', '', {
// path: '/api/auth/callback',
// maxAge: 0,
// expires: new Date(),
// httpOnly: true,
// })
// ]
],
});
}

View file

@ -1,29 +0,0 @@
import { NextRequest, NextResponse } from 'next/server';
import { serialize } from 'cookie';
export const runtime = 'edge';
export async function GET(req: NextRequest) {
const { origin } = req.nextUrl;
const next = req.nextUrl.searchParams.get('next');
const params = new URLSearchParams({
redirectUrl: new URL('/api/auth/callback', origin).toString(),
});
return NextResponse.redirect(
`https://vercel.com/api/vercel-auth?${params.toString()}`,
{
headers: next
? {
'Set-Cookie': serialize('auth-next', next, {
path: '/api/auth/callback',
maxAge: 60 * 60 * 10,
httpOnly: true,
}),
}
: undefined,
}
);
}

View file

@ -1,25 +0,0 @@
import { userSessionCookieName } from '@/lib/session/constants';
import { serialize } from 'cookie';
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export async function GET(req: NextRequest) {
const { origin, searchParams } = req.nextUrl;
const next = searchParams.get('next');
return NextResponse.redirect(new URL(next ?? '/', origin), {
headers: [
[
'set-cookie',
serialize(userSessionCookieName, '', {
path: '/',
maxAge: 0,
expires: new Date(),
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
}),
],
],
});
}

View file

@ -1,25 +0,0 @@
import { jwtDecrypt, base64url } from 'jose';
export async function decryptJWECookie<T extends string | object = any>(
cookie: string,
secret: string | undefined = process.env.JWE_SECRET
): Promise<T | undefined> {
if (!secret) {
throw new Error('Missing JWE secret');
}
if (typeof cookie !== 'string') return;
try {
const { payload } = await jwtDecrypt(cookie, base64url.decode(secret));
const decoded = payload as T;
// @ts-expect-error jwt field
delete decoded.iat;
// @ts-expect-error jwt field
delete decoded.exp;
return decoded;
} catch {
// Do nothing
}
}

View file

@ -1,16 +0,0 @@
import { EncryptJWT, base64url } from 'jose';
export async function encryptJWECookie<T extends string | object = any>(
payload: T,
expirationTime: string,
secret: string | undefined = process.env.JWE_SECRET
): Promise<string> {
if (!secret) {
throw new Error('Missing JWE secret');
}
return new EncryptJWT(payload as any)
.setExpirationTime(expirationTime)
.setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
.encrypt(base64url.decode(secret));
}

View file

@ -1 +0,0 @@
export const userSessionCookieName = 'user_session';

View file

@ -1,116 +0,0 @@
import { type Session } from './types';
export async function createSession(vercelToken: string): Promise<Session> {
const [user, teams] = await Promise.all([
fetchUser(vercelToken),
fetchTeams(vercelToken),
]);
const plan = getHighestAccountLevel(teams);
return {
user: {
id: user.uid,
name: user.name,
username: user.username,
email: user.email,
avatar: `https://vercel.com/api/www/avatar/?u=${user.username}`,
plan,
},
vercelToken,
};
}
interface VercelUser {
uid: string;
username: string;
email: string;
name: string;
avatar: string;
}
type BillingStatus = 'active' | 'trialing' | 'overdue' | 'canceled' | 'expired';
export const billingPlans = ['hobby', 'pro', 'enterprise'] as const;
export type BillingPlan = (typeof billingPlans)[number];
export interface Billing {
addons?: string | null;
language?: string | null;
plan: BillingPlan;
// TODO: Make this required once the migration has completed
status?: BillingStatus;
name?: string | null;
overdue?: boolean | null;
period: { start: number; end: number } | null;
purchaseOrder?: string | null;
platform?: 'stripe' | 'stripeTestMode';
email?: string;
trial: { start: number; end: number } | null;
}
export interface VercelTeam {
id: string;
slug: string;
name: string;
avatar?: string;
billing?: Billing;
created?: string;
}
async function fetchUser(token: string) {
const response = await fetch('https://vercel.com/api/user', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const { user } = (await response.json()) as { user: VercelUser };
return user;
}
export async function fetchTeams(token: string) {
const response = await fetch('https://vercel.com/api/teams', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const { teams } = (await response.json()) as { teams: VercelTeam[] };
return teams;
}
export function hasActiveProOrEnterpriseAccount(teams: VercelTeam[]) {
return teams.some(
(team) =>
(team.billing?.plan === 'pro' && team.billing.status === 'active') ||
(team.billing?.plan === 'enterprise' && team.billing.status === 'active')
);
}
export function hasActiveProAccount(teams: VercelTeam[]) {
return teams.some(
(team) => team.billing?.plan === 'pro' && team.billing.status === 'active'
);
}
export function isInTrial(teams: VercelTeam[]) {
return (
!hasActiveProOrEnterpriseAccount(teams) &&
teams.some(
(team) =>
team.billing?.plan === 'pro' && team.billing.status === 'trialing'
)
);
}
export function getHighestAccountLevel(teams: VercelTeam[]): BillingPlan {
const plans = teams
.filter(
(team) => team.billing?.plan && team.billing.status === 'active'
// (team.billing?.plan && team.billing.status === 'trialing')
)
.map((team) => team.billing?.plan);
return plans.includes('enterprise')
? 'enterprise'
: plans.includes('pro')
? 'pro'
: 'hobby';
}

View file

@ -1,8 +0,0 @@
import { cookies } from 'next/headers';
import { userSessionCookieName } from './constants';
import { getSessionFromCookie } from './server';
export async function getServerSession() {
const cookieValue = cookies().get(userSessionCookieName)?.value;
return getSessionFromCookie(cookieValue);
}

View file

@ -1,23 +0,0 @@
import { type NextApiRequest } from 'next';
import { type NextRequest } from 'next/server';
import { userSessionCookieName } from './constants';
import { decryptJWECookie } from '../cookies/decrypt-jwe';
import { type Session } from './types';
export async function getSessionFromCookie(cookieValue?: string) {
if (!cookieValue) return;
const session = await decryptJWECookie<Session>(cookieValue);
return session;
}
export async function getSessionFromReq(
req: NextApiRequest | NextRequest
): Promise<Session | undefined> {
const cookieValue =
'get' in req.cookies && typeof req.cookies.get === 'function'
? req.cookies.get(userSessionCookieName)?.value
: (req.cookies as Record<string, string>)[userSessionCookieName];
return getSessionFromCookie(cookieValue);
}

View file

@ -1,13 +0,0 @@
import { BillingPlan } from './create';
export interface Session {
vercelToken: string;
user: {
id: string;
username: string;
email: string;
avatar: string;
name?: string;
plan: BillingPlan;
};
}