Duende (Identity Server), Certificates and Postman. What could go wrong? Signed JWT Requests the "easy" way. Part 02
When requesting an OAuth Bearer Token to be used with a Machine-to-Machine communication (typically to interact with an API), you can use client credentials grant using only client id and client secret, however this practice is not recommended.
Enter signed JWT request.
According to Wikipedia
"JSON Web Token is a proposed Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims. The tokens are signed either using a private secret or a public/private key."
It consists of 3 parts:
const token = base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature)
Where the header will have the cryptographic algorithm (RS256, HMAC, JWA, etc.)
Payload will consist of the claims (in our case the parameters to create a request)
And finally, the signature, where it consists of the header + the payload and signed with a secret.
We will be using Postman in order to create requests for tokens.
On the previous post, I showed you how to obtain your own Duende server for testing. We will be using that server as well.
Just a reminder of the post.
Postman Environments
We will start by creating a new environment and global variables

The first global variable will be adding the jsrasign-js library.
This library is responsible of signing the header and payload and encoding the JWT.
You can get the script from
And copy the contents of the file jsrsasign-all-min.js into the current value.
Next we will need to create the environment variables to be used on the collection.

Don't worry, I will provide the environment and collection, but it is good to know the purpose of each variable.
- idpServer. The URL from where you will be obtaining the tokens. If using Duende, it will look something like https://yourduendedomain.com/connect/token
- clientId. On our previous post we created a client id, in the example will be using myclient.
- privateKey. You will need to use the cert reader to extract the private key from the certificate, refer to the post Certificate Reader (darthseldon.net) and copy the private rsa to the value of the variable.
- iss and sub. They match the client id value.
- aud. Will be the intended audience and it will be the same value as idpServer https://yourduendedomain.com/connect/token
- clientAssertionType. The value will be urn:ietf:params:oauth:client-assertion-type:jwt-bearer, meaning that will be using a client assertion (signed request) to obtain a bearer token.
- alg. The cryptographic algorithm to use, in our case will be RS256.
- scope. This is an optional field (controlled by the IDS) and it represents the authorization scope you are requesting, in our example is api.readonly; if omitted the IDS should return the default scope.
- clientAssertion. This is a calculated field to be sent on the request, no need to update this value.
- bearerToken. The result from the request, will be saving the result in this variable.
Postman Collection
Now for the request.

It will be a POST request with the following body in url encoded form.
- client_assertion_type is urn:ietf:params:oauth:client-assertion-type:jwt-bearer, but we will pass it as a variable.
- client_id I think is self explanatory.
- grant_type determines we are using client_credentials.
- client_assertion is the calculated value from the header + payload + signature.
- scope is the requested scope and can be optional.
Finally, we will include the pre and post request scripts. Their responsibility is to read the variables and build and sign the JWT.
Pre-request script
//import jwt signing library
var navigator = {};
var window = {};
eval(pm.globals.get("jsrsasign-js"));
//Obtain environment variables
var idp = pm.environment.get('idpServer') || '';
var iss = pm.environment.get('iss') || '';
var sub = pm.environment.get('sub') || '';
var aud = pm.environment.get('aud') || '';
var alg = pm.environment.get('alg') || '';
var privateKey = pm.environment.get('privateKey') || '';
// Set headers for JWT
var header = {
'typ': 'JWT',
'alg': `${alg}`,
};
// Prepare timestamp in seconds
var currentTimestamp = Math.floor(Date.now() / 1000)
// Set body data
var data = {
'iss': `${iss}`,
'sub': `${sub}`,
'aud': `${aud}`,
'exp': currentTimestamp + 300, // expiry time is 300 seconds from time of creation
'jti': `${uuidv4()}`
}
data = addIAT(data);
var keyObject = KEYUTIL.getKey(privateKey);
var jwtSigned = KJUR.jws.JWS.sign(header.alg, JSON.stringify(header), JSON.stringify(data), keyObject);
console.log('Signed and encoded JWT', jwtSigned);
pm.environment.set('clientAssertion', jwtSigned);
//create guid
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
//create iat
function addIAT(request) {
var iat = Math.floor(Date.now() / 1000) + 257;
data.iat = iat;
return data;
}
You will see we are importing the signing library and 2 functions to create a GUID and the Time To Live (TTL) for the request.
At the last step it will set the environment variable with the signed JWT into the clientAssertion variable.
Post-request script
const jsonResponse = pm.response.json();
pm.environment.set("bearerToken", jsonResponse.access_token);
Last stage is on the post-request script is to gather from the response the bearer token.


You can download the environment and collection from these links
Next post. C# to sign tokens.
Happy Coding!!!