Files
lstV2/packages/lst-auth/index.ts

100 lines
3.3 KiB
TypeScript

import {Hono, type MiddlewareHandler} from "hono";
import {basicAuth} from "hono/basic-auth";
import {sign, verify} from "jsonwebtoken";
const JWT_SECRET = "your-secret-key";
const EXPIRATION_TIME = "1h"; // Token expires in 1 minute
const REFRESH_THRESHOLD = 30; // Refresh token if it has less than 30 seconds left
const ACCESS_EXPIRATION = "1h"; // 1 minute for access tokens
const REFRESH_EXPIRATION = "7d"; // 7 days for refresh tokens
const fakeUsers = [
{id: 1, username: "admin", password: "password123"},
{id: 2, username: "user", password: "password123"},
{id: 3, username: "user2", password: "password123"},
];
// Hardcoded user credentials (for demonstration purposes)
const users = [{username: "admin", password: "password123"}];
// Middleware to check authentication
export const lstVerifyAuth = basicAuth({
verifyUser: (username, password) => {
const user = users.find((u) => u.username === username && u.password === password);
return !!user; // Return true if user exists, otherwise false
},
});
/**
* Authenticate a user and return a JWT.
*/
export function login(username: string, password: string): {token: string; user: {id: number; username: string}} {
const user = fakeUsers.find((u) => u.username === username && u.password === password);
if (!user) {
throw new Error("Invalid credentials");
}
// Create a JWT
const token = sign({userId: user?.id, username: user?.username}, JWT_SECRET, {expiresIn: EXPIRATION_TIME});
return {token, user: {id: user?.id, username: user.username}};
}
/**
* Verify a JWT and return the decoded payload.
*/
export function verifyToken(token: string): {userId: number} {
try {
const payload = verify(token, JWT_SECRET) as {userId: number};
return payload;
} catch (err) {
throw new Error("Invalid token");
}
}
export const authMiddleware: MiddlewareHandler = async (c, next) => {
const authHeader = c.req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return c.json({error: "Unauthorized"}, 401);
}
const token = authHeader.split(" ")[1];
try {
const decoded = verify(token, JWT_SECRET, {ignoreExpiration: false}) as {userId: number; exp: number};
const currentTime = Math.floor(Date.now() / 1000); // Get current timestamp
const timeLeft = decoded.exp - currentTime;
// If the token has less than REFRESH_THRESHOLD seconds left, refresh it
let newToken = null;
if (timeLeft < REFRESH_THRESHOLD) {
newToken = sign({userId: decoded.userId}, JWT_SECRET, {expiresIn: EXPIRATION_TIME});
c.res.headers.set("Authorization", `Bearer ${newToken}`);
}
c.set("user", {id: decoded.userId});
await next();
// If a new token was generated, send it in response headers
if (newToken) {
console.log("token was refreshed");
c.res.headers.set("X-Refreshed-Token", newToken);
}
} catch (err) {
return c.json({error: "Invalid token"}, 401);
}
};
/**
* Logout (clear the token).
* This is a placeholder function since JWTs are stateless.
* In a real app, you might want to implement token blacklisting.
*/
export function logout(): {message: string} {
return {message: "Logout successful"};
}