142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
import jwt, { SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
import config from '../config';
|
|
import logger from './logger';
|
|
import type { JwtPayload } from '../types';
|
|
|
|
interface TokenPayload {
|
|
userId?: string;
|
|
email?: string;
|
|
roleId?: string;
|
|
roleName?: string;
|
|
id?: string;
|
|
role?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
/**
|
|
* Generate an access token
|
|
* @param payload - Data to encode in the token
|
|
* @returns Signed JWT access token
|
|
*/
|
|
export const generateAccessToken = (payload: TokenPayload): string => {
|
|
const options: SignOptions = {
|
|
expiresIn: config.jwt.accessTokenExpiresIn as SignOptions['expiresIn'],
|
|
algorithm: 'HS256',
|
|
};
|
|
|
|
try {
|
|
const token = jwt.sign(payload, config.jwt.accessTokenSecret, options);
|
|
return token;
|
|
} catch (error) {
|
|
logger.error('Error generating access token', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
throw new Error('Failed to generate access token');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate a refresh token
|
|
* @param payload - Data to encode in the token
|
|
* @returns Signed JWT refresh token
|
|
*/
|
|
export const generateRefreshToken = (payload: TokenPayload): string => {
|
|
const options: SignOptions = {
|
|
expiresIn: config.jwt.refreshTokenExpiresIn as SignOptions['expiresIn'],
|
|
algorithm: 'HS256',
|
|
};
|
|
|
|
try {
|
|
const token = jwt.sign(payload, config.jwt.refreshTokenSecret, options);
|
|
return token;
|
|
} catch (error) {
|
|
logger.error('Error generating refresh token', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
throw new Error('Failed to generate refresh token');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Verify an access token
|
|
* @param token - JWT access token to verify
|
|
* @returns Decoded payload if valid, null if invalid or expired
|
|
*/
|
|
export const verifyAccessToken = (token: string): any => {
|
|
const options: VerifyOptions = {
|
|
algorithms: ['HS256'],
|
|
};
|
|
|
|
try {
|
|
const decoded = jwt.verify(
|
|
token,
|
|
config.jwt.accessTokenSecret,
|
|
options
|
|
);
|
|
return decoded;
|
|
} catch (error) {
|
|
if (error instanceof jwt.TokenExpiredError) {
|
|
logger.debug('Access token expired');
|
|
} else if (error instanceof jwt.JsonWebTokenError) {
|
|
logger.debug('Invalid access token', {
|
|
error: error.message,
|
|
});
|
|
} else {
|
|
logger.error('Error verifying access token', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Verify a refresh token
|
|
* @param token - JWT refresh token to verify
|
|
* @returns Decoded payload if valid, null if invalid or expired
|
|
*/
|
|
export const verifyRefreshToken = (token: string): any => {
|
|
const options: VerifyOptions = {
|
|
algorithms: ['HS256'],
|
|
};
|
|
|
|
try {
|
|
const decoded = jwt.verify(
|
|
token,
|
|
config.jwt.refreshTokenSecret,
|
|
options
|
|
);
|
|
return decoded;
|
|
} catch (error) {
|
|
if (error instanceof jwt.TokenExpiredError) {
|
|
logger.debug('Refresh token expired');
|
|
} else if (error instanceof jwt.JsonWebTokenError) {
|
|
logger.debug('Invalid refresh token', {
|
|
error: error.message,
|
|
});
|
|
} else {
|
|
logger.error('Error verifying refresh token', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Decode a token without verification (for debugging)
|
|
* @param token - JWT token to decode
|
|
* @returns Decoded payload or null
|
|
*/
|
|
export const decodeToken = (token: string): any => {
|
|
try {
|
|
const decoded = jwt.decode(token);
|
|
return decoded;
|
|
} catch (error) {
|
|
logger.error('Error decoding token', {
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
});
|
|
return null;
|
|
}
|
|
};
|