Skip to content
Merged
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
14 changes: 6 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,27 @@
"test": "vitest run"
},
"dependencies": {
"@cfworker/base64url": "^1.12.5",
"@tsndr/cloudflare-worker-jwt": "^3.2.1",
"@tsndr/cloudflare-worker-jwt": "^3.2.2",
"itty-router": "^5.0.24",
"rfc4648": "^1.5.4",
"zod": "^4.4.3"
},
"devDependencies": {
"@cloudflare/vitest-pool-workers": "^0.19.0",
"@cloudflare/vitest-pool-workers": "^0.20.1",
"@eslint/js": "^10.0.1",
"@types/node": "26.1.2",
"@typescript/native": "npm:typescript@^7.0.2",
"eslint": "^10.8.0",
"miniflare": "4.20260722.1",
"prettier": "3.9.6",
"typescript": "npm:@typescript/typescript6@^6.0.2",
"typescript-eslint": "^8.65.0",
"vite": "^8.1.5",
"typescript-eslint": "^8.66.0",
"vitest": "^4.1.10",
"wrangler": "^4.115.0"
"wrangler": "^4.118.0"
},
"engines": {
"node": ">=24.0.0",
"pnpm": ">=11.0.0"
},
"license": "Apache-2.0",
"packageManager": "pnpm@11.17.0"
"packageManager": "pnpm@11.20.0"
}
573 changes: 244 additions & 329 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions src/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { decode } from "@cfworker/base64url";
import { errorString } from "./utils";
import { base64UrlDecode, errorString } from "./utils";

export type RegistryTokenCapability = "push" | "pull";
export type RegistryAuthProtocolTokenPayload = {
Expand Down Expand Up @@ -42,7 +41,7 @@ export function stripUsernamePasswordFromHeader(r: Request): [string, string] |

try {
// Decodes the base64 value and performs unicode normalization.
const decoded = decode(encoded);
const decoded = base64UrlDecode(encoded);

// The username & password are split by the first colon.
//=> example: "username:password"
Expand Down
4 changes: 2 additions & 2 deletions src/token.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { decode } from "@cfworker/base64url";
import jwt from "@tsndr/cloudflare-worker-jwt";
import {
RegistryTokenCapability,
RegistryAuthProtocolTokenPayload,
stripUsernamePasswordFromHeader,
Authenticator,
} from "./auth";
import { base64UrlDecode } from "./utils";

export function importKeyFromBase64(key: string): JsonWebKeyWithKid {
// Decodes the base64 value and performs unicode normalization.
// The library's `JsonWebKeyWithKid` type requires `kid`, but ES256/HS256 sign
// and verify only need the key material at runtime, so casting is safe.
return JSON.parse(decode(key)) as JsonWebKeyWithKid;
return JSON.parse(base64UrlDecode(key)) as JsonWebKeyWithKid;
}

export async function newRegistryTokens(jwtPublicKey: string): Promise<RegistryTokens> {
Expand Down
11 changes: 11 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { base64url } from "rfc4648";
import { prettifyError, ZodError } from "zod";

export async function readableToBlob(
Expand Down Expand Up @@ -64,3 +65,13 @@ export async function wrap<T, E = unknown>(fn: Promise<T>): Promise<[T, null] |
export function jsonHeaders(): { "content-type": "application/json" } {
return { "content-type": "application/json" };
}

const textDecoder = new TextDecoder();

export function base64UrlDecode(s: string): string {
return textDecoder.decode(base64url.parse(s, { loose: true }));
}

export function base64UrlEncode(s: string): string {
return Buffer.from(s, "utf8").toString("base64url");
}
10 changes: 5 additions & 5 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { RegistryAuthProtocolTokenPayload } from "../src/auth";
import { registries } from "../src/registry/registry";
import type { ReferrerDescriptor } from "../src/registry/registry";
import { isDockerDotIO, RegistryHTTPClient } from "../src/registry/http";
import { encode } from "@cfworker/base64url";
import { ManifestSchema } from "../src/manifest";
import { limit } from "../src/chunk";
import worker from "../index";
import { env } from "cloudflare:workers";
import { createExecutionContext, reset, waitOnExecutionContext } from "cloudflare:test";
import { base64UrlEncode } from "../src/utils";

afterEach(async () => {
await reset();
Expand Down Expand Up @@ -149,14 +149,14 @@ describe("v2", () => {
test("Username password authenticatiom fails gracefully when wrong format", async () => {
const res = await fetchUnauth(
createRequest("GET", `/v2/`, null, {
Authorization: `Basic ${encode("hello")}:${encode("t")}`,
Authorization: `Basic ${base64UrlEncode("hello")}:${base64UrlEncode("t")}`,
}),
);
expect(res.status).toBe(401);
});

test("Username password authenticatiom fails gracefully when password is wrong", async () => {
const cred = encode(`hello:t`);
const cred = base64UrlEncode(`hello:t`);
const res = await fetchUnauth(
createRequest("GET", `/v2/`, null, {
Authorization: `Basic ${cred}`,
Expand All @@ -166,7 +166,7 @@ describe("v2", () => {
});

test("Simple username password authenticatiom fails gracefully when password is wrong", async () => {
const cred = encode(`hello:t`);
const cred = base64UrlEncode(`hello:t`);
const res = await fetchUnauth(
createRequest("GET", `/v2/`, null, {
Authorization: `Basic ${cred}`,
Expand All @@ -176,7 +176,7 @@ describe("v2", () => {
});

test("Simple username password authenticatiom fails gracefully when username is wrong", async () => {
const cred = encode(`hell0:world`);
const cred = base64UrlEncode(`hell0:world`);
const res = await fetchUnauth(
createRequest("GET", `/v2/`, null, {
Authorization: `Basic ${cred}`,
Expand Down
2 changes: 1 addition & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"types": ["../worker-configuration.d.ts", "@cloudflare/vitest-pool-workers/types"],
"types": ["../worker-configuration.d.ts", "@cloudflare/vitest-pool-workers/types", "node"],
"module": "esnext",
"target": "esnext",
"lib": ["esnext"],
Expand Down
2 changes: 1 addition & 1 deletion worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable */
// Runtime types generated with workerd@1.20260722.1 2024-09-09 nodejs_compat
// Runtime types generated with workerd@1.20260730.1 2024-09-09 nodejs_compat
// Begin runtime types
/*! *****************************************************************************
Copyright (c) Cloudflare. All rights reserved.
Expand Down