Private Key JWT authentication is the most secure method for authenticating service users with Zitadel. It uses asymmetric cryptography where you sign a JWT assertion with your private key, eliminating the need to transmit secrets.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zitadel/client-ruby/llms.txt
Use this file to discover all available pages before exploring further.
This is the recommended authentication method for production environments.
How It Works
The Private Key JWT flow works as follows:Generate Service Account Key
Create a service user in Zitadel and download the JSON key file containing your private key, user ID, and key ID.
JWT Creation
When authentication is needed, the SDK creates a JWT assertion with:
- Issuer (
iss): Your user ID - Subject (
sub): Your user ID - Audience (
aud): Zitadel host URL - Issued At (
iat): Current timestamp - Expiration (
exp): Current timestamp + lifetime (default 3600 seconds)
JWT Signing
The SDK signs the JWT with your RSA private key using RS256 algorithm and includes the key ID in the JWT header.
Token Exchange
The signed JWT is sent to Zitadel’s token endpoint to exchange for an OAuth access token.
Setup
1. Create a Service User in Zitadel
In your Zitadel instance:- Navigate to your project
- Create a new service user or select an existing one
- Generate a new key
- Download the JSON key file
2. Secure Your Key File
The downloaded JSON file contains sensitive credentials:service-account.json
3. Initialize the SDK
Advanced Configuration
For advanced use cases, you can use the builder pattern to customize JWT parameters:Builder Methods
TheWebTokenAuthenticatorBuilder provides these configuration methods:
| Method | Description | Default |
|---|---|---|
key_identifier(key_id) | Sets the JWT kid header | From JSON file |
token_lifetime_seconds(seconds) | JWT expiration time | 3600 (1 hour) |
scopes(*scopes) | OAuth scopes to request | ['openid', 'urn:zitadel:iam:org:project:id:zitadel:aud'] |
Implementation Details
JWT Structure
The SDK generates JWTs with this structure:Header
Payload
Thread Safety
TheWebTokenAuthenticator is thread-safe:
- Token refresh is protected by a mutex
- Multiple threads can share the same client instance
- Automatic refresh happens when the token expires
Key Management
Key Rotation
Regularly rotate your service account keys:Multiple Environments
Use different service accounts for each environment:Storing Keys in Secret Managers
For cloud deployments, use secret management services:Troubleshooting
Invalid Key File Error
Error:- Verify the file path is correct
- Use absolute paths or ensure relative paths are from the working directory
- Check file permissions
JSON Parse Error
Error:- Ensure the file contains valid JSON
- Check for trailing commas or syntax errors
- Re-download the file from Zitadel if corrupted
Missing Required Keys
Error:- Verify the JSON file contains all required fields:
userId: Service user identifierkeyId: Key identifierkey: RSA private key in PEM format
- Re-download the key file from Zitadel
Token Refresh Failed
Error:- Check that the service user still exists in Zitadel
- Verify the key hasn’t been deactivated
- Ensure the Zitadel host URL is correct
- Check network connectivity to Zitadel
- Enable debug logging to see detailed error messages:
Invalid Signature
Error:- The private key may be corrupted
- The key ID (
kid) doesn’t match the key in Zitadel - Re-download the key file from Zitadel
Source Code Reference
The implementation can be found in:- Main entry point:
lib/zitadel/client/zitadel.rb:120-122 - Authenticator:
lib/zitadel/client/auth/web_token_authenticator.rb - Builder:
lib/zitadel/client/auth/web_token_authenticator.rb:125-164
Next Steps
Client Credentials
Simpler OAuth2 authentication
Personal Access Tokens
Quick development setup