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

Testing


Effectively testing your DID-Auth integration is crucial for building a reliable and secure application. This guide provides a practical approach to writing automated tests for your authentication flows, using the same patterns found in the did-auth library's own test suite.

The primary strategy is to simulate the entire authentication process programmatically. This involves setting up a test server for your application and using an HTTP client, like axios, to act as the user's wallet.

Testing Architecture Overview#

Your test environment will typically consist of a test runner (like Jest), your application running on a test server, and an HTTP client that mimics the wallet's behavior.

Test Execution

Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list
Unsupported markdown: list

Test Runner (e.g., Jest)

HTTP Client (axios)

Your Application (Test Server)

Assert Final State


Setting Up the Test Environment#

Before you can test the flow, you need to prepare a few components.

  1. Test Server: Spin up an instance of your application. The library's test files use a simple create-test-server script, but any method that runs your Express app will work.
  2. In-Memory Storage: Use MemoryAuthStorage to ensure that sessions are isolated between tests and don't persist state.
const MemoryAuthStorage = require('@arcblock/did-auth-storage-memory');
const tokenStorage = new MemoryAuthStorage();
  1. Mock Wallets: Generate temporary wallets for the application and the simulated user. This allows you to sign and verify messages without needing real secret keys.
const { fromRandom, WalletType } = require('@ocap/wallet');
const Mcrypto = require('@ocap/mcrypto');

// Create a wallet for your application
const appWallet = fromRandom(WalletType({
role: Mcrypto.types.RoleType.ROLE_APPLICATION,
pk: Mcrypto.types.KeyType.ED25519,
hash: Mcrypto.types.HashType.SHA3,
}));

// Create a wallet for the simulated user
const userWallet = fromRandom();

Simulating the Authentication Flow#

Here is a step-by-step breakdown of how to simulate a standard login flow in a test.

Step 1: Initiate the Session and Get a Token#

Make a request to your token generation endpoint. The server will create a new session and return a session token and a url for the wallet to connect to.

// 1. Generate a new session token
const { data } = await axios.get(`${server.url}/api/did/login/token`);
const sessionToken = data.token;
const authUrl = decodeURIComponent(new URL(data.url).searchParams.get('url'));

Step 2: Simulate the Wallet Scan#

The wallet scans the QR code, which contains the authUrl. Your test simulates this by making a GET request to that URL. The application responds with the initial challenge and the claims it wants the user to provide.

// 2. Simulate wallet scanning the QR code
const { data: challengeResponse } = await axios.get(authUrl);
const authInfo1 = Jwt.decode(challengeResponse.authInfo);

Step 3: Respond to the First Challenge (Connect)#

The user approves the connection in their wallet. Your test simulates this by signing the challenge with the user's wallet and POST-ing it back to the url provided in the previous step.

// 3. User approves the connection
const { data: claimsResponse } = await axios.post(
authInfo1.url,
{
userPk: toBase58(userWallet.publicKey),
userInfo: Jwt.sign(userWallet.address, userWallet.secretKey, {
requestedClaims: [],
challenge: authInfo1.challenge
}),
}
);
const authInfo2 = Jwt.decode(claimsResponse.authInfo);

Step 4: Provide and Sign the Requested Claims#

If the application requested claims (like a profile), the wallet now prompts the user to approve sharing them. Your test simulates this by creating a JWT with the requested claims, signing it with the new challenge, and POST-ing it.

// 4. User provides the requested profile information
const { data: finalResponse } = await axios.post(
authInfo2.url,
{
userPk: toBase58(userWallet.publicKey),
userInfo: Jwt.sign(userWallet.address, userWallet.secretKey, {
requestedClaims: [{ type: 'profile', email: 'test@example.com', fullName: 'Test User' }],
challenge: authInfo2.challenge,
}),
}
);

Step 5: Verify the Final Status#

Finally, poll the status endpoint to confirm that the session was successful.

// 5. Check the session status
const { data: finalStatus } = await axios.get(`${server.url}/api/did/login/status?_t_=${sessionToken}`);

expect(finalStatus.status).toEqual('succeed');

Testing Specific Scenarios#

Your tests can be adapted to cover various authentication patterns.

  • Multi-Step Workflows: If your onAuth handler returns a nextWorkflow, your test should capture it from the response and simulate a new wallet scan on the subsequent URL. See Handle Multiple Workflows for more.
  • User Decline: To test a user declining the request, set action: 'declineAuth' in the userInfo JWT payload during the challenge response. The final status should be 'error'.
  • Error Handling: You can test server-side errors by configuring claim handlers to throw exceptions and asserting that the onError hook is called and the session status becomes 'error'.