@solana/keychain-dfns 0.0.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +136 -0
  2. package/dist/__tests__/dfns-signer.integration.test.d.ts +2 -0
  3. package/dist/__tests__/dfns-signer.integration.test.d.ts.map +1 -0
  4. package/dist/__tests__/dfns-signer.integration.test.js +17 -0
  5. package/dist/__tests__/dfns-signer.integration.test.js.map +1 -0
  6. package/dist/__tests__/dfns-signer.test.d.ts +2 -0
  7. package/dist/__tests__/dfns-signer.test.d.ts.map +1 -0
  8. package/dist/__tests__/dfns-signer.test.js +157 -0
  9. package/dist/__tests__/dfns-signer.test.js.map +1 -0
  10. package/dist/__tests__/setup.d.ts +45 -0
  11. package/dist/__tests__/setup.d.ts.map +1 -0
  12. package/dist/__tests__/setup.js +64 -0
  13. package/dist/__tests__/setup.js.map +1 -0
  14. package/dist/auth.d.ts +7 -0
  15. package/dist/auth.d.ts.map +1 -0
  16. package/dist/auth.js +117 -0
  17. package/dist/auth.js.map +1 -0
  18. package/dist/dfns-signer.d.ts +65 -0
  19. package/dist/dfns-signer.d.ts.map +1 -0
  20. package/dist/dfns-signer.js +331 -0
  21. package/dist/dfns-signer.js.map +1 -0
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +2 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/types.d.ts +101 -0
  27. package/dist/types.d.ts.map +1 -0
  28. package/dist/types.js +2 -0
  29. package/dist/types.js.map +1 -0
  30. package/package.json +61 -8
  31. package/src/__tests__/dfns-signer.integration.test.ts +17 -0
  32. package/src/__tests__/dfns-signer.test.ts +217 -0
  33. package/src/__tests__/setup.ts +76 -0
  34. package/src/auth.ts +136 -0
  35. package/src/dfns-signer.ts +421 -0
  36. package/src/index.ts +2 -0
  37. package/src/types.ts +113 -0
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # @solana/keychain-dfns
2
+
3
+ Dfns-based signer for Solana transactions using [Dfns](https://dfns.co) Keys API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @solana/keychain-dfns
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ 1. A [Dfns](https://dfns.co) account with API access
14
+ 2. A Solana wallet created in the Dfns dashboard
15
+ 3. A service account with signing permissions
16
+
17
+ ### Setting Up a Service Account
18
+
19
+ Follow the [Creating a Service
20
+ Account](https://docs.dfns.co/guides/developers/creating-a-service-account) guide:
21
+
22
+ 1. **Generate a keypair** - https://docs.dfns.co/guides/developers/generate-a-key-pair#generate-a-key-pair
23
+ 2. **Create the service account** in Settings > Developers > Service Accounts, pasting your public key
24
+ 3. **Save the auth token** — it is only shown once. Store it securely along with your private key
25
+ 4. **Configure permissions** — assign a role with minimal required permissions
26
+
27
+ You will need:
28
+ - **Auth token** (`authToken`) — the service account token
29
+ - **Credential ID** (`credId`) — shown in the service account details
30
+ - **Private key** (`privateKeyPem`) — the PEM key you generated in step 1
31
+ - **Wallet ID** (`walletId`) — found in the Dfns dashboard under Wallets
32
+
33
+ ## Usage
34
+
35
+ ### Basic Setup
36
+
37
+ ```typescript
38
+ import { createDfnsSigner } from '@solana/keychain-dfns';
39
+
40
+ const signer = await createDfnsSigner({
41
+ authToken: 'your-service-account-token',
42
+ credId: 'your-credential-id',
43
+ privateKeyPem: `-----BEGIN PRIVATE KEY-----
44
+ ...your private key...
45
+ -----END PRIVATE KEY-----`,
46
+ walletId: 'wa-xxxxx-xxxxx-xxxxx',
47
+ });
48
+
49
+ console.log('Signer address:', signer.address);
50
+ ```
51
+
52
+ ### Signing Transactions
53
+
54
+ ```typescript
55
+ import {
56
+ appendTransactionMessageInstructions,
57
+ createSolanaRpc,
58
+ createTransactionMessage,
59
+ getBase64EncodedWireTransaction,
60
+ pipe,
61
+ setTransactionMessageFeePayerSigner,
62
+ setTransactionMessageLifetimeUsingBlockhash,
63
+ signTransactionMessageWithSigners,
64
+ } from '@solana/kit';
65
+
66
+ const rpc = createSolanaRpc('https://api.devnet.solana.com');
67
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
68
+
69
+ const message = pipe(
70
+ createTransactionMessage({ version: 0 }),
71
+ (tx) => setTransactionMessageFeePayerSigner(signer, tx),
72
+ (tx) => appendTransactionMessageInstructions([/* your instructions */], tx),
73
+ (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
74
+ );
75
+
76
+ const signedTx = await signTransactionMessageWithSigners(message);
77
+ const encoded = getBase64EncodedWireTransaction(signedTx);
78
+ await rpc.sendTransaction(encoded).send();
79
+ ```
80
+
81
+ ### Signing Messages
82
+
83
+ ```typescript
84
+ const message = new TextEncoder().encode('Hello, Solana!');
85
+ const [signatureDictionary] = await signer.signMessages([{ content: message }]);
86
+ ```
87
+
88
+ ## Configuration
89
+
90
+ ### DfnsSignerConfig
91
+
92
+ | Field | Type | Required | Default | Description |
93
+ |-----------------|----------|----------|-------------------------|---------------------------------------------------|
94
+ | `authToken` | `string` | Yes | - | Service account token or personal access token |
95
+ | `credId` | `string` | Yes | - | Credential ID for user action signing |
96
+ | `privateKeyPem` | `string` | Yes | - | Private key in PEM format |
97
+ | `walletId` | `string` | Yes | - | Dfns wallet ID (starts with `wa-`) |
98
+ | `apiBaseUrl` | `string` | No | `https://api.dfns.io` | Custom API base URL |
99
+ | `requestDelayMs`| `number` | No | `0` | Delay in ms between concurrent signing requests |
100
+
101
+ ## How It Works
102
+
103
+ 1. **Initialization**: `create()` fetches the wallet from Dfns API and validates the Ed25519 public key
104
+ (your Solana address)
105
+ 2. **Signing**: Transactions and messages are sent to the Dfns Keys API (`/keys/{keyId}/signatures`)
106
+ which returns Ed25519 signature components (r, s)
107
+ 3. **User Action Signing**: Mutating API requests (like signing) go through a 3-step challenge/response
108
+ flow — the SDK handles this automatically using your `credId` and `privateKeyPem`
109
+
110
+ ## Error Handling
111
+
112
+ The signer throws errors with specific codes from `@solana/keychain-core`:
113
+
114
+ - `CONFIG_ERROR` — Invalid configuration (missing fields, inactive wallet, unsupported key type)
115
+ - `REMOTE_API_ERROR` — Dfns API returned an error
116
+ - `SIGNING_FAILED` — Signature request failed or requires policy approval
117
+ - `PARSING_ERROR` — Failed to parse Dfns response
118
+
119
+ ```typescript
120
+ import { SignerErrorCode } from '@solana/keychain-core';
121
+
122
+ try {
123
+ await signer.signMessages([message]);
124
+ } catch (error) {
125
+ if (error.code === SignerErrorCode.REMOTE_API_ERROR) {
126
+ console.error('Dfns API error:', error.message);
127
+ }
128
+ }
129
+ ```
130
+
131
+ ## Security Considerations
132
+
133
+ 1. **Token Security**: Never hardcode auth tokens. Use environment variables or a secrets manager.
134
+ 2. **Private Key**: Store the PEM private key securely. It is used to sign every user action requests.
135
+ 3. **Policy Engine**: Configure [Dfns Policies](https://docs.dfns.co/api-reference/policies) to enforce
136
+ signing rules and approval workflows.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dfns-signer.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfns-signer.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dfns-signer.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ import { describe, it } from 'vitest';
2
+ import { runSignerIntegrationTest } from '@solana/keychain-test-utils';
3
+ import { getConfig } from './setup';
4
+ import { config } from 'dotenv';
5
+ config();
6
+ describe('DfnsSigner Integration', () => {
7
+ it.skipIf(!process.env.DFNS_AUTH_TOKEN)('signs transactions with real API', async () => {
8
+ await runSignerIntegrationTest(await getConfig(['signTransaction']));
9
+ });
10
+ it.skipIf(!process.env.DFNS_AUTH_TOKEN)('signs messages with real API', async () => {
11
+ await runSignerIntegrationTest(await getConfig(['signMessage']));
12
+ });
13
+ it.skipIf(!process.env.DFNS_AUTH_TOKEN)('simulates transactions with real API', async () => {
14
+ await runSignerIntegrationTest(await getConfig(['simulateTransaction']));
15
+ });
16
+ });
17
+ //# sourceMappingURL=dfns-signer.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfns-signer.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/dfns-signer.integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,MAAM,EAAE,CAAC;AAET,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,wBAAwB,CAAC,MAAM,SAAS,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,wBAAwB,CAAC,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,wBAAwB,CAAC,MAAM,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dfns-signer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfns-signer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dfns-signer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ vi.mock('@solana/keychain-core', async (importOriginal) => {
3
+ const mod = await importOriginal();
4
+ return { ...mod, assertSignatureValid: vi.fn() };
5
+ });
6
+ import { DfnsSigner } from '../dfns-signer.js';
7
+ import { TEST_AUTH_TOKEN, TEST_CRED_ID, TEST_ED25519_PEM, TEST_WALLET_ID, createSignatureResponse, createUserActionInitResponse, createUserActionResponse, createWalletResponse, } from './setup.js';
8
+ global.fetch = vi.fn();
9
+ const mockFetch = global.fetch;
10
+ const defaultConfig = {
11
+ authToken: TEST_AUTH_TOKEN,
12
+ credId: TEST_CRED_ID,
13
+ privateKeyPem: TEST_ED25519_PEM,
14
+ walletId: TEST_WALLET_ID,
15
+ };
16
+ function mockWalletFetch(overrides) {
17
+ mockFetch.mockResolvedValueOnce({
18
+ json: async () => createWalletResponse(overrides),
19
+ ok: true,
20
+ });
21
+ }
22
+ describe('DfnsSigner', () => {
23
+ beforeEach(() => {
24
+ vi.resetAllMocks();
25
+ });
26
+ describe('create', () => {
27
+ it('creates a DfnsSigner with valid config', async () => {
28
+ mockWalletFetch();
29
+ const signer = await DfnsSigner.create(defaultConfig);
30
+ expect(signer).toBeDefined();
31
+ expect(signer.address).toBeDefined();
32
+ });
33
+ it('throws error for missing authToken', async () => {
34
+ await expect(DfnsSigner.create({ ...defaultConfig, authToken: '' })).rejects.toThrow('Missing required authToken field');
35
+ });
36
+ it('throws error for missing credId', async () => {
37
+ await expect(DfnsSigner.create({ ...defaultConfig, credId: '' })).rejects.toThrow('Missing required credId field');
38
+ });
39
+ it('throws error for missing privateKeyPem', async () => {
40
+ await expect(DfnsSigner.create({ ...defaultConfig, privateKeyPem: '' })).rejects.toThrow('Missing required privateKeyPem field');
41
+ });
42
+ it('throws error for missing walletId', async () => {
43
+ await expect(DfnsSigner.create({ ...defaultConfig, walletId: '' })).rejects.toThrow('Missing required walletId field');
44
+ });
45
+ it('throws error for inactive wallet', async () => {
46
+ mockWalletFetch({ status: 'Inactive' });
47
+ await expect(DfnsSigner.create(defaultConfig)).rejects.toThrow('not active');
48
+ });
49
+ it('throws error for non-EdDSA scheme', async () => {
50
+ mockWalletFetch({ scheme: 'ECDSA' });
51
+ await expect(DfnsSigner.create(defaultConfig)).rejects.toThrow('Unsupported key scheme');
52
+ });
53
+ it('throws error for API failure', async () => {
54
+ mockFetch.mockResolvedValueOnce({
55
+ ok: false,
56
+ status: 401,
57
+ });
58
+ await expect(DfnsSigner.create(defaultConfig)).rejects.toThrow();
59
+ });
60
+ it('throws error for negative requestDelayMs', async () => {
61
+ await expect(DfnsSigner.create({ ...defaultConfig, requestDelayMs: -1 })).rejects.toThrow('requestDelayMs must not be negative');
62
+ });
63
+ it('warns for high requestDelayMs', async () => {
64
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
65
+ mockWalletFetch();
66
+ await DfnsSigner.create({ ...defaultConfig, requestDelayMs: 5000 });
67
+ expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('requestDelayMs is greater than 3000ms'));
68
+ warnSpy.mockRestore();
69
+ });
70
+ it('throws HTTP_ERROR when fetch fails during create', async () => {
71
+ mockFetch.mockRejectedValueOnce(new Error('Network timeout'));
72
+ await expect(DfnsSigner.create(defaultConfig)).rejects.toMatchObject({
73
+ code: 'SIGNER_HTTP_ERROR',
74
+ message: expect.stringContaining('Dfns network request failed'),
75
+ });
76
+ });
77
+ });
78
+ describe('signMessages', () => {
79
+ it('throws HTTP_ERROR when fetch fails during signing', async () => {
80
+ mockWalletFetch();
81
+ const signer = await DfnsSigner.create(defaultConfig);
82
+ // The auth flow init request fails with network error
83
+ mockFetch.mockRejectedValueOnce(new Error('Network timeout'));
84
+ await expect(signer.signMessages([{ content: new Uint8Array([1, 2, 3]), signatures: {} }])).rejects.toMatchObject({
85
+ code: 'SIGNER_HTTP_ERROR',
86
+ message: expect.stringContaining('Dfns network request failed'),
87
+ });
88
+ });
89
+ it('signs a message successfully', async () => {
90
+ const rHex = '11'.repeat(32);
91
+ const sHex = '22'.repeat(32);
92
+ mockWalletFetch();
93
+ mockFetch.mockResolvedValueOnce({
94
+ json: async () => createUserActionInitResponse(),
95
+ ok: true,
96
+ });
97
+ mockFetch.mockResolvedValueOnce({
98
+ json: async () => createUserActionResponse(),
99
+ ok: true,
100
+ });
101
+ mockFetch.mockResolvedValueOnce({
102
+ json: async () => createSignatureResponse(rHex, sHex),
103
+ ok: true,
104
+ });
105
+ const signer = await DfnsSigner.create(defaultConfig);
106
+ const result = await signer.signMessages([{ content: new Uint8Array([1, 2, 3]), signatures: {} }]);
107
+ expect(result).toHaveLength(1);
108
+ expect(result[0]?.[signer.address]).toBeDefined();
109
+ const sig = result[0][signer.address];
110
+ expect(sig.length).toBe(64);
111
+ });
112
+ it('left-pads short signature components', async () => {
113
+ // r is 31 bytes (short by 1), s is 32 bytes
114
+ const rHex = 'ff'.repeat(31);
115
+ const sHex = 'aa'.repeat(32);
116
+ mockWalletFetch();
117
+ mockFetch.mockResolvedValueOnce({
118
+ json: async () => createUserActionInitResponse(),
119
+ ok: true,
120
+ });
121
+ mockFetch.mockResolvedValueOnce({
122
+ json: async () => createUserActionResponse(),
123
+ ok: true,
124
+ });
125
+ mockFetch.mockResolvedValueOnce({
126
+ json: async () => createSignatureResponse(rHex, sHex),
127
+ ok: true,
128
+ });
129
+ const signer = await DfnsSigner.create(defaultConfig);
130
+ const result = await signer.signMessages([{ content: new Uint8Array([1, 2, 3]), signatures: {} }]);
131
+ const sig = result[0][signer.address];
132
+ expect(sig.length).toBe(64);
133
+ // First byte should be 0x00 (left-pad), then 31 bytes of 0xff
134
+ expect(sig[0]).toBe(0x00);
135
+ expect(sig[1]).toBe(0xff);
136
+ });
137
+ });
138
+ describe('isAvailable', () => {
139
+ it('returns true when API responds', async () => {
140
+ mockWalletFetch();
141
+ // isAvailable doesn't need create(), but we need a signer instance
142
+ mockWalletFetch(); // for the isAvailable call
143
+ const signer = await DfnsSigner.create(defaultConfig);
144
+ expect(await signer.isAvailable()).toBe(true);
145
+ });
146
+ it('returns false when API fails', async () => {
147
+ mockWalletFetch(); // for create()
148
+ const signer = await DfnsSigner.create(defaultConfig);
149
+ mockFetch.mockResolvedValueOnce({
150
+ ok: false,
151
+ status: 500,
152
+ });
153
+ expect(await signer.isAvailable()).toBe(false);
154
+ });
155
+ });
156
+ });
157
+ //# sourceMappingURL=dfns-signer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dfns-signer.test.js","sourceRoot":"","sources":["../../src/__tests__/dfns-signer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { SolanaSigner } from '@solana/keychain-core';
2
+ import { SignerTestConfig, TestScenario } from '@solana/keychain-test-utils';
3
+ export declare const TEST_AUTH_TOKEN = "test-auth-token";
4
+ export declare const TEST_CRED_ID = "test-cred-id";
5
+ export declare const TEST_WALLET_ID = "test-wallet-id";
6
+ export declare const TEST_KEY_ID = "test-key-id";
7
+ export declare const TEST_ED25519_PEM = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikUmifl1yiWd+IiYyoHBD\n-----END PRIVATE KEY-----";
8
+ export declare const TEST_PUBKEY_HEX = "5da30b28c87836b0ee76ae7b07e3a2e3be1a4c12e48fce3aee18de0a13040b9a";
9
+ export declare function createWalletResponse(overrides?: Partial<{
10
+ status: string;
11
+ scheme: string;
12
+ curve: string;
13
+ }>): {
14
+ id: string;
15
+ network: string;
16
+ signingKey: {
17
+ id: string;
18
+ curve: string;
19
+ publicKey: string;
20
+ scheme: string;
21
+ };
22
+ status: string;
23
+ };
24
+ export declare function createUserActionInitResponse(): {
25
+ allowCredentials: {
26
+ key: {
27
+ id: string;
28
+ }[];
29
+ };
30
+ challenge: string;
31
+ challengeIdentifier: string;
32
+ };
33
+ export declare function createUserActionResponse(): {
34
+ userAction: string;
35
+ };
36
+ export declare function createSignatureResponse(r: string, s: string): {
37
+ id: string;
38
+ signature: {
39
+ r: string;
40
+ s: string;
41
+ };
42
+ status: string;
43
+ };
44
+ export declare function getConfig(scenarios: TestScenario[]): Promise<SignerTestConfig<SolanaSigner>>;
45
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG7E,eAAO,MAAM,eAAe,oBAAoB,CAAC;AACjD,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAC3C,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAC/C,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAGzC,eAAO,MAAM,gBAAgB,6HAEH,CAAC;AAE3B,eAAO,MAAM,eAAe,qEAAqE,CAAC;AAElG,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;;;;;;;;;;EAY1G;AAED,wBAAgB,4BAA4B;;;;;;;;EAQ3C;AAED,wBAAgB,wBAAwB;;EAIvC;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;;;;;;;EAM3D;AAkBD,wBAAsB,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAKlG"}
@@ -0,0 +1,64 @@
1
+ import { createDfnsSigner } from '../dfns-signer.js';
2
+ export const TEST_AUTH_TOKEN = 'test-auth-token';
3
+ export const TEST_CRED_ID = 'test-cred-id';
4
+ export const TEST_WALLET_ID = 'test-wallet-id';
5
+ export const TEST_KEY_ID = 'test-key-id';
6
+ // Ed25519 test key in PKCS#8 PEM format
7
+ export const TEST_ED25519_PEM = `-----BEGIN PRIVATE KEY-----
8
+ MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikUmifl1yiWd+IiYyoHBD
9
+ -----END PRIVATE KEY-----`;
10
+ export const TEST_PUBKEY_HEX = '5da30b28c87836b0ee76ae7b07e3a2e3be1a4c12e48fce3aee18de0a13040b9a';
11
+ export function createWalletResponse(overrides) {
12
+ return {
13
+ id: TEST_WALLET_ID,
14
+ network: 'Solana',
15
+ signingKey: {
16
+ id: TEST_KEY_ID,
17
+ curve: overrides?.curve ?? 'ed25519',
18
+ publicKey: TEST_PUBKEY_HEX,
19
+ scheme: overrides?.scheme ?? 'EdDSA',
20
+ },
21
+ status: overrides?.status ?? 'Active',
22
+ };
23
+ }
24
+ export function createUserActionInitResponse() {
25
+ return {
26
+ allowCredentials: {
27
+ key: [{ id: TEST_CRED_ID }],
28
+ },
29
+ challenge: 'test-challenge',
30
+ challengeIdentifier: 'test-challenge-id',
31
+ };
32
+ }
33
+ export function createUserActionResponse() {
34
+ return {
35
+ userAction: 'test-user-action-token',
36
+ };
37
+ }
38
+ export function createSignatureResponse(r, s) {
39
+ return {
40
+ id: 'sig-123',
41
+ signature: { r, s },
42
+ status: 'Signed',
43
+ };
44
+ }
45
+ const SIGNER_TYPE = 'dfns';
46
+ const REQUIRED_ENV_VARS = ['DFNS_AUTH_TOKEN', 'DFNS_CRED_ID', 'DFNS_PRIVATE_KEY_PEM', 'DFNS_WALLET_ID'];
47
+ const CONFIG = {
48
+ signerType: SIGNER_TYPE,
49
+ requiredEnvVars: REQUIRED_ENV_VARS,
50
+ createSigner: () => createDfnsSigner({
51
+ authToken: process.env.DFNS_AUTH_TOKEN,
52
+ credId: process.env.DFNS_CRED_ID,
53
+ privateKeyPem: process.env.DFNS_PRIVATE_KEY_PEM,
54
+ walletId: process.env.DFNS_WALLET_ID,
55
+ apiBaseUrl: process.env.DFNS_API_BASE_URL,
56
+ }),
57
+ };
58
+ export async function getConfig(scenarios) {
59
+ return {
60
+ ...CONFIG,
61
+ testScenarios: scenarios,
62
+ };
63
+ }
64
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AACjD,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC;AAC3C,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AAEzC,wCAAwC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG;;0BAEN,CAAC;AAE3B,MAAM,CAAC,MAAM,eAAe,GAAG,kEAAkE,CAAC;AAElG,MAAM,UAAU,oBAAoB,CAAC,SAAsE;IACvG,OAAO;QACH,EAAE,EAAE,cAAc;QAClB,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE;YACR,EAAE,EAAE,WAAW;YACf,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,SAAS;YACpC,SAAS,EAAE,eAAe;YAC1B,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO;SACvC;QACD,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,QAAQ;KACxC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,4BAA4B;IACxC,OAAO;QACH,gBAAgB,EAAE;YACd,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;SAC9B;QACD,SAAS,EAAE,gBAAgB;QAC3B,mBAAmB,EAAE,mBAAmB;KAC3C,CAAC;AACN,CAAC;AAED,MAAM,UAAU,wBAAwB;IACpC,OAAO;QACH,UAAU,EAAE,wBAAwB;KACvC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,CAAS,EAAE,CAAS;IACxD,OAAO;QACH,EAAE,EAAE,SAAS;QACb,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE;QACnB,MAAM,EAAE,QAAQ;KACnB,CAAC;AACN,CAAC;AAED,MAAM,WAAW,GAAG,MAAM,CAAC;AAC3B,MAAM,iBAAiB,GAAG,CAAC,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,gBAAgB,CAAC,CAAC;AAExG,MAAM,MAAM,GAAmC;IAC3C,UAAU,EAAE,WAAW;IACvB,eAAe,EAAE,iBAAiB;IAClC,YAAY,EAAE,GAAG,EAAE,CACf,gBAAgB,CAAC;QACb,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,eAAgB;QACvC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAa;QACjC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAqB;QAChD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAe;QACrC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;KAC5C,CAAC;CACT,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAyB;IACrD,OAAO;QACH,GAAG,MAAM;QACT,aAAa,EAAE,SAAS;KAC3B,CAAC;AACN,CAAC"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Perform the Dfns User Action Signing flow. For more details, see https://docs.dfns.co/api-reference/auth/signing-flows#asymetric-keys-signing-flow
3
+ *
4
+ * @returns The `userAction` token to include as `x-dfns-useraction` header.
5
+ */
6
+ export declare function signUserAction(apiBaseUrl: string, authToken: string, credId: string, privateKeyPem: string, httpMethod: string, httpPath: string, body: string): Promise<string>;
7
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAsB,cAAc,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAoHjB"}
package/dist/auth.js ADDED
@@ -0,0 +1,117 @@
1
+ import * as crypto from 'node:crypto';
2
+ import { base64UrlDecoder, SignerErrorCode, throwSignerError } from '@solana/keychain-core';
3
+ /**
4
+ * Perform the Dfns User Action Signing flow. For more details, see https://docs.dfns.co/api-reference/auth/signing-flows#asymetric-keys-signing-flow
5
+ *
6
+ * @returns The `userAction` token to include as `x-dfns-useraction` header.
7
+ */
8
+ export async function signUserAction(apiBaseUrl, authToken, credId, privateKeyPem, httpMethod, httpPath, body) {
9
+ // Request a challenge
10
+ const initUrl = `${apiBaseUrl}/auth/action/init`;
11
+ let initResponse;
12
+ try {
13
+ initResponse = await fetch(initUrl, {
14
+ body: JSON.stringify({
15
+ userActionHttpMethod: httpMethod,
16
+ userActionHttpPath: httpPath,
17
+ userActionPayload: body,
18
+ userActionServerKind: 'Api',
19
+ }),
20
+ headers: {
21
+ Authorization: `Bearer ${authToken}`,
22
+ 'Content-Type': 'application/json',
23
+ },
24
+ method: 'POST',
25
+ });
26
+ }
27
+ catch (error) {
28
+ throwSignerError(SignerErrorCode.HTTP_ERROR, {
29
+ cause: error,
30
+ message: 'Dfns network request failed',
31
+ url: initUrl,
32
+ });
33
+ }
34
+ if (!initResponse.ok) {
35
+ const errorText = await initResponse.text().catch(() => 'Failed to read error response');
36
+ throwSignerError(SignerErrorCode.REMOTE_API_ERROR, {
37
+ message: `Dfns auth/action/init failed: ${initResponse.status}`,
38
+ response: errorText,
39
+ status: initResponse.status,
40
+ });
41
+ }
42
+ let challenge;
43
+ try {
44
+ challenge = (await initResponse.json());
45
+ }
46
+ catch (error) {
47
+ throwSignerError(SignerErrorCode.PARSING_ERROR, {
48
+ cause: error,
49
+ message: 'Failed to parse Dfns auth challenge response',
50
+ });
51
+ }
52
+ // Verify credential is allowed
53
+ const allowed = challenge.allowCredentials.key.some(c => c.id === credId);
54
+ if (!allowed) {
55
+ throwSignerError(SignerErrorCode.CONFIG_ERROR, {
56
+ message: `Credential ${credId} not in allowed credentials`,
57
+ });
58
+ }
59
+ // Sign the challenge
60
+ const clientData = new TextEncoder().encode(JSON.stringify({
61
+ challenge: challenge.challenge,
62
+ type: 'key.get',
63
+ }));
64
+ const signature = crypto.sign(undefined, clientData, privateKeyPem);
65
+ const clientDataB64 = base64UrlDecoder(clientData);
66
+ const signatureB64 = base64UrlDecoder(new Uint8Array(signature));
67
+ // Submit the signed challenge
68
+ const actionUrl = `${apiBaseUrl}/auth/action`;
69
+ let signResponse;
70
+ try {
71
+ signResponse = await fetch(actionUrl, {
72
+ body: JSON.stringify({
73
+ challengeIdentifier: challenge.challengeIdentifier,
74
+ firstFactor: {
75
+ credentialAssertion: {
76
+ clientData: clientDataB64,
77
+ credId,
78
+ signature: signatureB64,
79
+ },
80
+ kind: 'Key',
81
+ },
82
+ }),
83
+ headers: {
84
+ Authorization: `Bearer ${authToken}`,
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ method: 'POST',
88
+ });
89
+ }
90
+ catch (error) {
91
+ throwSignerError(SignerErrorCode.HTTP_ERROR, {
92
+ cause: error,
93
+ message: 'Dfns network request failed',
94
+ url: actionUrl,
95
+ });
96
+ }
97
+ if (!signResponse.ok) {
98
+ const errorText = await signResponse.text().catch(() => 'Failed to read error response');
99
+ throwSignerError(SignerErrorCode.REMOTE_API_ERROR, {
100
+ message: `Dfns auth/action failed: ${signResponse.status}`,
101
+ response: errorText,
102
+ status: signResponse.status,
103
+ });
104
+ }
105
+ let actionResponse;
106
+ try {
107
+ actionResponse = (await signResponse.json());
108
+ }
109
+ catch (error) {
110
+ throwSignerError(SignerErrorCode.PARSING_ERROR, {
111
+ cause: error,
112
+ message: 'Failed to parse Dfns auth action response',
113
+ });
114
+ }
115
+ return actionResponse.userAction;
116
+ }
117
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}