JWS Implementation Guide for Unified Transfer

⚠️

Unified Transfer is in active development. As a pioneer merchant, you will be informed of the updates through your Maya Relationship Manager.

Overview

Unified Transfer requires all protected API requests to be digitally signed using JSON Web Signature (JWS) in addition to OAuth 2.0 authentication.

This guide explains how to implement your JWS library and correctly generate and attach the required signature.

Pre-requisite

Before proceeding, make sure you have read the following guides:


JWS Implementation

Step 1: Use your valid private key

Use the RSA private key generated during Setting Up Your JWKS Endpoint for Unified Transfer.

Important:

  • The private key must correspond to a public key published in your JWKS endpoint.
  • The kid used in the JWS header must match the kid of the public key in your JWKS.
  • Never expose or transmit your private key.

Step 2: Create your JWS Header

Every signature should use this header format:

{
  "alg": "RS256",
  "kid": "your-unique-key-id-12345",
  "typ": "JWT"
}

Where:

  • alg: The signing algorithm (RS256 recommended)
  • kid: Your Key ID from the JWKS endpoint (must match)
  • typ: Token type (JWT)

If the kid does not match a registered key, the request will be rejected.

Step 3: Sign the Exact Request Payload

Generate and include the signature in the x-jws-signature header.


Implementation Examples

Node.js (jsonwebtoken library)

const jwt = require('jsonwebtoken');
const fs = require('fs');

// Load your private key once at startup
const privateKey = fs.readFileSync('private-key.pem');
const keyId = 'your-unique-key-id-12345';

function signRequest(requestBody) {
  // Create JWS signature for this specific request
  const signature = jwt.sign(requestBody, privateKey, {
    algorithm: 'RS256',
    keyid: keyId,
    header: {
      typ: 'JWT'
    }
  });
  
  return signature;
}

// Usage example
const requestBody = {
  data: {
    initiation: {
      debit_account: { /* ... */ },
      credit_account: { /* ... */ },
      amount: { /* ... */ }
    }
  }
};

const signature = signRequest(requestBody);
// Use signature in x-jws-signature header

Python (PyJWT library)

import jwt
import json

# Load your private key once at startup
with open('private-key.pem', 'r') as f:
    private_key = f.read()

key_id = 'your-unique-key-id-12345'

def sign_request(request_body):
    """Create JWS signature for a specific request"""
    signature = jwt.encode(
        request_body,
        private_key,
        algorithm='RS256',
        headers={'kid': key_id, 'typ': 'JWT'}
    )
    return signature

# Usage example
request_body = {
    "data": {
        "initiation": {
            "debit_account": {},
            "credit_account": {},
            "amount": {}
        }
    }
}

signature = sign_request(request_body)
# Use signature in x-jws-signature header

Troubleshooting Signature Issues

Signature Verification Failed

Possible causes:

  • kid in signature doesn't match JWKS endpoint
  • Request body doesn't match signed payload
  • Using the wrong private key
  • Algorithm mismatch (e.g., using RS512 instead of RS256)

Solutions:

  • Verify kid matches your JWKS
  • Ensure you're signing the exact request body
  • Confirm you're using the correct private key for the environment
  • Check the algorithm is RS256

Invalid Signature Format

Possible causes:

  • Signature is not in JWS Compact Serialization format
  • Missing or incorrect header fields

Solutions:

  • Ensure signature has three parts separated by dots (header.payload.signature)
  • Verify JWS header includes alg, kid, and typ fields

Developer Notes and Tips

  1. Use the correct private key and kid values
    • The private key should be the correct pair of the public key loaded in your JWKS endpoint.
    • kid in your JWS header should match the one in your JWKS endpoint.
    • Update the values during key rotation
  2. Sign the exact payload: The signature must match the exact JSON body sent in the request
  3. Handle errors gracefully: Implement retry logic for signature verification failures
  4. Never log signatures: Signatures can be used to replay requests if intercepted

FAQs

Q: Can I use the same key pair for multiple environments?

A: No. Use separate key pairs for Sandbox and Production environments for better security isolation.

Q: Do I need to notify Maya every time I rotate keys?

A: Yes, coordinate with Maya before removing old keys to ensure a smooth transition. Adding new keys can be done independently.


Next Steps

Once you are done with the JWS Implementation, you are now ready for onboarding and will begin your integration. To start, see Getting Started with Unified Transfer.