Skip to content
4 changes: 3 additions & 1 deletion jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"./adapters/hono": "./src/adapters/hono/index.ts",
"./adapters/h3": "./src/adapters/h3/index.ts",
"./adapters/elysia": "./src/adapters/elysia/index.ts",
"./adapters/nestjs": "./src/adapters/nestjs/index.ts"
"./adapters/nestjs": "./src/adapters/nestjs/index.ts",
"./middleware/postgres": "./src/middleware/postgres/index.ts",
"./middleware/claims": "./src/middleware/claims/index.ts"
},
"publish": {
"include": [
Expand Down
28 changes: 27 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@
"default": "./dist/adapters/nestjs/index.cjs"
}
},
"./middleware/postgres": {
"import": {
"types": "./dist/middleware/postgres/index.d.mts",
"default": "./dist/middleware/postgres/index.mjs"
},
"require": {
"types": "./dist/middleware/postgres/index.d.cts",
"default": "./dist/middleware/postgres/index.cjs"
}
},
"./middleware/claims": {
"import": {
"types": "./dist/middleware/claims/index.d.mts",
"default": "./dist/middleware/claims/index.mjs"
},
"require": {
"types": "./dist/middleware/claims/index.d.cts",
"default": "./dist/middleware/claims/index.cjs"
}
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
Expand Down Expand Up @@ -128,7 +148,8 @@
"@supabase/supabase-js": "^2.0.0",
"elysia": "^1.4.0",
"h3": "^2.0.0",
"hono": "^4.0.0"
"hono": "^4.0.0",
"pg": "^8.0.0"
},
"peerDependenciesMeta": {
"@nestjs/common": {
Expand All @@ -142,6 +163,9 @@
},
"elysia": {
"optional": true
},
"pg": {
"optional": true
}
},
"devDependencies": {
Expand All @@ -156,6 +180,7 @@
"@supabase/supabase-js": "^2.105.4",
"@swc/core": "^1.15.33",
"@types/node": "^26.0.1",
"@types/pg": "^8.11.0",
"@types/supertest": "^7.2.0",
"elysia": "^1.4.0",
"eslint": "^10.0.2",
Expand All @@ -176,6 +201,7 @@
"vitest": "^4.1.0"
},
"dependencies": {
"@supabase/middleware": "https://pkg.pr.new/supabase/middleware/@supabase/middleware@0641674",
"jose": "^6.2.0"
}
}
133 changes: 133 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ minimumReleaseAgeExclude:
- '@esbuild/*'
blockExoticSubdeps: true
allowBuilds:
'@supabase/middleware': true
'@nestjs/core': false
'@swc/core': false
esbuild: false
Expand Down
64 changes: 64 additions & 0 deletions src/middleware/claims/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { describe, expect, it } from 'vitest'

import { withClaims } from './index.js'

function base64url(obj: unknown): string {
return Buffer.from(JSON.stringify(obj))
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '')
}

function tokenFor(claims: Record<string, unknown>): string {
return `header.${base64url(claims)}.sig`
}

describe('withClaims', () => {
it('decodes the Bearer token payload into ctx.jwtClaims', async () => {
let seen: unknown
const handler = withClaims(async (_req, ctx) => {
seen = ctx.jwtClaims
return Response.json({ ok: true })
})

// Called bare, the way a runtime invokes a fetch entry — no prerequisites.
await handler(
new Request('http://localhost', {
headers: {
Authorization: `Bearer ${tokenFor({ sub: 'u1', role: 'authenticated' })}`,
},
}),
)

expect(seen).toEqual({ sub: 'u1', role: 'authenticated' })
})

it('contributes null when no Authorization header is present', async () => {
let seen: unknown = 'unset'
const handler = withClaims(async (_req, ctx) => {
seen = ctx.jwtClaims
return Response.json({ ok: true })
})

await handler(new Request('http://localhost'))

expect(seen).toBeNull()
})

it('contributes null for a malformed token', async () => {
let seen: unknown = 'unset'
const handler = withClaims(async (_req, ctx) => {
seen = ctx.jwtClaims
return Response.json({ ok: true })
})

await handler(
new Request('http://localhost', {
headers: { Authorization: 'Bearer not-a-jwt' },
}),
)

expect(seen).toBeNull()
})
})
Loading
Loading