Used to check for browser translation.
用于检测浏览器翻译。
ブラウザの翻訳を検出する
Core Concepts

Encryption and Security


When exchanging sensitive information such as personal data, private keys, or verifiable credentials, security is paramount. The did-auth library provides a built-in mechanism for establishing an encrypted communication channel between your application and the user's DID Wallet, ensuring that sensitive data is protected end-to-end.

This section explains how the encryption flow works and how you can leverage it to handle sensitive claims securely. For a practical example of requesting a sensitive claim, see the Request a Signature guide.

The Encryption Flow#

The did-auth-protocol version 1.0.0 introduces an end-to-end encryption mechanism. The process is largely automated by the library. When a wallet supporting this protocol version scans a QR code, it initiates the secure flow.

Here is a diagram illustrating the key exchange and encrypted communication process:

Your ApplicationUser's DID WalletYour ApplicationUser's DID Wallet1. Wallet initiates connection with its public key.2. Application sends the encrypted key back.A secure channel is now established.3. All subsequent sensitive data is encrypted with the sharedKey (Symmetric Encryption).GET /auth/token?_ek_=[walletPublicKey]&_v_=1.0.0Generate a unique symmetric `sharedKey` for the session.Encrypt the `sharedKey` using the Wallet's public key (Asymmetric Encryption).JSON response containing the encrypted `sharedKey`.Decrypt the `sharedKey` using its private key.POST /auth/response (userInfo payload is encrypted)Decrypts userInfo using `sharedKey`.POST /auth/request (authInfo payload is encrypted)Decrypts authInfo using `sharedKey`.

This process combines asymmetric encryption for securely exchanging a session key and symmetric encryption for efficient and secure data transfer thereafter.

Enabling Encryption#

Encryption is automatically enabled when the wallet signals its support by providing its public key (_ek_ parameter) and a compatible version (_v_ parameter) during the initial request. The server then uses this to establish the secure channel.

Requesting Sensitive Claims#

Your application doesn't need to manually call encryption functions for claims. The library handles it when you request claims that are inherently sensitive, such as a keyPair claim.

Here is an example of setting up a handler that requests the wallet to generate a new key pair. The secret key of this pair will be transmitted over the encrypted channel.

// did/did-auth/tests/handlers/wallet.spec.js

const handlers = new WalletHandlers({ tokenStorage, authenticator });

handlers.attach({
app: server,
action: 'encrypted',
onConnect: () => {
return [
{
keyPair: () => {
return {
mfa: true, // Requires multi-factor authentication
description: 'generate keypair',
moniker: 'test',
targetType: {
role: 'application',
},
};
},
},
];
},
onAuth: ({ claims }) => {
const keyPair = claims.find((x) => x.type === 'keyPair');
// The `keyPair.secret` was received securely.
console.log('Received secret key:', keyPair.secret);
return { successMessage: 'Key pair generated successfully!' };
},
});

In this flow, the wallet will encrypt the response containing the generated private key using the sharedKey before sending it to your application. The did-auth library automatically decrypts the incoming payload.

Core Encryption Functions#

The library exposes encrypt and decrypt functions, though you typically won't need to call them directly. They are used internally by the handlers.

  • encrypt(data, config, dataKey = 'authInfo'): Encrypts the data[dataKey] field if data.sensitive is true and a sharedKey is present in the config.
  • decrypt(data, config, dataKey = 'userInfo'): Decrypts the data[dataKey] field if a sharedKey is present in the config.

Here is how they behave based on the test cases:

// From did/did-auth/tests/protocol.spec.js

const sharedKey = 'abcd';
const clientVersion = '1.0.0';

// Encryption is triggered by the `sensitive: true` flag
const encrypted = encrypt(
{ sensitive: true, authInfo: 'some-payload' },
{ clientVersion, sharedKey }
);
// encrypted.authInfo is now a base58 encoded encrypted string

// Decryption
const decrypted = decrypt(
{
userInfo: encrypted.authInfo, // Using the encrypted string from above
version: '1.0.0'
},
{ sharedKey }
);
// decrypted.userInfo is now 'some-payload'

Securely Storing Session Data#

In some cases, you may need to store sensitive information within the session on the server side that isn't part of a claim. The updateSession function, available in the handler callbacks, supports a secure flag for this purpose.

When you update the session with secure: true, the data is encrypted using the wallet's public key (encryptionKey) before being stored. This ensures that even if the session storage is compromised, the sensitive data remains protected.

// did/did-auth/tests/handlers/wallet.spec.js

// Inside an `onAuth` callback for example:

onAuth: async ({ updateSession }) => {
const unsecure = 'unsecure_data';
const secure = 'secure_data';
const secureObj = { secret: 'is_safe' };

// Store data as-is
await updateSession('unsecure', unsecure);

// Store data securely
await updateSession({ secure, secureObj }, true);

return { successMessage: 'Data stored' };
}

This uses tweetnacl-sealedbox-js to perform public-key authenticated encryption, providing strong guarantees about the confidentiality and integrity of the stored data.


By leveraging these built-in features, you can ensure a high level of security for your DID-based authentication flows.

Next, you may want to learn more about the different types of information you can request from a user by reading about Understanding Claims.