Issuer Service Configuration

The issuer-service.conf file configures core Issuer2 service settings: the service's public URL, the key used to sign OAuth2 tokens, optional credential encryption support, and the Pushed Authorization Request (PAR) policy.

File Location

waltid-services/waltid-issuer-api2/config/issuer-service.conf

Configuration Options

PropertyTypeRequiredDefaultDescription
baseUrlStringYesPublic URL where the issuer service is reachable.
ciTokenKeyStringNoGenerated secp256r1 JWKSerialized key used to sign/verify OAuth2 access & refresh tokens.
credentialEncryptionKeyStringNoNot setSerialized EC P-256 private key used for OID4VCI Credential Request decryption and Credential Response encryption.
enforcePushedAuthorizationRequestsBooleanNofalseRequire clients to use the Pushed Authorization Request (PAR) endpoint.

baseUrl

The public URL where the issuer service is accessible. Every OpenID4VCI endpoint is derived from it:

  • The credential issuer base and metadata URLs become "<baseUrl>/openid4vci". This is what is written into credential offers and the .well-known metadata, so it must be the URL wallets can actually reach.
  • The external OAuth callback becomes "<baseUrl>/openid4vci/external/oauth/callback".

Trailing slashes are trimmed automatically.

baseUrl = "http://localhost:7005"

When running via Docker Compose, environment interpolation is supported:

baseUrl = "http://${SERVICE_HOST}:${ISSUER_API2_PORT}"

ciTokenKey

The key used to sign and verify the OAuth2 access tokens and refresh tokens issued by the service's token endpoint.

The value is a serialized walt.id key — the same format used elsewhere in walt.id — provided as a single JSON string. The type field selects the key backend and is required; omitting it causes startup to fail. Because the value is a string (not a HOCON object), use a triple-quoted HOCON string so the inner quotes do not need escaping:

ciTokenKey = """{"type":"jwk","jwk":{"kty":"EC","d":"...","crv":"P-256","x":"...","y":"..."}}"""

Supported type values include jwk (local key), tse (HashiCorp Vault Transit), aws-rest-api (AWS KMS), azure-rest-api (Azure Key Vault) and oci-rest-api (Oracle Cloud KMS).

Generating a key

The simplest way to get a stable local JWK in exactly the format above is a one-line Node.js command. It is identical on macOS, Linux, and Windows (PowerShell or cmd):

node -e "const{generateKeyPairSync}=require('crypto');const{privateKey}=generateKeyPairSync('ec',{namedCurve:'P-256'});const q=String.fromCharCode(34).repeat(3);console.log('ciTokenKey = '+q+JSON.stringify({type:'jwk',jwk:privateKey.export({format:'jwk'})})+q)"

It prints the complete config line — triple-quoted and ready to paste straight into issuer-service.conf, for example:

ciTokenKey = """{"type":"jwk","jwk":{"kty":"EC","x":"...","y":"...","crv":"P-256","d":"..."}}"""

Generate your own — never reuse the example value from these docs.

If you don't have Node installed, you can instead simply omit ciTokenKey: the service generates a key automatically on startup. Note the limitation described below.

If omitted, a new secp256r1 JWK is generated on every startup. This is fine for local development, but not for production: the signing key changes on each restart, so any tokens issued before a restart can no longer be verified. Always set ciTokenKey explicitly in real deployments.

credentialEncryptionKey

credentialEncryptionKey enables OID4VCI Credential Request encryption and Credential Response encryption.

Issuer2 supports this profile:

  • Key type: EC P-256
  • JWE alg: ECDH-ES
  • JWE enc: A128GCM
  • Compression: not supported

When configured, issuer metadata includes credential_request_encryption and credential_response_encryption with encryption_required = false.

Wallets can keep using normal JSON credential requests. Wallets that send an encrypted Credential Request as application/jwt and include credential_response_encryption receive the Credential Response as an encrypted JWE.

Use a dedicated key for credential encryption. Do not reuse the token signing key or credential signing keys.

credentialEncryptionKey = """{"type":"jwk","jwk":{"kty":"EC","crv":"P-256","x":"...","y":"...","d":"..."}}"""

enforcePushedAuthorizationRequests

Controls the OAuth2 Pushed Authorization Request (PAR, RFC 9126) policy for the authorization code flow. Defaults to false.

When true, clients must submit authorization parameters to the PAR (/par) endpoint instead of passing them directly to /authorize.

Leave this false unless you specifically need to mandate PAR.

enforcePushedAuthorizationRequests = false

Example Configuration

# issuer-service.conf

baseUrl = "http://localhost:7005"

# Optional: provide a stable token signing key (recommended for anything beyond local dev)
ciTokenKey = """{"type":"jwk","jwk":{"kty":"EC","d":"...","crv":"P-256","x":"...","y":"..."}}"""

# Optional: enable OID4VCI Credential Request/Response encryption
credentialEncryptionKey = """{"type":"jwk","jwk":{"kty":"EC","crv":"P-256","x":"...","y":"...","d":"..."}}"""

# Optional: require Pushed Authorization Requests
enforcePushedAuthorizationRequests = false

Production Configuration

For production deployments, ensure:

  1. baseUrl points to your public HTTPS URL.
  2. ciTokenKey is set to a securely generated, persistent key (consider a KMS backend such as aws-rest-api, azure-rest-api or tse rather than an inline JWK).
  3. If encrypted Credential Requests or encrypted Credential Responses are required, set credentialEncryptionKey to a dedicated EC P-256 private key.
# Production example
baseUrl = "https://issuer.example.com"
ciTokenKey = """{"type":"jwk","jwk":{"kty":"EC","d":"...","crv":"P-256","x":"...","y":"..."}}"""
credentialEncryptionKey = """{"type":"jwk","jwk":{"kty":"EC","crv":"P-256","x":"...","y":"...","d":"..."}}"""
enforcePushedAuthorizationRequests = true

These concerns are configured in their own files, not in issuer-service.conf:

Last updated on July 28, 2026