Skip to content

Commit

Permalink
Update deps
Browse files Browse the repository at this point in the history
  • Loading branch information
johnpangalos committed Aug 2, 2023
1 parent e735ef6 commit bfdd8ba
Show file tree
Hide file tree
Showing 9 changed files with 1,953 additions and 1,573 deletions.
6 changes: 3 additions & 3 deletions api/package.json
Expand Up @@ -11,13 +11,13 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@cloudflare/workers-types": "^3.16.0",
"@cloudflare/workers-types": "^3.19.0",
"@types/uuid": "^8.3.4",
"wrangler": "^2.1.4"
"wrangler": "^2.19.0"
},
"dependencies": {
"@ssttevee/multipart-parser": "^0.1.9",
"hono": "^2.1.4",
"hono": "^2.7.8",
"toucan-js": "^2.7.0",
"uuid": "^8.3.2"
}
Expand Down
9 changes: 4 additions & 5 deletions api/src/db/posts.ts
@@ -1,4 +1,4 @@
import { Identity } from "@/middleware/auth";
import { Identity, PluginData } from "@/middleware/auth";
import { Account, Post, WigglesContext } from "@/types";
import { DAY, generateSignedUrl, ImageSize, unixTime } from "@/utils";

Expand Down Expand Up @@ -92,9 +92,8 @@ export async function createPosts(c: WigglesContext, postList: Post[]) {
metadata: post,
},
{
key: `post-account-${post.accountId}-${
MAX - Number.parseInt(post.timestamp)
}`,
key: `post-account-${post.accountId}-${MAX -
Number.parseInt(post.timestamp)}`,
value: "",
metadata: post,
},
Expand All @@ -121,7 +120,7 @@ export async function deletePosts(c: WigglesContext, orderKeys: string[]) {
for (const key of orderKeys) {
const feedKey = `post-feed-${key}`;

const access = await c.get("cloudflareAccess");
const access = c.get<PluginData["cloudflareAccess"]>("cloudflareAccess");
const identity: Identity | undefined = await access.JWT.getIdentity();
if (identity === undefined) throw new Error("Identity not found");
const accountKey = `post-account-${identity.email}-${key}`;
Expand Down
9 changes: 6 additions & 3 deletions api/src/handlers/account.ts
@@ -1,11 +1,14 @@
import { Identity } from "@/middleware/auth";
import { Identity, PluginData } from "@/middleware/auth";
import { Account, WigglesContext } from "@/types";

export async function GetMe(c: WigglesContext) {
const access = await c.get("cloudflareAccess");
const identity: Identity = await access.JWT.getIdentity();
const access = c.get<PluginData["cloudflareAccess"]>("cloudflareAccess");
const identity: Identity | undefined = await access.JWT.getIdentity();
if (identity === undefined) throw new Error("Identity not found");

const meStr = await c.env.WIGGLES.get(`account-${identity.email}`);
if (meStr === null) return c.json({ message: "Account invalid" }, 422);
const me = JSON.parse(meStr) as Account;

return c.json(me);
}
4 changes: 2 additions & 2 deletions api/src/handlers/posts.ts
Expand Up @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from "uuid";
import { createPosts, deletePosts, readPosts } from "@/db";
import { Post, WigglesContext } from "@/types";
import { parseFormDataRequest } from "@/utils";
import { Identity } from "@/middleware/auth";
import { Identity, PluginData } from "@/middleware/auth";

export async function GetPosts(c: WigglesContext) {
let size: string | null = c.req.query("size");
Expand Down Expand Up @@ -62,7 +62,7 @@ export async function PostUpload(c: WigglesContext) {
const idList = await Promise.all(promises);
const timestamp = +new Date();

const access = await c.get("cloudflareAccess");
const access = c.get<PluginData["cloudflareAccess"]>("cloudflareAccess");
const identity: Identity | undefined = await access.JWT.getIdentity();
if (identity === undefined) throw new Error("Identity not found");

Expand Down
173 changes: 83 additions & 90 deletions api/src/middleware/auth.ts
@@ -1,6 +1,5 @@
import { MiddlewareHandler, WigglesContext } from "@/types";
import { Next } from "hono";
import { Bindings, Variables } from "hono/dist/hono";

export const getIdentity = async ({
jwt,
Expand Down Expand Up @@ -93,11 +92,14 @@ export type PluginData = {
};

const base64URLDecode = (s: string) => {
s = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
s = s
.replace(/-/g, "+")
.replace(/_/g, "/")
.replace(/\s/g, "");
return new Uint8Array(
Array.prototype.map.call(atob(s), (c: string) =>
(Array.prototype.map.call(atob(s), (c: string) =>
c.charCodeAt(0)
) as unknown as ArrayBufferLike
) as unknown) as ArrayBufferLike
);
};

Expand All @@ -119,100 +121,91 @@ type CertsResponse = {
public_certs: { kid: string; cert: string }[];
};

const generateValidator =
(c: WigglesContext) =>
async (
request: Request
): Promise<{
jwt: string;
payload: object;
}> => {
const jwt = request.cookie("CF_Authorization");

if (jwt === null) throw new Error("JWT not on request");
const parts = jwt.split(".");
if (parts.length !== 3) {
throw new Error("JWT does not have three parts.");
}
const [header, payload, signature] = parts;

const textDecoder = new TextDecoder("utf-8");
const { kid, alg } = JSON.parse(
textDecoder.decode(base64URLDecode(header))
);
if (alg !== "RS256") {
throw new Error("Unknown JWT type or algorithm.");
}
const generateValidator = (c: WigglesContext) => async (
request: Request
): Promise<{
jwt: string;
payload: object;
}> => {
const jwt = request.cookie("CF_Authorization");

if (jwt === undefined) throw new Error("JWT not on request");
const parts = jwt.split(".");
if (parts.length !== 3) {
throw new Error("JWT does not have three parts.");
}
const [header, payload, signature] = parts;

const certsURL = new URL("/cdn-cgi/access/certs", c.env.DOMAIN);

const certsResponse = await fetch(certsURL.toString(), {
cf: {
cacheTtl: 5 * 60,
cacheEverything: true,
},
});
const { keys } = (await certsResponse.json()) as CertsResponse;
if (!keys) {
throw new Error("Could not fetch signing keys.");
}
const jwk = keys.find((key) => key.kid === kid);
if (!jwk) {
throw new Error("Could not find matching signing key.");
}
if (jwk.kty !== "RSA" || jwk.alg !== "RS256") {
throw new Error("Unknown key type of algorithm.");
}
// c.env.WIGGLES.put(jwkKey, JSON.stringify(jwk));
// }
const textDecoder = new TextDecoder("utf-8");
const { kid, alg } = JSON.parse(textDecoder.decode(base64URLDecode(header)));
if (alg !== "RS256") {
throw new Error("Unknown JWT type or algorithm.");
}

const key = await crypto.subtle.importKey(
"jwk",
jwk,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["verify"]
);
const certsURL = new URL("/cdn-cgi/access/certs", c.env.DOMAIN);

const unroundedSecondsSinceEpoch = Date.now() / 1000;
const certsResponse = await fetch(certsURL.toString(), {
cf: {
cacheTtl: 5 * 60,
cacheEverything: true,
},
});
const { keys } = (await certsResponse.json()) as CertsResponse;
if (!keys) {
throw new Error("Could not fetch signing keys.");
}
const jwk = keys.find((key) => key.kid === kid);
if (!jwk) {
throw new Error("Could not find matching signing key.");
}
if (jwk.kty !== "RSA" || jwk.alg !== "RS256") {
throw new Error("Unknown key type of algorithm.");
}
// c.env.WIGGLES.put(jwkKey, JSON.stringify(jwk));
// }

const key = await crypto.subtle.importKey(
"jwk",
jwk,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["verify"]
);

const payloadObj = JSON.parse(textDecoder.decode(base64URLDecode(payload)));
const unroundedSecondsSinceEpoch = Date.now() / 1000;

if (payloadObj.iss && payloadObj.iss !== certsURL.origin) {
throw new Error("JWT issuer is incorrect.");
}
if (payloadObj.aud && payloadObj.aud[0] !== c.env.AUDIENCE) {
throw new Error("JWT audience is incorrect.");
}
if (
payloadObj.exp &&
Math.floor(unroundedSecondsSinceEpoch) >= payloadObj.exp
) {
throw new Error("JWT has expired.");
}
if (
payloadObj.nbf &&
Math.ceil(unroundedSecondsSinceEpoch) < payloadObj.nbf
) {
throw new Error("JWT is not yet valid.");
}
const payloadObj = JSON.parse(textDecoder.decode(base64URLDecode(payload)));

const verified = await crypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
key,
base64URLDecode(signature),
asciiToUint8Array(`${header}.${payload}`)
);
if (!verified) {
throw new Error("Could not verify JWT.");
}
if (payloadObj.iss && payloadObj.iss !== certsURL.origin) {
throw new Error("JWT issuer is incorrect.");
}
if (payloadObj.aud && payloadObj.aud[0] !== c.env.AUDIENCE) {
throw new Error("JWT audience is incorrect.");
}
if (
payloadObj.exp &&
Math.floor(unroundedSecondsSinceEpoch) >= payloadObj.exp
) {
throw new Error("JWT has expired.");
}
if (
payloadObj.nbf &&
Math.ceil(unroundedSecondsSinceEpoch) < payloadObj.nbf
) {
throw new Error("JWT is not yet valid.");
}

return { jwt, payload: payloadObj };
};
const verified = await crypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
key,
base64URLDecode(signature),
asciiToUint8Array(`${header}.${payload}`)
);
if (!verified) {
throw new Error("Could not verify JWT.");
}

export type Environment = {
Bindings: Bindings;
Variables: Variables;
return { jwt, payload: payloadObj };
};

export function auth(): MiddlewareHandler<Response | undefined> {
Expand Down
8 changes: 1 addition & 7 deletions api/src/types/index.ts
@@ -1,6 +1,5 @@
import { Identity } from "@/middleware/auth";
import { Context } from "hono";
import { Bindings, Next, Variables } from "hono/dist/hono";

export type Account = {
displayName: string;
Expand Down Expand Up @@ -38,12 +37,7 @@ export type WigglesEnv = {
};
export type WigglesContext = Context<string, WigglesEnv>;

export type Environment = {
Bindings: Bindings;
Variables: Variables;
};

export type MiddlewareHandler<T> = (
c: WigglesContext,
next: Next
next: () => Promise<void>
) => Promise<T>;
10 changes: 5 additions & 5 deletions package.json
Expand Up @@ -7,15 +7,15 @@
"lint": "pnpm run -r lint"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"eslint": "^8.23.1",
"eslint-plugin-react": "^7.31.8",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.40.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.0.8",
"husky": "^3.0.0",
"lint-staged": "^9.2.0",
"prettier": "^1.18.2",
"typescript": "^4.8.3"
"typescript": "^4.9.5"
},
"husky": {
"hooks": {
Expand Down

0 comments on commit bfdd8ba

Please sign in to comment.