JSON Web Signature (JWS)
JSON Web Signature (JWS) is a JOSE specification that defines a compact, URL-safe way to represent digitally signed content. Implementing JWS ensures the sender's identity is verified and the message has not been altered, providing non-repudiation for your interactions with the Bahn API.
Here is a summary of how the signing process works:
- Generate a P-256 key pair and provide the public key to Bahn during onboarding.
- Bahn registers the public key for use by the API.
- Once registered, you can call JWS-enabled endpoints with signed request bodies.
- Bahn verifies the signature using your registered public key and performs additional checks, including the validity of the
urlheader.
Providing Public Keys
Public keys must be submitted to Bahn during onboarding. See Authentication for the full requirements, including mandatory JWK fields.
Workflow
The following is a step-by-step guide to manually construct and send a JWS-signed request.
1. Construct the Header and Payload
Prepare the request body according to the API reference, then construct the JWS header with the required fields. The url field is mandatory — Bahn uses it to confirm the request has not been replayed against a different endpoint.
Header
{
"alg": "ES256",
"iat": 1773220702,
"kid": "client_key",
"typ": "jose+json",
"url": "/bn/api/playground/concat-strings"
}Payload
{
"string1": "Hello",
"string2": "World"
}2. Sign with Your Private Key
Base64url-encode the header and payload, then sign them using your P-256 private key. Use an established JOSE library to avoid encoding or signing errors.
The resulting compact JWS has three dot-separated parts:
<base64url(header)>.<base64url(payload)>.<signature>Example
eyJhbGciOiJFUzI1NiIsImlhdCI6MTc3MzIyMDcwMiwia2lkIjoiY2xpZW50X2tleSIsInR5cCI6IkpXUyIsInVybCI6Ii9ibi9hcGkvcGxheWdyb3VuZC9jb25jYXQtc3RyaW5ncyJ9.eyJzdHJpbmcxIjoiSGVsbG8iLCAic3RyaW5nMiI6IldvcmxkIn0.xIY9EZMJTcawCnH7J2NLZO6P66hFfexNU-gJdNqEAuRyHs4szMZLjW--GZ15N_6Q8WYmhY8DX_XVpBySvUigVA3. Submit the Request
Bahn supports two content types for submitting signed requests. Choose whichever best fits your integration.
Option A: Compact Serialization — application/jose
Send the JWS compact serialization (header.payload.signature) directly as the request body as a plaintext string:
curl -X POST 'https://t.cakedigibank.tech/bn/api/playground/concat-strings' \
-H 'Bn-Api-Key: <your-api-key>' \
-H 'Bn-Jose-Method: jws' \
-H 'Content-Type: application/jose' \
-d 'eyJhbGciOiJFUzI1NiIsImlhdCI6MTc3MzIyMDcwMiwia2lkIjoiY2xpZW50X2tleSIsInR5cCI6Impvc2UranNvbiIsInVybCI6Ii9ibi9hcGkvcGxheWdyb3VuZC9jb25jYXQtc3RyaW5ncyJ9.eyJzdHJpbmcxIjoiSGVsbG8iLCAic3RyaW5nMiI6IldvcmxkIn0.xIY9EZMJTcawCnH7J2NLZO6P66hFfexNU-gJdNqEAuRyHs4szMZLjW--GZ15N_6Q8WYmhY8DX_XVpBySvUigVA'Structure:
BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)Option B: JSON Serialization — application/jose+json
Send the JWS General JSON Serialization (RFC 7515 Section 7.2) as the request body. This format separates the protected header, payload, and signature into distinct JSON fields:
curl -X POST 'https://t.cakedigibank.tech/bn/api/playground/concat-strings' \
-H 'Bn-Api-Key: <your-api-key>' \
-H 'Bn-Jose-Method: jws' \
-H 'Content-Type: application/jose+json' \
-d '{
"payload": "eyJzdHJpbmcxIjoiSGVsbG8iLCAic3RyaW5nMiI6IldvcmxkIn0",
"signatures": [
{
"protected": "eyJhbGciOiJFUzI1NiIsImlhdCI6MTc3MzIyMDcwMiwia2lkIjoiY2xpZW50X2tleSIsInR5cCI6Impvc2UranNvbiIsInVybCI6Ii9ibi9hcGkvcGxheWdyb3VuZC9jb25jYXQtc3RyaW5ncyJ9",
"signature": "xIY9EZMJTcawCnH7J2NLZO6P66hFfexNU-gJdNqEAuRyHs4szMZLjW--GZ15N_6Q8WYmhY8DX_XVpBySvUigVA"
}
]
}'Structure:
{
"payload": "<base64url(payload)>",
"signatures": [
{
"protected": "<base64url(header)>",
"signature": "<base64url(signature)>"
}
]
}When using
application/jose+jsonwith multiple signatures, Bahn uses the first signature entry for verification.
On success, Bahn returns the concatenated result:
{
"result": "HelloWorld"
}