100 lines
3.3 KiB
TypeScript
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"};
|
|
}
|