Secure Data Exchange with JWTs and the @cross/jwt Library
by Pinta
3 min read
JSON Web Tokens (JWTs) provide a secure and standardized way to transmit information between parties. They are widely used for authentication, but their ability to carry arbitrary data makes them valuable for a variety of secure data exchange scenarios. If you work with Deno, Bun, or Node.js, managing JWT workflows across these different runtimes can introduce inconsistencies and potential security risks. Lets take a look at this runtime agnostic library to enable one dependency across all major runtimes.
@cross/jwt
Introducing @cross/jwt
, a cross-platform JWT library designed to bring secure, consistent, and easy-to-use JWT
handling to your projects. Source code available at GitHub.
Key Features for Robust Security
- Strong Cryptography:
@cross/jwt
supports a range of industry-standard signing algorithms, including HMAC, RSA, RSA-PSS, and ECDSA, ensuring the integrity and authenticity of your tokens. - Cross-Platform Confidence: Your security practices don't need to change between Deno, Bun, and Node.js
environments.
@cross/jwt
provides the same robust functionality everywhere. - Intuitive API: Security shouldn't be complex. The library's clear functions for signing, verifying, and managing keys make it easy to integrate JWTs correctly.
- Flexible Options: Customize token behavior, such as expiration and validation rules, using
JWTOptions
.
Installation
# For Deno
deno add @cross/jwt
# For Bun
bunx jsr add @cross/jwt
# For Node.js
npx jsr add @cross/jwt
API at a glance
See detailed documentation on https://jsr.io for the complete API, but here's a quick look with a simple HMAC Example:
import { signJWT, validateJWT } from "@cross/jwt";
// A base64-encoded secret.
const secret = "y69uNvF9lbHE2disEqeYCBYOUmzJvr75txhxbUL5W0k=";
// Generate and sign the JWT.
const token = await signJWT({ userId: 123 }, secret);
// Verify and validate it.
const data = await validateJWT(token, secret);
Real-World Use Cases
- Secure API Authentication: Implement bearer token authentication in your REST APIs, ensuring that only requests with valid JWTs are authorized.
- Microservice Communication: Use JWTs to securely pass context and authorization data between microservices, especially across different runtimes.
- Distributed Data Sharing: Transmit sensitive data (e.g., configuration, limited-access resources) between applications in a secure and verifiable format.
Oak example (Simplified)
import { Application } from "jsr:@oak/oak/application";
import { signJWT, validateJWT } from "@cross/jwt";
import type { JWTPayload } from "@cross/jwt";
const app = new Application();
// Replace with a secure secret
const secret = "y69uNvF9lbHE2disEqeYCBYOUmzJvr75txhxbUL5W0k=";
// ... Your routes and other logic ...
1. Login Route
app.use(async (ctx, next) => {
if (ctx.request.url.pathname === '/login' && ctx.request.method === 'POST') {
// ... your login validation logic ...
const payload: JWTPayload = {
userId: 123,
role: "user"
};
const jwt = await signJWT(payload, secret);
ctx.response.body = { token: jwt };
} else {
await next();
}
});
2. Authentication Middleware
const authMiddleware = async (ctx: any, next: any) => {
const authHeader = ctx.request.headers.get('Authorization');
if (!authHeader) {
ctx.response.status = 401; // Unauthorized
return;
}
const token = authHeader.split(' ')[1]; // Assuming 'Bearer token' format
try {
const payload = await validateJWT(token, secret) as JWTPayload;
// Attach user data to the context for downstream routes/logic
ctx.state.user = {
id: payload.userId,
role: payload.role
};
await next();
} catch (error) {
ctx.response.status = 401; // Unauthorized
}
};
3. Protected Route
app.use(authMiddleware);
app.use((ctx) => {
if (ctx.request.url.pathname === "/protected") {
// Access user info
const userId = ctx.state.user.id;
const userRole = ctx.state.user.role;
// ... logic using the authenticated user data ...
}
});
console.log("Server running on port 8000");
await app.listen({ port: 8000 });
Explanation
- Login: Simulates a login process. Upon success, a JWT containing user information is generated.
- Middleware: Intercepts requests, extracts the JWT from the 'Authorization' header, and validates it. Unauthorized requests get a 401 response.
- Protected Route: Routes using the
authMiddleware
now require a valid JWT to access.
Important Notes:
- Store JWTs securely on the client: Typically in cookies (with HttpOnly flag).
- Secret Management: Use environment variables and proper secret handling.
- Error Handling: Implement more detailed error responses in production.