pangolin/server/auth/verifyResourceAccessToken.ts
2025-04-06 13:08:55 -04:00

117 lines
3.2 KiB
TypeScript

import db from "@server/db";
import {
Resource,
ResourceAccessToken,
resourceAccessToken,
resources
} from "@server/db/schemas";
import { and, eq } from "drizzle-orm";
import { isWithinExpirationDate } from "oslo";
import { verifyPassword } from "./password";
import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
export async function verifyResourceAccessToken({
accessToken,
accessTokenId,
resourceId
}: {
accessToken: string;
accessTokenId?: string;
resourceId?: number; // IF THIS IS NOT SET, THE TOKEN IS VALID FOR ALL RESOURCES
}): Promise<{
valid: boolean;
error?: string;
tokenItem?: ResourceAccessToken;
resource?: Resource;
}> {
const accessTokenHash = encodeHexLowerCase(
sha256(new TextEncoder().encode(accessToken))
);
let tokenItem: ResourceAccessToken | undefined;
let resource: Resource | undefined;
if (!accessTokenId) {
const [res] = await db
.select()
.from(resourceAccessToken)
.where(and(eq(resourceAccessToken.tokenHash, accessTokenHash)))
.innerJoin(
resources,
eq(resourceAccessToken.resourceId, resources.resourceId)
);
tokenItem = res?.resourceAccessToken;
resource = res?.resources;
} else {
const [res] = await db
.select()
.from(resourceAccessToken)
.where(and(eq(resourceAccessToken.accessTokenId, accessTokenId)))
.innerJoin(
resources,
eq(resourceAccessToken.resourceId, resources.resourceId)
);
if (res && res.resourceAccessToken) {
if (res.resourceAccessToken.tokenHash?.startsWith("$argon")) {
const validCode = await verifyPassword(
accessToken,
res.resourceAccessToken.tokenHash
);
if (!validCode) {
return {
valid: false,
error: "Invalid access token"
};
}
} else {
const tokenHash = encodeHexLowerCase(
sha256(new TextEncoder().encode(accessToken))
);
if (res.resourceAccessToken.tokenHash !== tokenHash) {
return {
valid: false,
error: "Invalid access token"
};
}
}
}
tokenItem = res?.resourceAccessToken;
resource = res?.resources;
}
if (!tokenItem || !resource) {
return {
valid: false,
error: "Access token does not exist for resource"
};
}
if (
tokenItem.expiresAt &&
!isWithinExpirationDate(new Date(tokenItem.expiresAt))
) {
return {
valid: false,
error: "Access token has expired"
};
}
if (resourceId && resource.resourceId !== resourceId) {
return {
valid: false,
error: "Resource ID does not match"
};
}
return {
valid: true,
tokenItem,
resource
};
}