Private Key JWT lets you authenticate using asymmetric cryptography instead of a shared secret.
Traditional OAuth uses a client_secret - a password shared between you and the auth server. Problems with this:
- Secrets leak through logs, config files, git history
- If the server is breached, your secret is exposed
- Rotating a compromised secret requires coordination
- The secret gets sent over the network on every request
With Private Key JWT, you keep a private key and share only the public key with Jiko. To authenticate, you sign a JWT with your private key. Jiko verifies it with your public key.
The private key never leaves your infrastructure.
| Client Secret | Private Key JWT | |
|---|---|---|
| Transmission | Sent every request | Never sent |
| Server breach | Secret exposed | Your key safe |
| Rotation | Coordinated update | Update independently |
- Sign a JWT with your private key
- Send it as
client_assertionin token requests - Jiko verifies the signature with your public key
Header:
{
"alg": "PS256",
"typ": "JWT"
}Payload:
{
"iss": "your-client-id",
"sub": "your-client-id",
"aud": "https://auth.jiko.io/api/oauth2/token",
"iat": 1711910400,
"exp": 1711910700,
"jti": "unique-token-id"
}iss/sub- your client IDaud- the token endpointiat/exp- issued/expiry time (keep it under 5 minutes)jti- unique ID to prevent replay
POST /api/oauth2/token HTTP/1.1
Host: auth.jiko.io
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=authorization-code&
client_id=your-client-id&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
client_assertion=eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9...# Generate private key
openssl genrsa -out private_key.pem 2048
# Extract public key
openssl rsa -in private_key.pem -pubout -out public_key.pemfrom cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
# Save private key
with open("private_key.pem", "wb") as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
))
# Save public key
with open("public_key.pem", "wb") as f:
f.write(private_key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
))Add your public key in the Settings page of the Jiko authentication portal. You'll need:
- Your public key (PEM format)
- Signing algorithm (PS256 or EdDSA recommended)
Keep your private key secure. Don't commit it to git.
- Use a secrets manager or HSM in production
- Keep JWTs short-lived (under 5 minutes)
- Use unique
jtivalues - Rotate keys periodically - register the new one before removing the old
- RFC 7523 - JWT client authentication
- OpenID Connect Core - private_key_jwt method
- OAuth 2.0 Security BCP - recommends asymmetric auth