H
Hono8mo ago
JavaDad

Typing Middleware JWT and others

Currently I am trying to type the c.get('jwtPayload') that is documented here. I have tried using the method described here in a global.d.ts file but this does not seem to take any affect, the jwtPayload get result is still typed as any. Currently I have removed the global.d.ts file and set types on the Hono instance instead so its more explicit. This is still not working for the jwtPayload (and other values I use in middelware like the oauth middleware):
// controller.ts
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();

app.use("/*", async (c, next) => {
const jwtMiddleware = jwt({
secret: c.env.JWT_SECRET,
});
return jwtMiddleware(c, next);
});

app.use("/*", async (c, next) => {
const jwtPayload = c.get("jwtPayload") as JWTPayload; // without cast is any
if (jwtPayload.role < Role.MANAGER) {
throw error(401, "Unauthorized");
}
});

app.get("/:id", async ({ get, env, req, json }) => {
const { id } = get("jwtPayload") as JWTPayload; // without cast is any
// More code below
// ...
});

// ===========================================

// auth.ts
app.post("/auth", async(c) => {
// Verification of google oauth omitted
// ...

// Once user is authed
const newUserToken = await sign({
id: user.id,
email: user.email,
name: user.name,
profilePicture: user.profilePicture,
role: user.role,
},
env.JWT_SECRET,
);

// Code that returns JWT to client to be included in Authorization header on subsequent requests
// ...
});
// controller.ts
const app = new Hono<{ Bindings: Bindings; Variables: Variables }>();

app.use("/*", async (c, next) => {
const jwtMiddleware = jwt({
secret: c.env.JWT_SECRET,
});
return jwtMiddleware(c, next);
});

app.use("/*", async (c, next) => {
const jwtPayload = c.get("jwtPayload") as JWTPayload; // without cast is any
if (jwtPayload.role < Role.MANAGER) {
throw error(401, "Unauthorized");
}
});

app.get("/:id", async ({ get, env, req, json }) => {
const { id } = get("jwtPayload") as JWTPayload; // without cast is any
// More code below
// ...
});

// ===========================================

// auth.ts
app.post("/auth", async(c) => {
// Verification of google oauth omitted
// ...

// Once user is authed
const newUserToken = await sign({
id: user.id,
email: user.email,
name: user.name,
profilePicture: user.profilePicture,
role: user.role,
},
env.JWT_SECRET,
);

// Code that returns JWT to client to be included in Authorization header on subsequent requests
// ...
});
13 Replies
JavaDad
JavaDadOP8mo ago
// ====================================

// types.ts

export type Bindings = {
DATABASE_URL: string;
DATABASE_AUTH_TOKEN: string;
GOOGLE_AUTH_CLIENT_ID: string;
GOOGLE_AUTH_CLIENT_SECRET: string;
SERVER_URL: string;
CLIENT_URL: string;
};

export type Variables = {
token: {
token: string;
expires_in: number;
};
"granted-scopes": string[];
"user-google": GoogleUserInfo;
};

export type JWTPayload = {
id: string;
email: string;
name: string;
profilePicture: string;
role: Role;
};
// ====================================

// types.ts

export type Bindings = {
DATABASE_URL: string;
DATABASE_AUTH_TOKEN: string;
GOOGLE_AUTH_CLIENT_ID: string;
GOOGLE_AUTH_CLIENT_SECRET: string;
SERVER_URL: string;
CLIENT_URL: string;
};

export type Variables = {
token: {
token: string;
expires_in: number;
};
"granted-scopes": string[];
"user-google": GoogleUserInfo;
};

export type JWTPayload = {
id: string;
email: string;
name: string;
profilePicture: string;
role: Role;
};
@Nico here is the thread, thanks!
Nico
Nico8mo ago
I see, to confirm it works fine it’s just the types that are throwing errors Let me test what you have put here and see for myself and I’ll get back to you
JavaDad
JavaDadOP8mo ago
The types are not throwing errors this way, they are just not being inferred. If I check the type of id from the jwtPayload its type is any
Nico
Nico8mo ago
And using as CustomType does not work correct?
JavaDad
JavaDadOP8mo ago
as CustomType will work because I am casting the result but this isn't exactly type safe because if the type changes or is unexpexted this won't catch it or give any errors. it also requires anywhere in the code that c.get() is used for the developer to know what type they should be expecting as there will be no inference
Nico
Nico8mo ago
I totally get that, I'm getting on my computer now to check Have you tried just adding the JWTPayload to the variables For example this is type safe for me
import { Hono } from 'hono';
import test from './middleware';

export type JWTPayload = {
id: string;
email: string;
name: string;
profilePicture: string;
};

type Variables = {
jwt: JWTPayload;
}

const api = new Hono<{ Variables: Variables }>();

api.get('/', test, (c) => {
const test = c.get('jwt')

return c.text('Hello, API!')
});

export default api;
import { Hono } from 'hono';
import test from './middleware';

export type JWTPayload = {
id: string;
email: string;
name: string;
profilePicture: string;
};

type Variables = {
jwt: JWTPayload;
}

const api = new Hono<{ Variables: Variables }>();

api.get('/', test, (c) => {
const test = c.get('jwt')

return c.text('Hello, API!')
});

export default api;
Nico
Nico8mo ago
No description
JavaDad
JavaDadOP8mo ago
Yes this works But the middelware JWT plugin sets a variable called jwtPayload not jwt https://hono.dev/middleware/builtin/jwt#usage Its only an issue when you are trying to type variables in existing middleware
Nico
Nico8mo ago
I see what you mean now
JavaDad
JavaDadOP8mo ago
The docs here seem to indicate there is a way to do this though - https://hono.dev/api/context#contextvariablemap
Nico
Nico8mo ago
That would be for adding, you won't be able to override them. I think for now casting is the only way to achieve this. I will bring up on GitHub for implementing this so maybe you can do jwt<MyPayload>({}) But it would have to be a way where breaking changes are not implemented
JavaDad
JavaDadOP8mo ago
Yeah I think this would make sense. And if you don’t pass the generic then it just stays as any, that shouldn’t be breaking?
Nico
Nico8mo ago
Or the other option is to get rid of the type it sets. I just testing and built that and it just requires people to set the type
Want results from more Discord servers?
Add your server