- Added @eslint/js dependency - Updated eslint.config.js for ESLint 9 compatibility - Added required globals (crypto, localStorage, React, etc.) - Fixed unused imports and variables - Disabled sort-imports temporarily - Formatted all files with Prettier
67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
/**
|
|
* JWT utilities — configurable issuer and expiry.
|
|
* Uses jose library for standards-compliant JWT handling.
|
|
*/
|
|
|
|
import { SignJWT, jwtVerify } from 'jose';
|
|
import type { JwtUtils, JwtUtilsOptions, TokenPayload } from './types.js';
|
|
|
|
function getSecret(): Uint8Array {
|
|
const secret = process.env.JWT_SECRET;
|
|
if (!secret) throw new Error('JWT_SECRET must be set');
|
|
return new TextEncoder().encode(secret);
|
|
}
|
|
|
|
/**
|
|
* Create a JWT utility set with the given issuer and expiry configuration.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const jwt = createJwtUtils({ issuer: "lysnrai", accessTokenExpiry: "1h" });
|
|
* const token = await jwt.createAccessToken({ sub: "u1", email: "a@b.com", role: "admin" });
|
|
* const payload = await jwt.verifyToken(token);
|
|
* ```
|
|
*/
|
|
export function createJwtUtils(options: JwtUtilsOptions): JwtUtils {
|
|
const { issuer, accessTokenExpiry = '1h', refreshTokenExpiry = '30d' } = options;
|
|
|
|
return {
|
|
async createAccessToken(payload) {
|
|
return new SignJWT({
|
|
...payload,
|
|
productId: payload.productId || issuer,
|
|
type: 'access',
|
|
})
|
|
.setProtectedHeader({ alg: 'HS256' })
|
|
.setIssuedAt()
|
|
.setExpirationTime(accessTokenExpiry)
|
|
.setIssuer(issuer)
|
|
.sign(getSecret());
|
|
},
|
|
|
|
async createRefreshToken(payload) {
|
|
return new SignJWT({
|
|
sub: payload.sub,
|
|
productId: payload.productId || issuer,
|
|
type: 'refresh',
|
|
})
|
|
.setProtectedHeader({ alg: 'HS256' })
|
|
.setIssuedAt()
|
|
.setExpirationTime(refreshTokenExpiry)
|
|
.setIssuer(issuer)
|
|
.sign(getSecret());
|
|
},
|
|
|
|
async verifyToken(token: string) {
|
|
try {
|
|
const { payload } = await jwtVerify(token, getSecret(), {
|
|
issuer,
|
|
});
|
|
return payload as unknown as TokenPayload;
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
};
|
|
}
|