Skip to content

Commit c76a194

Browse files
tomaspozoclaude
andcommitted
refactor: rename user to userClaims and UserIdentity to UserClaims
Sets the right expectation that ctx.userClaims is JWT-derived identity, not the full Supabase User object. JSDoc points to supabase.auth.getUser() for the complete User. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6cb1186 commit c76a194

7 files changed

Lines changed: 40 additions & 19 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Deno.serve(
4040
withSupabase({ allow: 'user' }, async (req, ctx) => {
4141
// ctx.supabase — RLS-scoped to the authenticated user
4242
// ctx.supabaseAdmin — bypasses RLS (service role)
43-
// ctx.user — user identity (id, email, role)
43+
// ctx.userClaims — user identity from JWT (id, email, role)
4444
// ctx.claims — JWT claims
4545
// ctx.authType — which auth mode matched
4646

@@ -76,7 +76,7 @@ Deno.serve(
7676
```ts
7777
Deno.serve(
7878
withSupabase({ allow: ['user', 'secret'] }, async (req, ctx) => {
79-
const userId = ctx.user?.id ?? (await req.json()).user_id
79+
const userId = ctx.userClaims?.id ?? (await req.json()).user_id
8080
const { data } = await ctx.supabaseAdmin
8181
.from('reports')
8282
.select()
@@ -107,7 +107,7 @@ Every handler receives a `SupabaseContext`:
107107
interface SupabaseContext {
108108
supabase: SupabaseClient // RLS-scoped (user or anon depending on auth)
109109
supabaseAdmin: SupabaseClient // Bypasses RLS
110-
user: UserIdentity | null // Present when auth is JWT
110+
userClaims: UserClaims | null // JWT-derived identity (for full User, call supabase.auth.getUser())
111111
claims: JWTClaims | null // Present when auth is JWT
112112
authType: Allow // Which auth mode matched
113113
}

src/core/verify-credentials.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ describe('verifyCredentials', () => {
253253
})
254254
expect(result.error).toBeNull()
255255
expect(result.data!.authType).toBe('user')
256-
expect(result.data!.user!.id).toBe('user-123')
257-
expect(result.data!.user!.email).toBe('test@example.com')
256+
expect(result.data!.userClaims!.id).toBe('user-123')
257+
expect(result.data!.userClaims!.email).toBe('test@example.com')
258258
expect(result.data!.claims!.sub).toBe('user-123')
259259
expect(result.data!.token).toBe(validToken)
260260
})

src/core/verify-credentials.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
Credentials,
99
JWTClaims,
1010
SupabaseEnv,
11-
UserIdentity,
11+
UserClaims,
1212
} from '../types.js'
1313
import { timingSafeEqual } from './utils/timing-safe-equal.js'
1414
import { resolveEnv } from './resolve-env.js'
@@ -37,7 +37,7 @@ function parseAllowMode(mode: AllowWithKey): {
3737
return { base, keyName }
3838
}
3939

40-
function claimsToUser(claims: JWTClaims): UserIdentity {
40+
function claimsToUserClaims(claims: JWTClaims): UserClaims {
4141
return {
4242
id: claims.sub,
4343
role: claims.role,
@@ -56,7 +56,7 @@ async function tryMode(
5656

5757
switch (base) {
5858
case 'always':
59-
return { authType: 'always', token: null, user: null, claims: null }
59+
return { authType: 'always', token: null, userClaims: null, claims: null }
6060

6161
case 'public': {
6262
if (!credentials.apikey) return null
@@ -65,14 +65,24 @@ async function tryMode(
6565
if (keyName === '*') {
6666
for (const value of Object.values(keys)) {
6767
if (await timingSafeEqual(credentials.apikey, value)) {
68-
return { authType: 'public', token: null, user: null, claims: null }
68+
return {
69+
authType: 'public',
70+
token: null,
71+
userClaims: null,
72+
claims: null,
73+
}
6974
}
7075
}
7176
} else {
7277
const name = keyName ?? 'default'
7378
const value = keys[name]
7479
if (value && (await timingSafeEqual(credentials.apikey, value))) {
75-
return { authType: 'public', token: null, user: null, claims: null }
80+
return {
81+
authType: 'public',
82+
token: null,
83+
userClaims: null,
84+
claims: null,
85+
}
7686
}
7787
}
7888
return null
@@ -85,14 +95,24 @@ async function tryMode(
8595
if (keyName === '*') {
8696
for (const value of Object.values(keys)) {
8797
if (await timingSafeEqual(credentials.apikey, value)) {
88-
return { authType: 'secret', token: null, user: null, claims: null }
98+
return {
99+
authType: 'secret',
100+
token: null,
101+
userClaims: null,
102+
claims: null,
103+
}
89104
}
90105
}
91106
} else {
92107
const name = keyName ?? 'default'
93108
const value = keys[name]
94109
if (value && (await timingSafeEqual(credentials.apikey, value))) {
95-
return { authType: 'secret', token: null, user: null, claims: null }
110+
return {
111+
authType: 'secret',
112+
token: null,
113+
userClaims: null,
114+
claims: null,
115+
}
96116
}
97117
}
98118
return null
@@ -111,7 +131,7 @@ async function tryMode(
111131
return {
112132
authType: 'user',
113133
token: credentials.token,
114-
user: claimsToUser(claims),
134+
userClaims: claimsToUserClaims(claims),
115135
claims,
116136
}
117137
} catch {

src/create-supabase-context.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('createSupabaseContext', () => {
3131
env: baseEnv,
3232
})
3333

34-
expect(result.data!.user).toBeNull()
34+
expect(result.data!.userClaims).toBeNull()
3535
expect(result.data!.claims).toBeNull()
3636
})
3737

src/create-supabase-context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function createSupabaseContext(
2828
data: {
2929
supabase,
3030
supabaseAdmin,
31-
user: auth.user,
31+
userClaims: auth.userClaims,
3232
claims: auth.claims,
3333
authType: auth.authType,
3434
},

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type {
1414
JWTClaims,
1515
SupabaseContext,
1616
SupabaseEnv,
17-
UserIdentity,
17+
UserClaims,
1818
WithSupabaseConfig,
1919
} from './types.js'
2020
export { AuthError, EnvError } from './errors.js'

src/types.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface Credentials {
2222
export interface AuthResult {
2323
authType: Allow
2424
token: string | null
25-
user: UserIdentity | null
25+
userClaims: UserClaims | null
2626
claims: JWTClaims | null
2727
}
2828

@@ -39,7 +39,7 @@ export interface JWTClaims {
3939
[key: string]: unknown
4040
}
4141

42-
export interface UserIdentity {
42+
export interface UserClaims {
4343
id: string
4444
role?: string
4545
email?: string
@@ -56,7 +56,8 @@ export interface WithSupabaseConfig {
5656
export interface SupabaseContext {
5757
supabase: SupabaseClient
5858
supabaseAdmin: SupabaseClient
59-
user: UserIdentity | null
59+
/** JWT-derived identity. For the full Supabase User object, call `supabase.auth.getUser()`. */
60+
userClaims: UserClaims | null
6061
claims: JWTClaims | null
6162
authType: Allow
6263
}

0 commit comments

Comments
 (0)