LakeQL uses JWT-based authentication to identify the current user on each request. The getUser resolver extracts user information from the Authorization header and makes it available in the GraphQL context.
GetUserResolver type #
1
2
3
4
type GetUserResolver = (
req: Request
) => Promise<JWTPayload | null | undefined> | JWTPayload | null | undefined
Returning null or undefined means the request is unauthenticated. The JWTPayload type extends jose's JWTPayload with an additional userName field:
1
2
3
4
5
6
// Extends jose JWTPayload
interface JWTPayload {
userName: string
// ...standard JWT claims (iss, sub, aud, exp, etc.)
}
Default behavior #
The built-in getUser resolver supports mock authentication for local development. It does not perform real JWT verification — you must provide a custom resolver for production.
Mock authentication #
Set the following environment variables to enable mock auth:
1
2
3
AUTH_MOCK=true
AUTH_MOCK_TOKEN=my-dev-token
Then pass the token as the Authorization header and the username via x-username:
1
2
3
4
5
6
curl -X POST http://localhost:4000/graphql \
-H "Authorization: my-dev-token" \
-H "x-username: developer" \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}'
Custom JWT verification #
For production, provide a custom getUser resolver that verifies tokens using your identity provider:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import type { GetUserResolver } from "@lakeql/api/types"
import { jwtVerify, createRemoteJWKSet } from "jose"
const jwks = createRemoteJWKSet(
new URL("https://auth.example.com/.well-known/jwks.json")
)
export const getUser: GetUserResolver = async (req) => {
const authHeader = req.headers.get("authorization")
if (!authHeader?.startsWith("Bearer ")) {
return null
}
const token = authHeader.slice(7)
try {
const { payload } = await jwtVerify(token, jwks, {
issuer: "https://auth.example.com",
audience: "lakeql-api",
})
return {
...payload,
userName: payload.sub ?? "unknown",
}
} catch {
return null
}
}
Pass it to defineConfig in your src/config.ts:
1
2
3
4
5
6
7
8
9
10
11
12
import { defineConfig } from "@lakeql/api/config"
import { getUser } from "./auth"
import { allConfigs } from "./config-registry"
export const config = defineConfig({
allConfigs,
baseDir: import.meta.dirname,
getUser,
schemaPath: "./schemas",
})
The resolved user is available in every GraphQL resolver via context.currentUser.