Skip to content

Commit

Permalink
Update api signature (#17)
Browse files Browse the repository at this point in the history
* update

* update example

* add test #16

* add changeset
  • Loading branch information
toyamarinyon authored Jun 28, 2022
1 parent c792dca commit a2be98c
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 38 deletions.
6 changes: 6 additions & 0 deletions .changeset/ten-bobcats-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"webcrypt-session": minor
"webcrypt-session-example": patch
---

Add save function into session
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ jobs:
- name: Install Dependencies
run: pnpm install

- name: Test
run: pnpm test:ci

- name: Build
run: pnpm build
9 changes: 5 additions & 4 deletions examples/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export default {
request,
{
password: "IF4B#t69!WlX$uS22blaxDvzJJ%$vEh%",
cookie: "session",
}
);
const url = new URL(request.url);
Expand All @@ -47,8 +46,10 @@ export default {
}
try {
const signInParam = signInParamScheme.parse(await request.json());
webCryptSession.username = signInParam.username;
const session = await webCryptSession.toHeaderValue();
await webCryptSession.save({
username: signInParam.username,
});
const session = webCryptSession.toHeaderValue();
if (session == null) {
throw new Error();
}
Expand All @@ -63,7 +64,7 @@ export default {
});
}
}
const session = await webCryptSession.toHeaderValue();
const session = webCryptSession.username;
if (session == null) {
return new Response(
"Please sign in first with http://localhost:8787/signIn"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"test:ci": "turbo run test:ci",
"version": "changeset version",
"release": "pnpm build && changeset publish"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/webcrypt-session/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"build": "run-p build:*",
"build:esbuild": "node script/build.js",
"build:type": "tsc --project tsconfig.build.json",
"test": "vitest"
"test": "vitest",
"test:ci": "vitest run"
},
"exports": {
".": {
Expand Down
14 changes: 7 additions & 7 deletions packages/webcrypt-session/src/adapters/trpc/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ test("works properly", async () => {
const session = await getWebCryptSession(scheme, defaultRequest, option);
expect(session.userId).not.toBeNull();
// @ts-ignore we actually want to test this
const [key, value] = await session.responseHeader()
expect(key).toBe('set-cookie')
expect(value).toBeUndefined()
const [key, value] = session.responseHeader();
expect(key).toBe("set-cookie");
expect(value).toBeUndefined();

session.userId = 1
await session.save({ userId: 1 });
// @ts-ignore we actually want to test this
const [key2, value2] = await session.responseHeader()
expect(key2).toBe('set-cookie')
expect(value2).not.toBeUndefined()
const [key2, value2] = session.responseHeader();
expect(key2).toBe("set-cookie");
expect(value2).not.toBeUndefined();
});
17 changes: 12 additions & 5 deletions packages/webcrypt-session/src/adapters/trpc/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { z } from "zod";
import { createWebCryptSession, WebCryptSessionOption } from "../..";
import {
createWebCryptSession,
WebCryptSession,
WebCryptSessionOption,
} from "../..";
import {
AnyRouter,
inferRouterContext,
inferRouterError,
ProcedureType,
TRPCError,
} from "@trpc/server";
import { TRPCResponse } from "@trpc/server/rpc"
import { TRPCResponse } from "@trpc/server/rpc";

export async function getWebCryptSession<T extends z.AnyZodObject>(
scheme: T,
Expand All @@ -17,14 +21,14 @@ export async function getWebCryptSession<T extends z.AnyZodObject>(
const webCryptSession = await createWebCryptSession(scheme, req, option);

// Call this function inside tRPC.responseMeta to set cookie into header
async function responseHeader<TRouter extends AnyRouter>(opts: {
function responseHeader<TRouter extends AnyRouter>(opts: {
data: TRPCResponse<unknown, inferRouterError<TRouter>>[];
ctx?: inferRouterContext<TRouter>;
paths?: string[];
type: ProcedureType | "unknown";
errors: TRPCError[];
}) {
const setCookieHeader = await webCryptSession.toHeaderValue();
const setCookieHeader = webCryptSession.toHeaderValue();
return ["set-cookie", setCookieHeader];
}

Expand All @@ -34,5 +38,8 @@ export async function getWebCryptSession<T extends z.AnyZodObject>(

// Block calls to WebCryptSession's internal functions
// allowing access only to the scheme.
return (webCryptSession as unknown) as z.infer<T>;
return (webCryptSession as unknown) as Exclude<
WebCryptSession<T>,
"toHeaderValue"
>;
}
28 changes: 22 additions & 6 deletions packages/webcrypt-session/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ test("session not exists", async () => {
option
);
expect(webCryptSession.userId).toBeUndefined();
const setCookieHeader = await webCryptSession.toHeaderValue();
const setCookieHeader = webCryptSession.toHeaderValue();
expect(setCookieHeader).toBeUndefined();
});

Expand All @@ -68,7 +68,7 @@ test("session exists", async () => {
expect(webCryptSession.userId).toBe(1);
});

test("update session", async () => {
test("save session", async () => {
const webCryptSession = await createWebCryptSession(
scheme,
new Request("http://loclahost:8989/test"),
Expand All @@ -77,9 +77,10 @@ test("update session", async () => {
expect(webCryptSession.userId).toBeUndefined();
const nonSessionHeader = await webCryptSession.toHeaderValue();
expect(nonSessionHeader).toBeUndefined();

webCryptSession.userId = 1;
const sessionHeader = await webCryptSession.toHeaderValue();
await webCryptSession.save({
userId: 1,
});
const sessionHeader = webCryptSession.toHeaderValue();
expect(sessionHeader).not.toBeUndefined();
});

Expand All @@ -93,7 +94,22 @@ test("invalid session", async () => {
}),
option
);
const nonSessionHeader = await webCryptSession.toHeaderValue();
const nonSessionHeader = webCryptSession.toHeaderValue();
expect(webCryptSession.userId).toBeUndefined();
expect(nonSessionHeader).toBeUndefined();
});

test("there is no session in cookie", async () => {
const webCryptSession = await createWebCryptSession(
scheme,
new Request("http://loclahost:8989/test", {
headers: {
cookie: "googleAnalytics=%7B%22userId%22%3A1%7",
},
}),
option
);
const nonSessionHeader = webCryptSession.toHeaderValue();
expect(webCryptSession.userId).toBeUndefined();
expect(nonSessionHeader).toBeUndefined();
});
33 changes: 18 additions & 15 deletions packages/webcrypt-session/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ function JSONCookie(
async function decryptSession(
key: CryptoKey,
scheme: z.AnyZodObject,
sessionEncrypted: string,
counter: string
cookie: string | null
) {
try {
const cookieParsed = cookie ? parse(cookie) : { session: "" };
const [sessionEncrypted, counter] = cookieParsed.session.split("--");
const sessionDecrypted = await decrypt(key, sessionEncrypted, counter);
const session = JSONCookie(scheme, sessionDecrypted);
return session;
Expand All @@ -39,7 +40,8 @@ async function decryptSession(
}

export type WebCryptSession<T extends AnyZodObject> = z.infer<T> & {
toHeaderValue: () => Promise<string | undefined>;
save: (scheme: z.infer<T>) => Promise<void>;
toHeaderValue: () => string | undefined;
};

export async function createWebCryptSession<T extends AnyZodObject>(
Expand All @@ -61,21 +63,22 @@ export async function createWebCryptSession<T extends AnyZodObject>(
}
const key = await importKey(parsedOption.data.password);

const rawCookie = req.headers.get("cookie");
const cookie = rawCookie ? parse(rawCookie) : { session: "" };
const [sessionEncrypted, counter] = cookie.session.split("--");

const session = await decryptSession(key, scheme, sessionEncrypted, counter);
const session = await decryptSession(key, scheme, req.headers.get("cookie"));
let encryptedSession: string;
let encryptedCounter: string;
return Object.assign(session, {
toHeaderValue: async () => {
try {
const safeSession = scheme.parse(session);
const safeSessionString = JSON.stringify(safeSession);
const { encrypted, counter } = await encrypt(key, safeSessionString);
return serialize("session", `${encrypted}--${counter}`);
} catch (_) {
save: async (newSession: z.infer<T>) => {
const safeSession = scheme.parse(newSession);
const safeSessionString = JSON.stringify(safeSession);
const { encrypted, counter } = await encrypt(key, safeSessionString);
encryptedSession = encrypted;
encryptedCounter = counter;
},
toHeaderValue: () => {
if (encryptedSession == null || encryptedCounter == null) {
return undefined;
}
return serialize("session", `${encryptedSession}--${encryptedCounter}`);
},
});
}
5 changes: 5 additions & 0 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"dependsOn": ["build"],
"outputs": [],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
},
"test:ci": {
"dependsOn": ["build"],
"outputs": [],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"]
}
}
}

0 comments on commit a2be98c

Please sign in to comment.