Topic 024: Validating JSON Web Tokens (JWT) Using JSON Web Keys (JWKS) in Node.js Without jwks-rsa
jwks-rsa
jwks-rsa
?jwks-rsa
simplifies the process but comes with limitations, especially regarding additional security configurations. Direct implementation offers more control over HTTP requests, error handling, and fine-tuning client secret authentication.
{
"kid": "",
"typ": "",
"alg": "RS384"
}
{
"sub": "",
"aud": "",
"clientid": "",
"at": "",
"nbf": "",
"DisplayName": "",
"iss": "",
"exp": "",
"iat": "",
"jti": "",
"sid": ""
}
Key Type | Purpose | Common Formats |
---|---|---|
Public Key | Used to verify the signature | SPKI, PKCS #1, X.509 certificate, JWK format |
Private Key | Used to create the signature (stays secure) | PKCS #8, PKCS #1, JWK format |
header
and payload
in Base64 URL encoding, separated by a dot (.
).Below is a sample bash command with placeholder values.
# Step 1: Encode Header and Payload
header=$(echo -n '{"alg":"RS384","typ":"JWT"}' | base64 | tr '/+' '_-' | tr -d '=')
payload=$(echo -n '{"sub":"1234567890","name":"John Doe","iat":1516239022}' | base64 | tr '/+' '_-' | tr -d '=')
combined="$header.$payload"
# Step 2: Create Signature using Private Key
signature=$(echo -n "$combined" | openssl dgst -sha384 -sign private_key.pem | base64 | tr '/+' '_-' | tr -d '=')
# Step 3: Verify Signature using Public Key
echo -n "$combined.$signature" | openssl dgst -sha384 -verify public_key.pem -signature <(echo -n "$signature" | base64 -d)
---
### **JWKS Call - cURL Command**
```bash
curl --location --request GET 'URL' \
--header 'Accept: app/json' \
--header 'Content-type: app/json' \
--header 'client-id: abcde..z012..5' \
--header 'client-secret: 012..5abc...z'
{
"keys": [
{
"kty": "",
"e": "",
"use": "",
"kid": "",
"alg": "",
"n": "abc...(approx 256 characters)"
},....
]
}
const axios = require("axios");
const jwt = require("jsonwebtoken");
const jwkToPem = require("jwk-to-pem");
// JWKS URL and client credentials
const jwksUrl = "https://example.com/.well-known/jwks.json";
const clientId = "your-client-id";
const clientSecret = "your-client-secret";
async function getJWKS() {
try {
const response = await axios.get(jwksUrl, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"client-id": clientId,
"client-secret": clientSecret,
},
});
return response.data.keys;
} catch (error) {
throw new Error(`Failed to retrieve JWKS: ${error.message}`);
}
}
function validateToken(token) {
return getJWKS()
.then((keys) => {
const decodedHeader = jwt.decode(token, { complete: true }).header;
const key = keys.find((k) => k.kid === decodedHeader.kid);
if (!key) throw new Error("Matching key not found");
const pem = jwkToPem(key);
jwt.verify(token, pem, { algorithms: ["RS384"] }, (err, decoded) => {
if (err) throw new Error("Token verification failed");
console.log("Token is valid", decoded);
});
})
.catch((error) => console.error(error.message));
}
// Sample token for validation
const token = "your-jwt-token";
validateToken(token);
kty
: Key Type, e.g., RSA
, specifies the type of cryptographic algorithm used.e
: Exponent, a component of the RSA key.use
: Specifies the key’s purpose, commonly sig
(signature).kid
: Key ID, used to identify the key in JWKS.alg
: Algorithm used, e.g., RS384
.n
: Modulus, a component of the RSA key.sub
: Subject, representing the user or entity.aud
: Audience, the intended recipient(s) of the token.clientid
: Identifier for the client application.at
: Auth time, time at which the user was authenticated.nbf
: Not Before, time before which the token is invalid.DisplayName
: User-friendly name.iss
: Issuer, the entity that issued the token.exp
: Expiry, the time at which the token expires.iat
: Issued At, time the token was issued.jti
: JWT ID, a unique identifier for the token.sid
: Session ID, representing a session.typ
: Token type, usually JWT
.A typical JWKS request can be executed using the following curl
command, which includes the client ID and secret for authorization:
curl --location --request GET 'https://example.com/.well-known/jwks.json' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'client-id: your-client-id' \
--header 'client-secret: your-client-secret'
This command retrieves the JWKS data, including public keys used to validate the JWT signature.