Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
-- CreateTable
CREATE TABLE "Token" (
"id" UUID NOT NULL,
"token" TEXT NOT NULL,
"tokenHash" TEXT NOT NULL,
"type" TEXT NOT NULL,
"meta" JSONB NOT NULL,
"trace" JSONB NOT NULL,
Expand All @@ -13,4 +13,4 @@ CREATE TABLE "Token" (
);

-- CreateIndex
CREATE UNIQUE INDEX "Token_token_key" ON "Token"("token");
CREATE UNIQUE INDEX "Token_tokenHash_key" ON "Token"("tokenHash");
2 changes: 1 addition & 1 deletion modules/tokens/db/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ datasource db {

model Token {
id String @id @default(uuid()) @db.Uuid
token String @unique
tokenHash String @unique
type String
meta Json
trace Json
Expand Down
8 changes: 3 additions & 5 deletions modules/tokens/scripts/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ScriptContext } from "../module.gen.ts";
import { TokenWithSecret } from "../utils/types.ts";
import { tokenFromRow } from "../utils/types.ts";
import { TokenWithSecret, tokenFromRowWithSecret, hash } from "../utils/types.ts";

export interface Request {
type: string;
Expand All @@ -17,10 +16,9 @@ export async function run(
req: Request,
): Promise<Response> {
const tokenStr = generateToken(req.type);

const token = await ctx.db.token.create({
data: {
token: tokenStr,
tokenHash: await hash(tokenStr),
type: req.type,
meta: req.meta,
trace: ctx.trace,
Expand All @@ -29,7 +27,7 @@ export async function run(
});

return {
token: tokenFromRow(token),
token: tokenFromRowWithSecret(token, tokenStr),
};
}

Expand Down
5 changes: 2 additions & 3 deletions modules/tokens/scripts/extend.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { ScriptContext } from "../module.gen.ts";
import { TokenWithSecret } from "../utils/types.ts";
import { tokenFromRow } from "../utils/types.ts";
import { Token, tokenFromRow } from "../utils/types.ts";

export interface Request {
token: string;
newExpiration: string | null;
}

export interface Response {
token: TokenWithSecret;
token: Token;
}

export async function run(
Expand Down
5 changes: 2 additions & 3 deletions modules/tokens/scripts/get.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ScriptContext } from "../module.gen.ts";
import { Token } from "../utils/types.ts";
import { tokenFromRow } from "../utils/types.ts";
import { Token, tokenFromRow } from "../utils/types.ts";

export interface Request {
tokenIds: string[];
Expand All @@ -23,7 +22,7 @@ export async function run(
},
});

const tokens = rows.map(tokenFromRow);
const tokens = rows.map(row => tokenFromRow(row));

return { tokens };
}
12 changes: 8 additions & 4 deletions modules/tokens/scripts/get_by_token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ScriptContext } from "../module.gen.ts";
import { Token, tokenFromRow } from "../utils/types.ts";
import { Token, tokenFromRowWithSecret, hash } from "../utils/types.ts";

export interface Request {
tokens: string[];
Expand All @@ -13,18 +13,22 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const hashed = await Promise.all(req.tokens.map(hash));
console.log(hashed);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

const rows = await ctx.db.token.findMany({
where: {
token: {
in: req.tokens,
tokenHash: {
in: hashed,
},
},
orderBy: {
createdAt: "desc",
},
});

const tokens = rows.map(tokenFromRow);
// Map from the hashed secrets to the original secrets
const hashMap = Object.fromEntries(req.tokens.map((token, i) => [hashed[i], token]));
const tokens = rows.map(row => tokenFromRowWithSecret(row, hashMap[row.tokenHash]));

return { tokens };
}
3 changes: 2 additions & 1 deletion modules/tokens/tests/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ test(
meta: { foo: "bar" },
});

await ctx.modules.tokens.revoke({ tokenIds: [token.id] });
const { updates } = await ctx.modules.tokens.revoke({ tokenIds: [token.id] });
assertEquals(updates[token.id], "REVOKED");

const error = await assertRejects(async () => {
await ctx.modules.tokens.validate({ token: token.token });
Expand Down
35 changes: 33 additions & 2 deletions modules/tokens/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,44 @@ export interface TokenWithSecret extends Token {
token: string;
}

export function tokenFromRow(

export function withoutKeys<T extends object, K extends keyof T>(
obj: T,
keys: K[]
): Omit<T, K> {
const copy = { ...obj };
for (const key of keys) {
delete copy[key];
}
return copy;
}

export function tokenFromRowWithSecret(
row: prisma.Prisma.TokenGetPayload<any>,
origToken: string
): TokenWithSecret {
return {
...row,
...tokenFromRow(row),
token: origToken,
}
}

export function tokenFromRow(
row: prisma.Prisma.TokenGetPayload<any>,
): Token {
return {
...withoutKeys(row, ["tokenHash"]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is more lines of code and harder to understand than just manually passing in each parameter from the row in to the response.

createdAt: row.createdAt.toISOString(),
expireAt: row.expireAt?.toISOString() ?? null,
revokedAt: row.revokedAt?.toISOString() ?? null,
};
}

export async function hash(token: string): Promise<string> {
const encoder = new TextEncoder();
const data = encoder.encode(token);
const hash = await crypto.subtle.digest("SHA-256", data);
const digest = Array.from(new Uint8Array(hash));
const strDigest = digest.map(b => b.toString(16).padStart(2, "0")).join("");
return strDigest;
}