AWS KMS EdDSA signing for Authress JWTs
With AWS KMS you can generate an Ed25519 signing key whose private key material never leaves KMS. Instead of storing a private key in your service, you store only the KMS key ARN. Every time your service needs to authenticate, it calls KMS to sign the JWT payload โ the private key is never exposed.
This guide covers the one-time setup to register the KMS-backed key with Authress, and the production runtime flow to generate signed JWTs using the Authress SDK. For CI/CD scenarios and build pipelines instead consult the Trusted OIDC provider setup instead.
The same KMS service client BYOK pattern works across all BYOK contexts:
- Your service โ Authress โ use a KMS-backed key as your Authress service client identity
- Service-to-service (M2M) โ sign tokens for inter-service calls without storing any secrets
- Customer BYOK โ your customer generates a key in their own AWS account, exports only the public key, and you upload it to a service client you create on their behalf
Backgroundโ
Authress service client access keys are asymmetric Ed25519 key pairs. Authress stores the public key and your service holds the private key to sign JWTs. In the standard flow the private key is returned to you at key generation time and you store it securely.
With the KMS pattern, you generate the key pair inside KMS โ the private key is created and stored by AWS and is never exportable. You extract only the public key and upload it to Authress. At runtime you call the KMS Sign API to produce the JWT signature.
Setup (one-time)โ
Install the AWS SDK first:
npm install @aws-sdk/client-kms
1. Create an Ed25519 key in AWS KMSโ
import { KMSClient, CreateKeyCommand, CreateAliasCommand } from '@aws-sdk/client-kms';
const kmsClient = new KMSClient({ });
const { KeyMetadata } = await kmsClient.send(new CreateKeyCommand({
KeySpec: 'ECC_NIST_EDWARDS25519',
KeyUsage: 'SIGN_VERIFY',
Description: 'Authress service client key'
}));
await kmsClient.send(new CreateAliasCommand({
AliasName: 'alias/authress-service-client',
TargetKeyId: KeyMetadata.KeyId
}));
const kmsKeyArn = KeyMetadata.Arn;
console.log('KMS Key ARN:', kmsKeyArn);
// Store this ARN in your deployment config
2. Retrieve the public key and register it with Authressโ
Use the KMS public key to register a service client with Authress. KMS returns the public key in DER-encoded SPKI format โ which is exactly what Authress expects.
import { KMSClient, GetPublicKeyCommand } from '@aws-sdk/client-kms';
import { AuthressClient } from '@authress/sdk';
const kmsClient = new KMSClient({});
const authressClient = new AuthressClient(
{ authressApiUrl: 'https://auth.yourdomain.com' },
adminServiceClientAccessKey // your existing Authress admin service client key
);
// Get the public key from KMS
const { PublicKey } = await kmsClient.send(new GetPublicKeyCommand({ KeyId: kmsKeyArn }));
// Create a new service client in Authress
const { data: serviceClient } = await authressClient.serviceClients.createClient({
name: 'My KMS-backed service'
});
// Upload the public key to Authress
const { data: accessKeyResult } = await authressClient.serviceClients.requestAccessKey(
serviceClient.clientId,
{ publicKey: Buffer.from(PublicKey).toString('base64') }
);
// Save these for runtime use
const clientId = serviceClient.clientId; // e.g. "sc_serviceClientId"
const keyId = accessKeyResult.keyId; // the key ID assigned by Authress
At this point Authress has your public key and will accept JWTs signed by the corresponding KMS private key.
Runtime โ Generating Signed JWTsโ
At runtime your service calls the Authress SDK to get a signed JWT. The KmsServiceClientTokenProvider can be passed directly to AuthressClient:
import { KmsServiceClientTokenProvider } from '@authress/sdk';
const tokenProvider = new KmsServiceClientTokenProvider({
kmsKeyArn: 'arn:aws:kms:us-east-1:123456789012:key/abc-123',
clientId: 'sc_serviceClientId',
keyId: 'accessKeyId',
authressAccountId: 'acc_accountId'
});
import { AuthressClient } from '@authress/sdk';
const authressClient = new AuthressClient(
{ authressApiUrl: 'https://auth.yourdomain.com' },
tokenProvider
);
// Make authorized calls to Authress as normal
await authressClient.accessRecords.createRecord({ ... });