@archlast/shared 0.0.1 → 0.1.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 (2) hide show
  1. package/README.md +265 -5
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,10 +1,270 @@
1
1
  # @archlast/shared
2
2
 
3
- Shared utilities used by Archlast packages.
3
+ Internal shared utilities for Archlast packages. This package provides token obfuscation, validation helpers, and shared constants used across the Archlast ecosystem.
4
4
 
5
- This package is primarily an internal dependency. Most users should install
6
- `@archlast/client`, `@archlast/server`, or `@archlast/cli` instead.
5
+ > ⚠️ **Internal Package**: Most applications should use `@archlast/client`, `@archlast/server`, or `@archlast/cli` instead. This package is primarily for internal use by other Archlast packages.
7
6
 
8
- ## Publishing (maintainers)
7
+ ## Table of Contents
9
8
 
10
- See `docs/npm-publishing.md` for release and publish steps.
9
+ - [Installation](#installation)
10
+ - [Features](#features)
11
+ - [Token Obfuscation](#token-obfuscation)
12
+ - [Token Format](#token-format)
13
+ - [API Reference](#api-reference)
14
+ - [Configuration](#configuration)
15
+ - [Security Considerations](#security-considerations)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @archlast/shared
21
+
22
+ # Or with other package managers
23
+ pnpm add @archlast/shared
24
+ bun add @archlast/shared
25
+ ```
26
+
27
+ ## Features
28
+
29
+ - **Token Obfuscation**: Secure token wrapping with HMAC signatures
30
+ - **Token Validation**: Verify and extract obfuscated tokens
31
+ - **Path-Bound Tokens**: Tokens are bound to specific API paths
32
+ - **Timestamp Validation**: Short validity window prevents replay attacks
33
+ - **Shared Constants**: Common constants used across packages
34
+
35
+ ## Token Obfuscation
36
+
37
+ Token obfuscation protects sensitive tokens (API keys, session tokens) in transit by:
38
+
39
+ 1. Adding a timestamp for time-limited validity
40
+ 2. Including a random nonce for uniqueness
41
+ 3. Computing an HMAC signature bound to the request path
42
+ 4. Encoding the result in a structured format
43
+
44
+ ### Why Obfuscate?
45
+
46
+ - **Path Binding**: A token obfuscated for `/api/users` cannot be reused for `/api/admin`
47
+ - **Time Limiting**: Tokens expire after a short window (default: 5 minutes)
48
+ - **Replay Prevention**: Random nonce ensures each request is unique
49
+ - **Signature Verification**: HMAC ensures token integrity
50
+
51
+ ## Token Format
52
+
53
+ Obfuscated tokens follow this structure:
54
+
55
+ ```
56
+ v1:<timestamp>:<nonce>:<signature>:<token>
57
+ ```
58
+
59
+ | Component | Description |
60
+ |-----------|-------------|
61
+ | `v1` | Version identifier for future format changes |
62
+ | `timestamp` | Unix milliseconds when token was obfuscated |
63
+ | `nonce` | Random 16-character string |
64
+ | `signature` | HMAC-SHA256 of `token:timestamp:nonce:path` |
65
+ | `token` | The original raw token |
66
+
67
+ ### Example
68
+
69
+ ```
70
+ v1:1704067200000:a1b2c3d4e5f6g7h8:8f4e2c1a9b7d5e3f1a2b4c6d8e0f2a4b6:arch_sk_live_xxx
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### `obfuscateToken(token, path, options?)`
76
+
77
+ Obfuscate a raw token for a specific API path.
78
+
79
+ ```ts
80
+ import { obfuscateToken } from "@archlast/shared";
81
+
82
+ const obfuscated = obfuscateToken(
83
+ "arch_sk_live_abc123",
84
+ "/_archlast/admin/auth/me"
85
+ );
86
+
87
+ // Result: "v1:1704067200000:a1b2c3d4e5f6g7h8:..."
88
+ ```
89
+
90
+ **Parameters:**
91
+
92
+ | Parameter | Type | Description |
93
+ |-----------|------|-------------|
94
+ | `token` | `string` | Raw token to obfuscate |
95
+ | `path` | `string` | API path the token is bound to |
96
+ | `options.secret` | `string?` | Custom HMAC secret (default: env var) |
97
+ | `options.timestamp` | `number?` | Custom timestamp (default: now) |
98
+
99
+ ### `deobfuscateToken(obfuscated, path, options?)`
100
+
101
+ Verify and extract a token from its obfuscated form.
102
+
103
+ ```ts
104
+ import { deobfuscateToken } from "@archlast/shared";
105
+
106
+ const result = deobfuscateToken(
107
+ "v1:1704067200000:...",
108
+ "/_archlast/admin/auth/me"
109
+ );
110
+
111
+ if (result.valid) {
112
+ console.log("Token:", result.token);
113
+ } else {
114
+ console.error("Invalid:", result.error);
115
+ }
116
+ ```
117
+
118
+ **Returns:**
119
+
120
+ ```ts
121
+ // Success
122
+ { valid: true, token: string }
123
+
124
+ // Failure
125
+ { valid: false, error: string }
126
+ ```
127
+
128
+ **Possible Errors:**
129
+
130
+ | Error | Description |
131
+ |-------|-------------|
132
+ | `"Invalid format"` | Token doesn't match expected structure |
133
+ | `"Unsupported version"` | Version prefix is not recognized |
134
+ | `"Token expired"` | Timestamp is outside validity window |
135
+ | `"Invalid signature"` | HMAC verification failed |
136
+ | `"Path mismatch"` | Token was created for a different path |
137
+
138
+ ### `extractRawToken(obfuscated, path, options?)`
139
+
140
+ Convenience wrapper that returns the raw token or throws.
141
+
142
+ ```ts
143
+ import { extractRawToken } from "@archlast/shared";
144
+
145
+ try {
146
+ const rawToken = extractRawToken(obfuscated, path);
147
+ // Use rawToken...
148
+ } catch (error) {
149
+ console.error("Token validation failed:", error.message);
150
+ }
151
+ ```
152
+
153
+ ### `isObfuscatedToken(value)`
154
+
155
+ Check if a string looks like an obfuscated token.
156
+
157
+ ```ts
158
+ import { isObfuscatedToken } from "@archlast/shared";
159
+
160
+ isObfuscatedToken("v1:123:abc:def:token"); // true
161
+ isObfuscatedToken("arch_sk_live_xxx"); // false
162
+ ```
163
+
164
+ ## Configuration
165
+
166
+ ### Environment Variables
167
+
168
+ | Variable | Default | Description |
169
+ |----------|---------|-------------|
170
+ | `ARCHLAST_TOKEN_OBFUSCATION_SECRET` | - | **Required in production.** HMAC secret for signatures. |
171
+ | `ARCHLAST_TOKEN_VALIDITY_MS` | `300000` | Token validity window (5 minutes) |
172
+
173
+ ### Setting the Secret
174
+
175
+ ```bash
176
+ # Generate a strong secret
177
+ openssl rand -base64 32
178
+
179
+ # Set in environment
180
+ export ARCHLAST_TOKEN_OBFUSCATION_SECRET="your-32-char-random-secret-here"
181
+ ```
182
+
183
+ ### Custom Options
184
+
185
+ ```ts
186
+ const obfuscated = obfuscateToken(token, path, {
187
+ secret: "custom-secret",
188
+ timestamp: Date.now(),
189
+ });
190
+
191
+ const result = deobfuscateToken(obfuscated, path, {
192
+ secret: "custom-secret",
193
+ validityMs: 60000, // 1 minute
194
+ });
195
+ ```
196
+
197
+ ## Security Considerations
198
+
199
+ ### Production Requirements
200
+
201
+ 1. **Set a Strong Secret**: Always set `ARCHLAST_TOKEN_OBFUSCATION_SECRET` in production
202
+ 2. **Keep Clocks Synced**: Use NTP to keep server clocks synchronized
203
+ 3. **Use HTTPS**: Always transmit tokens over HTTPS
204
+ 4. **Short Validity**: Keep the validity window short (default: 5 minutes)
205
+
206
+ ### Best Practices
207
+
208
+ ```ts
209
+ // ✅ Good: Obfuscate before sending
210
+ const headers = {
211
+ Authorization: `Bearer ${obfuscateToken(apiKey, requestPath)}`
212
+ };
213
+
214
+ // ❌ Bad: Sending raw tokens in headers
215
+ const headers = {
216
+ Authorization: `Bearer ${apiKey}`
217
+ };
218
+ ```
219
+
220
+ ### Clock Skew
221
+
222
+ The validation allows for some clock skew between client and server:
223
+
224
+ ```ts
225
+ // Token is valid if:
226
+ // (serverTime - validityMs) < tokenTimestamp < (serverTime + clockSkew)
227
+ ```
228
+
229
+ Default clock skew allowance: 30 seconds.
230
+
231
+ ## Internal Usage
232
+
233
+ This package is used internally by:
234
+
235
+ - **@archlast/server**: Validating incoming requests
236
+ - **@archlast/client**: Preparing authenticated requests
237
+ - **@archlast/cli**: API key handling for deployments
238
+
239
+ ### Example: Server Middleware
240
+
241
+ ```ts
242
+ // Internal usage in @archlast/server
243
+ import { deobfuscateToken } from "@archlast/shared";
244
+
245
+ function authMiddleware(req, res, next) {
246
+ const authHeader = req.headers.authorization;
247
+ const token = authHeader?.replace("Bearer ", "");
248
+
249
+ if (!token) {
250
+ return res.status(401).json({ error: "No token" });
251
+ }
252
+
253
+ const result = deobfuscateToken(token, req.path);
254
+
255
+ if (!result.valid) {
256
+ return res.status(401).json({ error: result.error });
257
+ }
258
+
259
+ req.apiKey = result.token;
260
+ next();
261
+ }
262
+ ```
263
+
264
+ ## Keywords
265
+
266
+ archlast, shared, utilities, typescript, token, obfuscation
267
+
268
+ ## License
269
+
270
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archlast/shared",
3
- "version": "0.0.1",
3
+ "version": "0.1.1",
4
4
  "description": "Shared utilities for Archlast packages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",