inslash 1.1.1 → 1.2.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 (3) hide show
  1. package/README.md +36 -0
  2. package/index.js +116 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -35,8 +35,44 @@ const verifyResult = await verify("myPassword", result.passport, secret);
35
35
  console.log(verifyResult.valid); // true or false
36
36
  ```
37
37
 
38
+ ## API Mode (Hosted Hashing)
39
+
40
+ `inslash` can connect to a hosted API for password hashing, with automatic fallback to local crypto if the API is unavailable.
41
+
42
+ ### Setup
43
+
44
+ ```js
45
+ const inslash = require('inslash');
46
+
47
+ // Configure once with your API key
48
+ inslash.configure({
49
+ apiKey: 'inslash_your_api_key_here',
50
+ apiUrl: 'https://api.inslash.com' // or http://localhost:3001 for testing
51
+ });
52
+
53
+ // Now hash() and verify() automatically use the API
54
+ const result = await inslash.hash('myPassword');
55
+ const verified = await inslash.verify('myPassword', result.passport);
56
+ ```
57
+
58
+ ### How It Works
59
+
60
+ 1. **API First**: When configured, `hash()` and `verify()` call your hosted API
61
+ 2. **Silent Fallback**: If the API is down or slow, falls back to local crypto automatically
62
+ 3. **Zero Config Local**: If not configured, uses local crypto only (no secret needed from you)
63
+
64
+ ### Get an API Key
65
+
66
+ Visit [https://inslash.com](https://inslash.com) to create a project and get your API key.
67
+
38
68
  ## API
39
69
 
70
+ ### `configure(options)`
71
+ - `options.apiKey` (string): Your Inslash API key.
72
+ - `options.apiUrl` (string): API endpoint URL.
73
+ - **Returns:** Current configuration object.
74
+ - **Note:** Call this once before using `hash()` or `verify()` to enable API mode.
75
+
40
76
  ### `async hash(value, secret, options?)`
41
77
  - `value` (string): The value to hash.
42
78
  - `secret` (string): Secret key for HMAC.
package/index.js CHANGED
@@ -1,4 +1,13 @@
1
1
  const crypto = require("crypto");
2
+ const https = require("https");
3
+ const http = require("http");
4
+
5
+ // Module-level configuration for API mode
6
+ let CONFIG = {
7
+ apiKey: null,
8
+ apiUrl: null,
9
+ strictMode: false // If true, throw error instead of falling back to local
10
+ };
2
11
 
3
12
  const DEFAULTS = {
4
13
  saltLength: 16,
@@ -108,11 +117,90 @@ const decodePassport = (passport) => {
108
117
  }
109
118
  };
110
119
 
111
- // Enhanced: Hash function with more options
120
+ // Helper: Call API endpoint
121
+ const callAPI = (endpoint, body) => {
122
+ return new Promise((resolve, reject) => {
123
+ const url = new URL(endpoint, CONFIG.apiUrl);
124
+ const client = url.protocol === 'https:' ? https : http;
125
+
126
+ const postData = JSON.stringify(body);
127
+ const options = {
128
+ hostname: url.hostname,
129
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
130
+ path: url.pathname,
131
+ method: 'POST',
132
+ headers: {
133
+ 'Content-Type': 'application/json',
134
+ 'x-api-key': CONFIG.apiKey,
135
+ 'Content-Length': Buffer.byteLength(postData)
136
+ },
137
+ timeout: 10000
138
+ };
139
+
140
+ const req = client.request(options, (res) => {
141
+ let data = '';
142
+ res.on('data', chunk => data += chunk);
143
+ res.on('end', () => {
144
+ if (res.statusCode >= 200 && res.statusCode < 300) {
145
+ try {
146
+ resolve(JSON.parse(data));
147
+ } catch (e) {
148
+ reject(new Error('Invalid JSON response'));
149
+ }
150
+ } else {
151
+ reject(new Error(`API error: ${res.statusCode}`));
152
+ }
153
+ });
154
+ });
155
+
156
+ req.on('error', reject);
157
+ req.on('timeout', () => {
158
+ req.destroy();
159
+ reject(new Error('API request timeout'));
160
+ });
161
+
162
+ req.write(postData);
163
+ req.end();
164
+ });
165
+ };
166
+
167
+ // Configure API mode
168
+ const configure = (options = {}) => {
169
+ const { apiKey, apiUrl, strictMode } = options;
170
+
171
+ if (apiKey) CONFIG.apiKey = apiKey;
172
+ if (apiUrl) CONFIG.apiUrl = apiUrl;
173
+ if (typeof strictMode !== 'undefined') CONFIG.strictMode = strictMode;
174
+
175
+ return CONFIG;
176
+ };
177
+
178
+ // Enhanced: Hash function with more options and API support
112
179
  const hash = async (value, secret, opts = {}) => {
113
- if (!secret) throw new Error("Secret key is required");
114
180
  if (typeof value !== "string" || !value) throw new Error("Value to hash must be a non-empty string");
115
181
 
182
+ // API Mode: Try API first if configured
183
+ if (CONFIG.apiKey && CONFIG.apiUrl) {
184
+ try {
185
+ const apiResult = await callAPI('/api/hash', {
186
+ value,
187
+ secret,
188
+ options: opts
189
+ });
190
+ return apiResult;
191
+ } catch (error) {
192
+ // In strict mode, throw the error instead of falling back
193
+ if (CONFIG.strictMode) {
194
+ throw new Error(`API hash failed: ${error.message}`);
195
+ }
196
+ // Silent fallback to local crypto
197
+ console.warn('API hash failed, falling back to local:', error.message);
198
+ }
199
+ }
200
+
201
+ // Local Mode: Original crypto implementation
202
+ if (!secret) throw new Error("Secret key is required");
203
+
116
204
  // Validate algorithm
117
205
  if (opts.algorithm && !SUPPORTED_ALGORITHMS.includes(opts.algorithm)) {
118
206
  throw new Error(`Unsupported algorithm: ${opts.algorithm}. Supported: ${SUPPORTED_ALGORITHMS.join(", ")}`);
@@ -155,8 +243,29 @@ const hash = async (value, secret, opts = {}) => {
155
243
  };
156
244
  };
157
245
 
158
- // Enhanced: Verify with more detailed response
246
+ // Enhanced: Verify with more detailed response and API support
159
247
  const verify = async (value, passport, secret, opts = {}) => {
248
+ // API Mode: Try API first if configured
249
+ if (CONFIG.apiKey && CONFIG.apiUrl) {
250
+ try {
251
+ const apiResult = await callAPI('/api/verify', {
252
+ value,
253
+ passport,
254
+ secret,
255
+ options: opts
256
+ });
257
+ return apiResult;
258
+ } catch (error) {
259
+ // In strict mode, throw the error instead of falling back
260
+ if (CONFIG.strictMode) {
261
+ throw new Error(`API verify failed: ${error.message}`);
262
+ }
263
+ // Silent fallback to local crypto
264
+ console.warn('API verify failed, falling back to local:', error.message);
265
+ }
266
+ }
267
+
268
+ // Local Mode: Original crypto implementation
160
269
  const meta = decodePassport(passport);
161
270
  const options = {
162
271
  algorithm: meta.algorithm,
@@ -372,6 +481,9 @@ module.exports = {
372
481
  encodePassport,
373
482
  decodePassport,
374
483
 
484
+ // API configuration
485
+ configure,
486
+
375
487
  // New enhanced functions
376
488
  batchVerify,
377
489
  inspectPassport,
@@ -385,5 +497,5 @@ module.exports = {
385
497
  SUPPORTED_ENCODINGS,
386
498
 
387
499
  // Version info
388
- VERSION: "1.1.0"
500
+ VERSION: "1.2.0"
389
501
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inslash",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "A modern, upgradeable, and secure password hashing utility with passport encoding, hash ancestry, and comprehensive security features.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",