useJWT
JSON Web Tokens for client-side token decoding and validation.
Token Format: JWT with Ed25519 signatures for secure authentication
Import
import { useJWT } from '@heliorim/sdk-react';Type Signature
interface UseJWTReturn {
decoded: DecodedJWT | null;
isExpired: boolean;
expiration: Date | null;
isValid: boolean;
decodeToken: (token: string) => DecodedJWT | null;
isTokenExpired: (token: string) => boolean;
getTokenExpiration: (token: string) => Date | null;
getClaim: <T = any>(token: string, claimName: string) => T | null;
getSessionId: (token: string) => string | null;
getUserId: (token: string) => string | null;
getTenantId: (token: string) => string | null;
isValidToken: (token: string) => boolean;
clearError: () => void;
}
interface DecodedJWT {
header: {
alg: "EdDSA" | "ES256" | "ES384" | "ES512" | "RS256";
typ: "JWT";
kid?: string;
};
payload: {
sub: string; // Subject (User ID)
aud: string | string[]; // Audience
iss: string; // Issuer
exp: number; // Expiration
iat: number; // Issued at
sid?: string; // Session ID
tid?: string; // Tenant ID
[key: string]: any; // Custom claims
};
}Basic Usage
Never store auth tokens in localStorage or sessionStorage. Use httpOnly cookies set by the server. Pass tokens to useJWT from server-rendered loaders or context.
// Token from server loader (httpOnly cookie handled by server)
// Never store auth tokens in localStorage - XSS risk
function TokenAuthentication({ tokenFromLoader }: { tokenFromLoader: string | null }) {
const [token, setToken] = useState<string | null>(tokenFromLoader);
const {
decoded,
isExpired,
expiration,
isValid,
decodeToken,
isTokenExpired,
getTokenExpiration,
getSessionId,
getUserId
} = useJWT(token);
// Token comes from server context/loader, not localStorage
useEffect(() => {
if (tokenFromLoader) setToken(tokenFromLoader);
}, [tokenFromLoader]);
const validateToken = (tokenString: string) => {
try {
const decoded = decodeToken(tokenString);
if (!decoded) return false;
if (isTokenExpired(tokenString)) return false;
if (decoded.payload.aud !== 'https://api.example.com') return false;
return true;
} catch {
return false;
}
};
return (
<div>
<h3>Token Status</h3>
{token && decoded && (
<div>
<p>User ID: {getUserId(token)}</p>
<p>Session ID: {getSessionId(token)}</p>
<p>Expires: {expiration?.toISOString()}</p>
<p>Valid: {isValid ? 'Yes' : 'No'}</p>
</div>
)}
</div>
);
}Token Validation
Client-Side Decoding
function TokenValidation() {
const [token, setToken] = useState<string | null>(null);
const { decoded, isExpired, isValid, decodeToken, isTokenExpired } = useJWT(token);
const validateToken = (tokenString: string) => {
try {
// Decode token (unsafe - no signature verification)
const decoded = decodeToken(tokenString);
if (!decoded) {
console.log('Invalid token format');
return false;
}
// Check expiration
if (isTokenExpired(tokenString)) {
console.log('Token expired');
return false;
}
// Check required claims
if (!decoded.payload.sub) {
console.log('Missing subject claim');
return false;
}
return true;
} catch (error) {
console.error(
'Token validation failed:',
error instanceof Error ? error.message : "unknown error",
);
return false;
}
};
return (
<div>
<h3>Token Validation</h3>
<p>Client-side JWT validation without signature verification</p>
{token && decoded && (
<div className="token-info">
<h4>Token Information</h4>
<p>Valid: {isValid ? 'Yes' : 'No'}</p>
<p>Expired: {isExpired ? 'Yes' : 'No'}</p>
<p>User ID: {decoded.payload.sub}</p>
<p>Audience: {decoded.payload.aud}</p>
<p>Issuer: {decoded.payload.iss}</p>
</div>
)}
</div>
);
}Security Considerations
⚠️ Important
- Client-side JWT decoding does NOT verify signatures
- Always verify tokens server-side for security-critical operations
- Use this hook for UI state and client-side validation only
- Never trust client-side validation for authorization decisions
✓ Recommended
- Use for UI state management and user experience
- Check expiration for token refresh prompts
- Display user information from token claims
- Validate token format before sending to server