@phantom/api-key-stamper 0.0.2
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.
- package/README.md +120 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +67 -0
- package/dist/index.mjs +32 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# @phantom/api-key-stamper
|
|
2
|
+
|
|
3
|
+
API key stamper for authenticating requests to Phantom Wallet API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @phantom/api-key-stamper
|
|
9
|
+
# or
|
|
10
|
+
yarn add @phantom/api-key-stamper
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
The `ApiKeyStamper` is used to sign HTTP requests with Ed25519 signatures for authentication with Phantom's API.
|
|
16
|
+
|
|
17
|
+
### Basic Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { ApiKeyStamper } from "@phantom/api-key-stamper";
|
|
21
|
+
import { PhantomClient } from "@phantom/client";
|
|
22
|
+
|
|
23
|
+
// Create a stamper with your secret key
|
|
24
|
+
const stamper = new ApiKeyStamper({
|
|
25
|
+
apiSecretKey: "your-base58-encoded-secret-key",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Use it with PhantomClient
|
|
29
|
+
const client = new PhantomClient(
|
|
30
|
+
{
|
|
31
|
+
apiBaseUrl: "https://api.phantom.com",
|
|
32
|
+
organizationId: "your-org-id",
|
|
33
|
+
},
|
|
34
|
+
stamper,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Now all requests will be automatically signed
|
|
38
|
+
const wallet = await client.createWallet("My Wallet");
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With Server SDK
|
|
42
|
+
|
|
43
|
+
The `@phantom/server-sdk` package uses this stamper internally:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { ServerSDK } from "@phantom/server-sdk";
|
|
47
|
+
|
|
48
|
+
const sdk = new ServerSDK({
|
|
49
|
+
organizationId: "your-org-id",
|
|
50
|
+
apiBaseUrl: "https://api.phantom.com",
|
|
51
|
+
apiPrivateKey: "your-base58-encoded-secret-key",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// The SDK automatically creates and uses an ApiKeyStamper
|
|
55
|
+
const wallet = await sdk.createWallet("My Wallet");
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### How it Works
|
|
59
|
+
|
|
60
|
+
1. The stamper takes your base58-encoded Ed25519 secret key
|
|
61
|
+
2. For each request, it signs the request body with the secret key
|
|
62
|
+
3. The stamp is added to the request headers as `X-Phantom-Stamp` containing:
|
|
63
|
+
- `publicKey`: Base64url-encoded public key
|
|
64
|
+
- `signature`: Base64url-encoded signature
|
|
65
|
+
- `kind`: Always "PKI" for this authentication method
|
|
66
|
+
4. The entire stamp object is JSON-encoded and then base64url-encoded
|
|
67
|
+
5. The server verifies the signature to authenticate the request
|
|
68
|
+
|
|
69
|
+
#### Example Stamp Structure
|
|
70
|
+
|
|
71
|
+
Before encoding, the stamp object looks like:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"publicKey": "base64url-encoded-public-key",
|
|
76
|
+
"signature": "base64url-encoded-signature",
|
|
77
|
+
"kind": "PKI"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This JSON is then base64url-encoded and sent as the `X-Phantom-Stamp` header.
|
|
82
|
+
|
|
83
|
+
### Security Notes
|
|
84
|
+
|
|
85
|
+
- Keep your secret key secure and never expose it in client-side code
|
|
86
|
+
- Only use this stamper in server-side applications
|
|
87
|
+
- The secret key should be stored securely (e.g., in environment variables)
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
### Running Tests
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm test
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The test suite includes:
|
|
98
|
+
|
|
99
|
+
- Constructor validation with valid/invalid keys
|
|
100
|
+
- Header addition and preservation
|
|
101
|
+
- Different data types (string, object, undefined)
|
|
102
|
+
- Stamp structure validation
|
|
103
|
+
- Cryptographic signature verification
|
|
104
|
+
- Base64url encoding/decoding utilities
|
|
105
|
+
- Edge cases and error handling
|
|
106
|
+
|
|
107
|
+
## API Reference
|
|
108
|
+
|
|
109
|
+
### ApiKeyStamper
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
class ApiKeyStamper {
|
|
113
|
+
constructor(config: ApiKeyStamperConfig);
|
|
114
|
+
stamp(config: AxiosRequestConfig): Promise<AxiosRequestConfig>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface ApiKeyStamperConfig {
|
|
118
|
+
apiSecretKey: string; // Base58-encoded Ed25519 secret key
|
|
119
|
+
}
|
|
120
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
|
|
3
|
+
interface ApiKeyStamperConfig {
|
|
4
|
+
apiSecretKey: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* ApiKeyStamper that signs requests with Ed25519
|
|
8
|
+
*/
|
|
9
|
+
declare class ApiKeyStamper {
|
|
10
|
+
private keypair;
|
|
11
|
+
constructor(config: ApiKeyStamperConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Stamp (sign) an axios request configuration
|
|
14
|
+
*/
|
|
15
|
+
stamp(config: AxiosRequestConfig): Promise<AxiosRequestConfig>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { ApiKeyStamper, ApiKeyStamperConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
ApiKeyStamper: () => ApiKeyStamper
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_buffer = require("buffer");
|
|
37
|
+
var import_base64url = require("@phantom/base64url");
|
|
38
|
+
var import_crypto = require("@phantom/crypto");
|
|
39
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
40
|
+
var ApiKeyStamper = class {
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.keypair = (0, import_crypto.createKeyPairFromSecret)(config.apiSecretKey);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Stamp (sign) an axios request configuration
|
|
46
|
+
*/
|
|
47
|
+
async stamp(config) {
|
|
48
|
+
const requestBody = typeof config.data === "string" ? config.data : config.data === void 0 ? "" : JSON.stringify(config.data);
|
|
49
|
+
const dataUtf8 = import_buffer.Buffer.from(requestBody, "utf8");
|
|
50
|
+
const signature = (0, import_crypto.signWithSecret)(this.keypair.secretKey, dataUtf8);
|
|
51
|
+
const stampData = {
|
|
52
|
+
// The keypair is bs58 encoded, need to decode and then base64url encode the public key
|
|
53
|
+
publicKey: (0, import_base64url.base64urlEncode)(import_bs58.default.decode(this.keypair.publicKey)),
|
|
54
|
+
signature: (0, import_base64url.base64urlEncode)(signature),
|
|
55
|
+
kind: "PKI"
|
|
56
|
+
};
|
|
57
|
+
const stampJson = JSON.stringify(stampData);
|
|
58
|
+
const stamp = (0, import_base64url.stringToBase64url)(stampJson);
|
|
59
|
+
config.headers = config.headers || {};
|
|
60
|
+
config.headers["X-Phantom-Stamp"] = stamp;
|
|
61
|
+
return Promise.resolve(config);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
65
|
+
0 && (module.exports = {
|
|
66
|
+
ApiKeyStamper
|
|
67
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Buffer } from "buffer";
|
|
3
|
+
import { base64urlEncode, stringToBase64url } from "@phantom/base64url";
|
|
4
|
+
import { createKeyPairFromSecret, signWithSecret } from "@phantom/crypto";
|
|
5
|
+
import bs58 from "bs58";
|
|
6
|
+
var ApiKeyStamper = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.keypair = createKeyPairFromSecret(config.apiSecretKey);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Stamp (sign) an axios request configuration
|
|
12
|
+
*/
|
|
13
|
+
async stamp(config) {
|
|
14
|
+
const requestBody = typeof config.data === "string" ? config.data : config.data === void 0 ? "" : JSON.stringify(config.data);
|
|
15
|
+
const dataUtf8 = Buffer.from(requestBody, "utf8");
|
|
16
|
+
const signature = signWithSecret(this.keypair.secretKey, dataUtf8);
|
|
17
|
+
const stampData = {
|
|
18
|
+
// The keypair is bs58 encoded, need to decode and then base64url encode the public key
|
|
19
|
+
publicKey: base64urlEncode(bs58.decode(this.keypair.publicKey)),
|
|
20
|
+
signature: base64urlEncode(signature),
|
|
21
|
+
kind: "PKI"
|
|
22
|
+
};
|
|
23
|
+
const stampJson = JSON.stringify(stampData);
|
|
24
|
+
const stamp = stringToBase64url(stampJson);
|
|
25
|
+
config.headers = config.headers || {};
|
|
26
|
+
config.headers["X-Phantom-Stamp"] = stamp;
|
|
27
|
+
return Promise.resolve(config);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export {
|
|
31
|
+
ApiKeyStamper
|
|
32
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@phantom/api-key-stamper",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "API key stamper for Phantom Wallet SDK",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --external buffer,@phantom/base64url,@phantom/crypto",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"test": "jest",
|
|
20
|
+
"test:watch": "jest --watch",
|
|
21
|
+
"lint": "tsc --noEmit && eslint --cache . --ext .ts,.tsx",
|
|
22
|
+
"prettier": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/bs58": "^4.0.1",
|
|
26
|
+
"@types/jest": "^29.5.12",
|
|
27
|
+
"@types/node": "^20.11.0",
|
|
28
|
+
"eslint": "8.53.0",
|
|
29
|
+
"jest": "^29.7.0",
|
|
30
|
+
"prettier": "^3.5.2",
|
|
31
|
+
"rimraf": "^6.0.1",
|
|
32
|
+
"ts-jest": "^29.1.2",
|
|
33
|
+
"tsup": "^6.7.0",
|
|
34
|
+
"typescript": "^5.0.4"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@phantom/base64url": "workspace:^",
|
|
38
|
+
"@phantom/crypto": "workspace:^",
|
|
39
|
+
"axios": "^1.10.0",
|
|
40
|
+
"bs58": "^6.0.0",
|
|
41
|
+
"buffer": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
]
|
|
46
|
+
}
|