I am using Keycloak and want to verify the users token before allowing them to access the server.
For this, I am using the following middlware:
require('dotenv').config();
import axios from "axios";
import { NextFunction, Request, Response } from "express";
import jwt, { JwtPayload } from 'jsonwebtoken';
async function getKeycloakPublicKey(): Promise<string> {
try {
const response = await axios.get(`${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/certs`);
const key = response.data.keys[0].x5c[0];
return `-----BEGIN CERTIFICATE-----n${key}n-----END CERTIFICATE-----n`;
} catch (error) {
console.error("Server | getKeycloakPublicKey | Error: ", error)
throw new Error('Server | getKeycloakPublicKey | Unable to fetch Keycloak public key');
}
}
async function verifyToken(token: string) {
try {
const publicKey = await getKeycloakPublicKey();
console.log(token, publicKey)
const decodedToken = jwt.verify(token, publicKey);
return decodedToken;
} catch (error) {
console.error('Server | verifyToken | Token verification failed:', error);
throw new Error('Token verification failed');
}
}
const checkToken = async (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
if (authHeader) {
const token = authHeader.split(' ')[1];
try {
await verifyToken(token);
next();
} catch (error) {
res.status(401).json({ message: "Access Denied: Invalid Token" });
}
} else {
console.log("server | checkToken | No token found");
res.status(401).json({ message: "Access Denied: No Token Provided" });
}
}
However, it is throwing an invalid JsonWebTokenError: invalid signature
error when it attempts to verify the my token:
Server | verifyToken | Token verification failed: JsonWebTokenError: invalid signature
My public key is in the correct format, and the token is valid because I pull it directly from the keycloak client:
const client = new Keycloak({
url: import.meta.env.VITE_KEYCLOAK_URL,
realm: import.meta.env.VITE_KEYCLOAK_REALM,
clientId: import.meta.env.VITE_KEYCLOAK_CLIENTID
})
export const useAuth = (setToken: (token: string | undefined) => any) => {
const hasLoginOccurred = useRef(false)
const [isLogin, setLogin] = useState(false)
useEffect(() => {
if (!hasLoginOccurred.current) {
client.init({ onLoad: 'login-required' }).then((res) => {
if (res && client.token) {
console.log('useAuth | useEffect | User has logged in: ' + res);
setToken(client.token);
hasLoginOccurred.current = true;
setLogin(true)
} else {
console.error('useAuth | useEffect | User has not logged in: ' + res);
}
}).catch((error) => console.error('useAuth | useEffect | Login error: ', error));
}
}, [])
return isLogin;
}
Why is it throwing an error?