JWT Assertions
What is a JWT assertion?
A JWT assertion is a security token used to make verifiable claims about a subject. It is cryptographically signed to ensure authenticity and integrity.
Why should you care?
JWT assertions play a critical role in the process of obtaining a three-legged access token for a server-to-server application.
Once you have an SSA (Secure Service Account) for a server-to-server application:
- Request a private key for the SSA using the Create Key operation.
- Use the returned private key to generate a JWT assertion for that SSA. This procedure is described in the section How do you generate a JWT assertion? below.
- Use the Exchange JWT Assertion for Token operation to exchange the generated JWT assertion for a three-legged access token. This access token provides secure access to the resources the server-to-server application needs to access.

How do you generate a JWT assertion?
To generate a JWT assertion, you need to sign a combination of headers and claims. You will use a private key that you obtained for the SSA to perform this signing. Each header and claim serve a unique purpose as listed below:
Headers:
kid
(Key ID): The private key ID returned by the Create Key operation.alg
(Algorithm): The signing algorithm used to sign the assertion. For this flow, it will always beRS256
.
Claims:
iss
(Issuer): Client ID of the application generating the assertion.Example:
JlO9TA1zjfJQOGXpJmq9JHJSI0D4UkQ4
sub
(Subject): Service Account ID of the SSA that owns the key.Example:
Z752CT5MKW2S9N7E
aud
(Audience): Always set to https://developer.api.autodesk.com/authentication/v2/token.exp
(Expiration): Time (in Epoch) when the assertion expires. Must be 0-5 minutes in the future.Example:
1710907100
scope
: Requested scopes as an array of strings.Example:
["user:read", "data:read"]
To manually generate a JWT assertion for testing:
- Open https://jwt.io/
- Paste the example assertion shown below, in the Encoded box. The
headers and the claims are displayed in the Decoded box.
eyJraWQiOiI1ZGU5OTNmNC02MmIwLTQ5NWEtYTQzYS1iOTg5NmQ2ZTk1ODIiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJKbE85VEExempmSlFPR1hwSm1xOUpISlNJMEQ0VWtRNCIsInN1YiI6Ilo3NTJDVDVNS1cyUzlON0UiLCJhdWQiOiJodHRwczovL2RldmVsb3Blci5hcGkuYXV0b2Rlc2suY29tL2F1dGhlbnRpY2F0aW9uL3YyL3Rva2VuIiwiZXhwIjoxNzEwOTA3MTAwLCJzY29wZSI6WyJ1c2VyOnJlYWQiLCJkYXRhOnJlYWQiXX0.p9RNN28G38VCczbO6JgkTRfcb079_xDcDm2i4-HUqUdSZKre6jllx1IWhmwG0cm79EhC3OjJ0_zoPfKj2sP4lrPm27iXzd6x_SfD4LKS4zAJI2IERXjU05T9zWU4bfZWk0EinBysV0stvvEtZIBHczD_uAXCB5YLvyBX-O_kXqqkigNQupG9RsmE4GOjhG7pGLL_tdDYXkN46JAw-vMyXlhsdOntuZCjDOpcD4hsIueKwaqm6aLBKUTE1Htwpk0MUYmvl7AF03XDgWjhwRnJVOk_MkdF44bjSCAmsQ5uTYbWipUJjDqUy38b4xiRRRB0_qsg_kZ-DBOAFzUtYN6ilA
- Paste your private key in the area reserved for the private key, within the
Verify Signature box. Leave the area reserved for the public key empty.
Note: If your private key still contains
\n
, you must replace it with newlines.Tip: In Terminal, execute the following command to convert all\n
into actual newline characters.``echo -e "<your_private_key>"``
- Change claims as needed. The assertion displays in the Encoded box.
To programmatically generate a JWT assertion:
The following code snippets demonstrate how to generate a JWT assertion in various programming languages. You can use any of these snippets to generate a JWT assertion. The code snippets are provided in JavaScript, C#, Python, and Go. You can choose the one that best suits your needs.
Notes
- The code snippets assume you have already obtained the private key and key ID from the Create Key operation.
- Each snippet uses a different library:
- JavaScript:
jsonwebtoken
- C#:
System.IdentityModel.Tokens.Jwt
- Python:
PyJWT
- Go:
github.com/golang-jwt/jwt/v4
Make sure to install the required libraries before running the code.
- JavaScript:
const jwt = require('jsonwebtoken');
// or
import jwt from 'jsonwebtoken';
// Choose whichever way of importing that suits your needs
// GenerateAssertionJWT generates the encoded JWT token in the form of a string
function generateAssertionJWT(keyID, privateKey) {
return jwt.sign(
{
// Fill your claims here
iss: "test-client-id"
},
privateKey,
{
algorithm: "RS256",
header: { kid: keyID } // Fill headers here
}
);
}
// Usage example
try {
let token = generateAssertionJWT('<your key id>', '<your private key (string)>');
console.log("Generated Token:", token);
} catch (error) {
console.error("Error generating token:", error);
}
// Token can be used in `assertion` field when requesting /token endpoint
static string GenerateJwtAssertion(
string keyId,
string privateKeyPem,
string clientId,
string ssa_id,
string[] scope)
{
// Create RSA from the PEM-formatted private key
using RSA rsa = RSA.Create();
privateKeyPem = privateKeyPem.Replace("\r\n", "\n");
rsa.ImportFromPem(privateKeyPem.ToCharArray());
var securityKey = new RsaSecurityKey(rsa)
{
KeyId = keyId
};
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
// Build JWT claims
var claims = new List<Claim>
{
new Claim("iss", clientId),
new Claim("sub", ssa_id),
new Claim("aud", "https://developer.api.autodesk.com/authentication/v2/token"),
};
foreach (string scopeStr in scope)
{
claims.Add(new Claim("scope", scopeStr));
}
// Create the token with a 5-minute expiration
var jwtToken = new JwtSecurityToken(
claims: claims,
// notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddSeconds(300),
signingCredentials: signingCredentials
);
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(jwtToken);
}
import jwt
# GenerateAssertionJWT generates the encoded JWT token as a string
def generate_assertion_jwt(key_id, private_key):
claims = {
'iss': 'test-client-id',
# Fill your claims here
}
headers = {
'kid': key_id,
# Fill headers here
}
token = jwt.encode(claims, private_key, algorithm='RS256', headers=headers)
return token
# Usage example
try:
token = generate_assertion_jwt('<your key id>', '<your private key (string)>')
except Exception as error:
print(f"Error: {error}")
# Use the generated token in the `assertion` field when requesting the /token endpoint
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"github.com/golang-jwt/jwt/v4"
)
// DecodePEMPrivateKey decodes the provided PEM-encoded private key into Golang's PrivateKey struct
func DecodePEMPrivateKey(privateKeyPEM string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(privateKeyPEM))
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
return privateKey, err
}
// GenerateAssertionJWT generates the encoded JWT token in the form of a string
func GenerateAssertionJWT(keyID string, privateKey *rsa.PrivateKey) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, &jwt.RegisteredClaims{ // you may have to use your own custom claims struct
// fill your claims here
Issuer: "test-client-id",
})
token.Header["kid"] = keyID // fill headers like this
tokenStr, err := token.SignedString(privateKey)
return tokenStr, err
}
// to use
privateKey, err := DecodePEMPrivateKey("<your private key>")
token, err := GenerateAssertionJWT("<your key id>", privateKey)
// token can be used in `assertion` field when requesting /token endpoint
Once you have generated the JWT assertion, you can exchange it for a three-legged access token. See Task 3 – Generate 3-Legged Access Token in the Walkthrough “Get Started with SSA” for step-by-step instructions.
Frequently asked questions
- I lost my private key, what should I do?
If your private key is lost or is compromised, generate a new key using the Create Key operation.
- Is there a way to identify whether an access token is generated using this flow?
Yes. You can determine if an access token was generated using the service account flow by checking its ID claim (jti).
Access tokens created through service accounts will have a distinctive “SA-” prefix in their ID claim.
- The Exchange JWT Assertion for Token operation is returning the error message “The ‘assertion’ is invalid”. What can I do?
Check for these common issues:
- Scope Format: Make sure that the
scope
claim is formatted as a string array, not as a single string. - Key ID Verification: Confirm that the
kid
parameter matches the SSA’s private key. If needed you can delete the current key and create a new one. - Missing Claims: Ensure that all required claims are included in your assertion.
- Expiration Time: Verify if the token expiry is set to a value between 0-5 minutes from now. Tokens with longer expiration times will be rejected.
- Audience Value: Ensure that the
aud
claim is set to https://developer.api.autodesk.com/authentication/v2/token.
- Scope Format: Make sure that the