hivesigner 3.3.5 → 4.1.0

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 (58) hide show
  1. package/README.md +29 -2
  2. package/lib/client.d.ts +19 -3
  3. package/lib/hivesigner.min.js +1 -10
  4. package/lib/index.d.ts +4 -0
  5. package/lib/index.js +194 -225
  6. package/lib/methods/send-operation.method.d.ts +1 -2
  7. package/lib/methods/send-operations.method.d.ts +1 -2
  8. package/lib/methods/send-transaction.method.d.ts +1 -2
  9. package/lib/pkce.d.ts +16 -0
  10. package/lib/types/callback-function.type.d.ts +1 -1
  11. package/lib/types/hive.types.d.ts +13 -0
  12. package/lib/types/index.d.ts +2 -0
  13. package/lib/types/login-options.interface.d.ts +2 -0
  14. package/lib/types/send-response.type.d.ts +1 -1
  15. package/lib/types/token-response.interface.d.ts +8 -0
  16. package/package.json +11 -18
  17. package/.babelrc +0 -8
  18. package/.claude/settings.local.json +0 -12
  19. package/.eslintignore +0 -3
  20. package/.eslintrc.js +0 -16
  21. package/CLAUDE.md +0 -142
  22. package/docs/CNAME +0 -1
  23. package/docs/index.html +0 -221
  24. package/jest.config.js +0 -10
  25. package/rollup.config.js +0 -12
  26. package/src/client.spec.ts +0 -455
  27. package/src/client.ts +0 -317
  28. package/src/consts/api-url.const.ts +0 -1
  29. package/src/consts/base-url.const.ts +0 -1
  30. package/src/consts/beta-url.const.ts +0 -1
  31. package/src/consts/index.ts +0 -3
  32. package/src/default-import.spec.ts +0 -10
  33. package/src/factories/index.ts +0 -1
  34. package/src/factories/initialize.factory.spec.ts +0 -9
  35. package/src/factories/initialize.factory.ts +0 -7
  36. package/src/index.ts +0 -16
  37. package/src/methods/index.ts +0 -4
  38. package/src/methods/send-operation.method.spec.ts +0 -47
  39. package/src/methods/send-operation.method.ts +0 -15
  40. package/src/methods/send-operations.method.spec.ts +0 -47
  41. package/src/methods/send-operations.method.ts +0 -15
  42. package/src/methods/send-transaction.method.spec.ts +0 -47
  43. package/src/methods/send-transaction.method.ts +0 -15
  44. package/src/methods/sign.method.spec.ts +0 -60
  45. package/src/methods/sign.method.ts +0 -22
  46. package/src/modules-import.spec.ts +0 -11
  47. package/src/require-import.spec.js +0 -11
  48. package/src/require-import.spec.ts +0 -10
  49. package/src/types/callback-function.type.ts +0 -1
  50. package/src/types/client-config.interface.ts +0 -8
  51. package/src/types/index.ts +0 -5
  52. package/src/types/login-options.interface.ts +0 -3
  53. package/src/types/send-response.type.ts +0 -1
  54. package/src/types/sign-errors.interface.ts +0 -4
  55. package/src/utilities/index.ts +0 -1
  56. package/src/utilities/is-browser.util.ts +0 -3
  57. package/tsconfig.json +0 -15
  58. package/webpack.config.js +0 -26
package/lib/index.js CHANGED
@@ -2,80 +2,20 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var crossFetch = require('cross-fetch');
6
5
  var hiveUri = require('hive-uri');
7
6
 
8
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
7
+ const API_URL = 'https://hivesigner.com';
9
8
 
10
- var crossFetch__default = /*#__PURE__*/_interopDefaultLegacy(crossFetch);
9
+ const BASE_URL = 'https://hivesigner.com';
11
10
 
12
- /*! *****************************************************************************
13
- Copyright (c) Microsoft Corporation.
14
-
15
- Permission to use, copy, modify, and/or distribute this software for any
16
- purpose with or without fee is hereby granted.
17
-
18
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
19
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
20
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
21
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
22
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
23
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24
- PERFORMANCE OF THIS SOFTWARE.
25
- ***************************************************************************** */
26
-
27
- function __awaiter(thisArg, _arguments, P, generator) {
28
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
29
- return new (P || (P = Promise))(function (resolve, reject) {
30
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
31
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
32
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
33
- step((generator = generator.apply(thisArg, _arguments || [])).next());
34
- });
35
- }
36
-
37
- function __generator(thisArg, body) {
38
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
39
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
- function verb(n) { return function (v) { return step([n, v]); }; }
41
- function step(op) {
42
- if (f) throw new TypeError("Generator is already executing.");
43
- while (_) try {
44
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
45
- if (y = 0, t) op = [op[0] & 2, t.value];
46
- switch (op[0]) {
47
- case 0: case 1: t = op; break;
48
- case 4: _.label++; return { value: op[1], done: false };
49
- case 5: _.label++; y = op[1]; op = [0]; continue;
50
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
51
- default:
52
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
53
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
54
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
55
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
56
- if (t[2]) _.ops.pop();
57
- _.trys.pop(); continue;
58
- }
59
- op = body.call(thisArg, _);
60
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
61
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
62
- }
63
- }
64
-
65
- var API_URL = 'https://hivesigner.com';
66
-
67
- var BASE_URL = 'https://hivesigner.com';
68
-
69
- var BETA_URL = 'https://hivesigner.com';
11
+ const BETA_URL = 'https://hivesigner.com';
70
12
 
71
13
  function isBrowser() {
72
14
  return typeof window !== 'undefined' && !!window;
73
15
  }
74
16
 
75
- // Bind fetch to avoid "Illegal invocation" errors in browsers
76
- var fetch = crossFetch__default['default'].bind(typeof globalThis !== 'undefined' ? globalThis : undefined);
77
- var Client = /** @class */ (function () {
78
- function Client(config) {
17
+ class Client {
18
+ constructor(config) {
79
19
  this.apiURL = config.apiURL || API_URL;
80
20
  this.app = config.app;
81
21
  this.callbackURL = config.callbackURL;
@@ -83,203 +23,196 @@ var Client = /** @class */ (function () {
83
23
  this.scope = config.scope;
84
24
  this.responseType = config.responseType;
85
25
  }
86
- Client.prototype.setBaseURL = function () {
26
+ setBaseURL() {
87
27
  console.warn('The function "setBaseUrl" is deprecated, the base URL is always "https://hivesigner.com", you can only change the API URL with "setApiURL"');
88
28
  return this;
89
- };
90
- Client.prototype.setApiURL = function (url) {
29
+ }
30
+ setApiURL(url) {
91
31
  this.apiURL = url;
92
32
  return this;
93
- };
94
- Client.prototype.setApp = function (app) {
33
+ }
34
+ setApp(app) {
95
35
  this.app = app;
96
36
  return this;
97
- };
98
- Client.prototype.setCallbackURL = function (url) {
37
+ }
38
+ setCallbackURL(url) {
99
39
  this.callbackURL = url;
100
40
  return this;
101
- };
102
- Client.prototype.setAccessToken = function (accessToken) {
41
+ }
42
+ setAccessToken(accessToken) {
103
43
  this.accessToken = accessToken;
104
44
  return this;
105
- };
106
- Client.prototype.removeAccessToken = function () {
45
+ }
46
+ removeAccessToken() {
107
47
  delete this.accessToken;
108
48
  return this;
109
- };
110
- Client.prototype.setScope = function (scope) {
49
+ }
50
+ setScope(scope) {
111
51
  this.scope = scope;
112
52
  return this;
113
- };
114
- Client.prototype.getLoginURL = function (state, account) {
115
- var redirectUri = encodeURIComponent(this.callbackURL);
116
- var loginURL = BASE_URL + "/oauth2/authorize?client_id=" + this.app + "&redirect_uri=" + redirectUri;
53
+ }
54
+ /**
55
+ * The authorize URL. Pass `options.codeChallenge` (from createPkcePair) to
56
+ * protect a code flow sign-in with PKCE, which a native app needs for a
57
+ * custom scheme callback such as `myapp://callback`.
58
+ */
59
+ getLoginURL(state, account, options = {}) {
60
+ const redirectUri = encodeURIComponent(this.callbackURL);
61
+ let loginURL = `${BASE_URL}/oauth2/authorize?client_id=${this.app}&redirect_uri=${redirectUri}`;
117
62
  if (this.responseType === 'code') {
118
- loginURL += "&response_type=" + this.responseType;
63
+ loginURL += `&response_type=${this.responseType}`;
119
64
  }
120
65
  if (this.scope) {
121
- loginURL += "&scope=" + this.scope.join(',');
66
+ loginURL += `&scope=${this.scope.join(',')}`;
122
67
  }
123
68
  if (state) {
124
- loginURL += "&state=" + encodeURIComponent(state);
69
+ loginURL += `&state=${encodeURIComponent(state)}`;
125
70
  }
126
71
  if (account) {
127
- loginURL += "&account=" + encodeURIComponent(account);
72
+ loginURL += `&account=${encodeURIComponent(account)}`;
73
+ }
74
+ // PKCE protects a code: the token flow has none, and Hivesigner ignores it there.
75
+ if (options.codeChallenge && this.responseType === 'code') {
76
+ loginURL += `&code_challenge=${encodeURIComponent(options.codeChallenge)}&code_challenge_method=S256`;
128
77
  }
129
78
  return loginURL;
130
- };
131
- Client.prototype.login = function (options) {
79
+ }
80
+ login(options) {
132
81
  if (isBrowser()) {
133
82
  // @ts-ignore
134
- window.location = this.getLoginURL(options.state);
83
+ window.location = this.getLoginURL(options.state, undefined, { codeChallenge: options.codeChallenge });
135
84
  }
136
- };
137
- Client.prototype.me = function (cb) {
85
+ }
86
+ /**
87
+ * Exchange a code for tokens at /api/oauth2/token. With PKCE send the
88
+ * verifier; a native app sends no secret and gets no refresh token. A
89
+ * server may pass its client secret as well.
90
+ *
91
+ * Sent WITHOUT the Authorization header: the API reads that header before
92
+ * the body, so a token this client already holds would be read instead of
93
+ * the code.
94
+ */
95
+ async exchangeCode(code, codeVerifier, clientSecret) {
96
+ const body = { code };
97
+ if (codeVerifier)
98
+ body.code_verifier = codeVerifier;
99
+ if (clientSecret)
100
+ body.client_secret = clientSecret;
101
+ return (await this.makeRequest(`${this.apiURL}/api/oauth2/token`, 'POST', body, false));
102
+ }
103
+ me(cb) {
138
104
  return this.send('me', 'POST', {}, cb);
139
- };
140
- Client.prototype.decode = function (memo, cb) {
105
+ }
106
+ decode(memo, cb) {
141
107
  return this.send('decode', 'POST', { memo: memo }, cb);
142
- };
143
- Client.prototype.vote = function (voter, author, permlink, weight, cb) {
144
- var params = {
145
- voter: voter,
146
- author: author,
147
- permlink: permlink,
148
- weight: weight
108
+ }
109
+ vote(voter, author, permlink, weight, cb) {
110
+ const params = {
111
+ voter,
112
+ author,
113
+ permlink,
114
+ weight
149
115
  };
150
116
  return this.broadcast([['vote', params]], cb);
151
- };
152
- Client.prototype.comment = function (parentAuthor, parentPermlink, author, permlink, title, body, jsonMetadata, cb) {
153
- var json = jsonMetadata;
117
+ }
118
+ comment(parentAuthor, parentPermlink, author, permlink, title, body, jsonMetadata, cb) {
119
+ let json = jsonMetadata;
154
120
  if (typeof jsonMetadata !== 'string') {
155
121
  json = JSON.stringify(jsonMetadata);
156
122
  }
157
- var params = {
123
+ const params = {
158
124
  parent_author: parentAuthor,
159
125
  parent_permlink: parentPermlink,
160
- author: author,
161
- permlink: permlink,
162
- title: title,
163
- body: body,
126
+ author,
127
+ permlink,
128
+ title,
129
+ body,
164
130
  json_metadata: json
165
131
  };
166
132
  return this.broadcast([['comment', params]], cb);
167
- };
168
- Client.prototype.deleteComment = function (author, permlink, cb) {
169
- var params = {
170
- author: author,
171
- permlink: permlink
133
+ }
134
+ deleteComment(author, permlink, cb) {
135
+ const params = {
136
+ author,
137
+ permlink
172
138
  };
173
139
  return this.broadcast([['delete_comment', params]], cb);
174
- };
175
- Client.prototype.customJson = function (requiredAuths, requiredPostingAuths, id, json, cb) {
176
- var params = {
140
+ }
141
+ customJson(requiredAuths, requiredPostingAuths, id, json, cb) {
142
+ const params = {
177
143
  required_auths: requiredAuths,
178
144
  required_posting_auths: requiredPostingAuths,
179
- id: id,
180
- json: json
145
+ id,
146
+ json
181
147
  };
182
148
  return this.broadcast([['custom_json', params]], cb);
183
- };
184
- Client.prototype.reblog = function (account, author, permlink, cb) {
185
- var json = ['reblog', { account: account, author: author, permlink: permlink }];
149
+ }
150
+ reblog(account, author, permlink, cb) {
151
+ const json = ['reblog', { account, author, permlink }];
186
152
  return this.customJson([], [account], 'follow', JSON.stringify(json), cb);
187
- };
188
- Client.prototype.follow = function (follower, following, cb) {
189
- var json = ['follow', { follower: follower, following: following, what: ['blog'] }];
153
+ }
154
+ follow(follower, following, cb) {
155
+ const json = ['follow', { follower, following, what: ['blog'] }];
190
156
  return this.customJson([], [follower], 'follow', JSON.stringify(json), cb);
191
- };
192
- Client.prototype.unfollow = function (unfollower, unfollowing, cb) {
193
- var json = ['follow', { follower: unfollower, following: unfollowing, what: [] }];
157
+ }
158
+ unfollow(unfollower, unfollowing, cb) {
159
+ const json = ['follow', { follower: unfollower, following: unfollowing, what: [] }];
194
160
  return this.customJson([], [unfollower], 'follow', JSON.stringify(json), cb);
195
- };
196
- Client.prototype.ignore = function (follower, following, cb) {
197
- var json = ['follow', { follower: follower, following: following, what: ['ignore'] }];
161
+ }
162
+ ignore(follower, following, cb) {
163
+ const json = ['follow', { follower, following, what: ['ignore'] }];
198
164
  return this.customJson([], [follower], 'follow', JSON.stringify(json), cb);
199
- };
200
- Client.prototype.claimRewardBalance = function (account, rewardHive, rewardHbd, rewardVests, cb) {
201
- var params = {
202
- account: account,
165
+ }
166
+ claimRewardBalance(account, rewardHive, rewardHbd, rewardVests, cb) {
167
+ const params = {
168
+ account,
203
169
  reward_hive: rewardHive,
204
170
  reward_hbd: rewardHbd,
205
171
  reward_vests: rewardVests
206
172
  };
207
173
  return this.broadcast([['claim_reward_balance', params]], cb);
208
- };
209
- Client.prototype.revokeToken = function (cb) {
210
- return __awaiter(this, void 0, Promise, function () {
211
- return __generator(this, function (_a) {
212
- switch (_a.label) {
213
- case 0: return [4 /*yield*/, this.send('oauth2/token/revoke', 'POST', { token: this.accessToken }, cb)];
214
- case 1:
215
- _a.sent();
216
- return [2 /*return*/, this.removeAccessToken()];
217
- }
218
- });
219
- });
220
- };
221
- Client.prototype.updateUserMetadata = function (metadata, cb) {
222
- if (metadata === void 0) { metadata = {}; }
174
+ }
175
+ async revokeToken(cb) {
176
+ await this.send('oauth2/token/revoke', 'POST', { token: this.accessToken }, cb);
177
+ return this.removeAccessToken();
178
+ }
179
+ updateUserMetadata(metadata = {}, cb) {
223
180
  console.warn('The function "updateUserMetadata" is deprecated.');
224
181
  return this.send('me', 'PUT', { user_metadata: metadata }, cb);
225
- };
226
- Client.prototype.send = function (route, method, body, cb) {
227
- return __awaiter(this, void 0, Promise, function () {
228
- var url, json, failureResponse_1;
229
- return __generator(this, function (_a) {
230
- switch (_a.label) {
231
- case 0:
232
- url = this.apiURL + "/api/" + route;
233
- if (!cb) {
234
- return [2 /*return*/, this.makeRequest(url, method, body)];
235
- }
236
- _a.label = 1;
237
- case 1:
238
- _a.trys.push([1, 3, , 4]);
239
- return [4 /*yield*/, this.makeRequest(url, method, body)];
240
- case 2:
241
- json = _a.sent();
242
- return [2 /*return*/, cb(null, json)];
243
- case 3:
244
- failureResponse_1 = _a.sent();
245
- return [2 /*return*/, cb(failureResponse_1, null)];
246
- case 4: return [2 /*return*/];
247
- }
248
- });
249
- });
250
- };
251
- Client.prototype.broadcast = function (operations, cb) {
252
- return this.send('broadcast', 'POST', { operations: operations }, cb);
253
- };
254
- Client.prototype.makeRequest = function (url, method, body) {
255
- return __awaiter(this, void 0, Promise, function () {
256
- var response, json;
257
- return __generator(this, function (_a) {
258
- switch (_a.label) {
259
- case 0: return [4 /*yield*/, fetch(url, {
260
- method: method,
261
- headers: {
262
- Accept: 'application/json, text/plain, */*',
263
- 'Content-Type': 'application/json',
264
- Authorization: this.accessToken,
265
- },
266
- body: JSON.stringify(body),
267
- })];
268
- case 1:
269
- response = _a.sent();
270
- return [4 /*yield*/, response.json()];
271
- case 2:
272
- json = _a.sent();
273
- if (response.status !== 200 || json.error) {
274
- throw json;
275
- }
276
- return [2 /*return*/, json];
277
- }
278
- });
182
+ }
183
+ async send(route, method, body, cb) {
184
+ const url = `${this.apiURL}/api/${route}`;
185
+ if (!cb) {
186
+ return this.makeRequest(url, method, body);
187
+ }
188
+ try {
189
+ const json = await this.makeRequest(url, method, body);
190
+ return cb(null, json);
191
+ }
192
+ catch (failureResponse) {
193
+ return cb(failureResponse, null);
194
+ }
195
+ }
196
+ broadcast(operations, cb) {
197
+ return this.send('broadcast', 'POST', { operations }, cb);
198
+ }
199
+ async makeRequest(url, method, body, withToken = true) {
200
+ if (typeof globalThis.fetch !== 'function') {
201
+ throw new Error('hivesigner: fetch is not available. Node.js 18+ or a modern browser is required. ' +
202
+ 'If you are in an older environment, provide a global fetch polyfill.');
203
+ }
204
+ const response = await globalThis.fetch(url, {
205
+ method,
206
+ headers: Object.assign({ Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, (withToken ? { Authorization: this.accessToken } : {})),
207
+ body: JSON.stringify(body),
279
208
  });
280
- };
281
- return Client;
282
- }());
209
+ const json = await response.json();
210
+ if (response.status !== 200 || json.error) {
211
+ throw json;
212
+ }
213
+ return json;
214
+ }
215
+ }
283
216
 
284
217
  function sign(name, params, redirectUri) {
285
218
  console.warn('The function "sign" is deprecated.');
@@ -289,39 +222,39 @@ function sign(name, params, redirectUri) {
289
222
  error_description: 'Request has an invalid format'
290
223
  };
291
224
  }
292
- var url = BASE_URL + "/sign/" + name + "?";
225
+ let url = `${BASE_URL}/sign/${name}?`;
293
226
  url += Object.keys(params)
294
- .map(function (key) { return key + "=" + encodeURIComponent(params[key]); })
227
+ .map(key => `${key}=${encodeURIComponent(params[key])}`)
295
228
  .join('&');
296
- url += redirectUri ? "&redirect_uri=" + encodeURIComponent(redirectUri) : '';
229
+ url += redirectUri ? `&redirect_uri=${encodeURIComponent(redirectUri)}` : '';
297
230
  return url;
298
231
  }
299
232
 
300
233
  function sendOperation(op, params, cb) {
301
- var uri = hiveUri.encodeOp(op, params);
302
- var webUrl = uri.replace('hive://', BETA_URL + "/");
234
+ const uri = hiveUri.encodeOp(op, params);
235
+ const webUrl = uri.replace('hive://', `${BETA_URL}/`);
303
236
  if (cb && isBrowser()) {
304
- var win = window.open(webUrl, '_blank');
237
+ const win = window.open(webUrl, '_blank');
305
238
  return win.focus();
306
239
  }
307
240
  return webUrl;
308
241
  }
309
242
 
310
243
  function sendOperations(ops, params, cb) {
311
- var uri = hiveUri.encodeOps(ops, params);
312
- var webUrl = uri.replace('hive://', BETA_URL + "/");
244
+ const uri = hiveUri.encodeOps(ops, params);
245
+ const webUrl = uri.replace('hive://', `${BETA_URL}/`);
313
246
  if (cb && isBrowser()) {
314
- var win = window.open(webUrl, '_blank');
247
+ const win = window.open(webUrl, '_blank');
315
248
  return win.focus();
316
249
  }
317
250
  return webUrl;
318
251
  }
319
252
 
320
253
  function sendTransaction(tx, params, cb) {
321
- var uri = hiveUri.encodeTx(tx, params);
322
- var webUrl = uri.replace('hive://', BETA_URL + "/");
254
+ const uri = hiveUri.encodeTx(tx, params);
255
+ const webUrl = uri.replace('hive://', `${BETA_URL}/`);
323
256
  if (cb && isBrowser()) {
324
- var win = window.open(webUrl, '_blank');
257
+ const win = window.open(webUrl, '_blank');
325
258
  return win.focus();
326
259
  }
327
260
  return webUrl;
@@ -332,18 +265,54 @@ function Initialize(config) {
332
265
  return new Client(config);
333
266
  }
334
267
 
268
+ /**
269
+ * PKCE (RFC 7636) for apps that cannot keep a client secret: a native app makes
270
+ * a fresh verifier for every sign-in, sends only its S256 challenge to the
271
+ * authorize page, and proves it holds the verifier when it exchanges the code.
272
+ * See https://hivesigner.com/docs/pkce
273
+ */
274
+ function subtleCrypto() {
275
+ const c = globalThis.crypto;
276
+ if (!c || !c.subtle || typeof c.getRandomValues !== 'function') {
277
+ throw new Error('hivesigner: Web Crypto is not available. Node.js 20+ or a modern browser is required for PKCE.');
278
+ }
279
+ return c;
280
+ }
281
+ function base64url(bytes) {
282
+ let binary = '';
283
+ bytes.forEach((b) => {
284
+ binary += String.fromCharCode(b);
285
+ });
286
+ const b64 = typeof btoa === 'function' ? btoa(binary) : Buffer.from(binary, 'binary').toString('base64');
287
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
288
+ }
289
+ /** BASE64URL(SHA256(verifier)), the S256 challenge of a verifier. */
290
+ async function pkceChallengeFor(codeVerifier) {
291
+ const digest = await subtleCrypto().subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
292
+ return base64url(new Uint8Array(digest));
293
+ }
294
+ /** A new verifier (32 random bytes, 43 characters) and its S256 challenge. */
295
+ async function createPkcePair() {
296
+ const codeVerifier = base64url(subtleCrypto().getRandomValues(new Uint8Array(32)));
297
+ return { codeVerifier, codeChallenge: await pkceChallengeFor(codeVerifier) };
298
+ }
299
+
335
300
  var index = {
336
- Client: Client,
337
- Initialize: Initialize,
338
- sendTransaction: sendTransaction,
339
- sendOperations: sendOperations,
340
- sendOperation: sendOperation,
341
- sign: sign
301
+ Client,
302
+ Initialize,
303
+ sendTransaction,
304
+ sendOperations,
305
+ sendOperation,
306
+ sign,
307
+ createPkcePair,
308
+ pkceChallengeFor
342
309
  };
343
310
 
344
311
  exports.Client = Client;
345
312
  exports.Initialize = Initialize;
346
- exports.default = index;
313
+ exports.createPkcePair = createPkcePair;
314
+ exports["default"] = index;
315
+ exports.pkceChallengeFor = pkceChallengeFor;
347
316
  exports.sendOperation = sendOperation;
348
317
  exports.sendOperations = sendOperations;
349
318
  exports.sendTransaction = sendTransaction;
@@ -1,4 +1,3 @@
1
1
  import { Parameters } from 'hive-uri';
2
- import { CallbackFunction } from '../types';
3
- import { Operation } from '@hiveio/dhive';
2
+ import { CallbackFunction, Operation } from '../types';
4
3
  export declare function sendOperation(op: Operation, params: Parameters, cb: CallbackFunction): string | void;
@@ -1,4 +1,3 @@
1
1
  import { Parameters } from 'hive-uri';
2
- import { CallbackFunction } from '../types';
3
- import { Operation } from '@hiveio/dhive';
2
+ import { CallbackFunction, Operation } from '../types';
4
3
  export declare function sendOperations(ops: Operation[], params: Parameters, cb: CallbackFunction): string | void;
@@ -1,4 +1,3 @@
1
1
  import { Parameters } from 'hive-uri';
2
- import { CallbackFunction } from '../types';
3
- import { Transaction } from '@hiveio/dhive';
2
+ import { CallbackFunction, Transaction } from '../types';
4
3
  export declare function sendTransaction(tx: Transaction, params: Parameters, cb: CallbackFunction): string | void;
package/lib/pkce.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * PKCE (RFC 7636) for apps that cannot keep a client secret: a native app makes
3
+ * a fresh verifier for every sign-in, sends only its S256 challenge to the
4
+ * authorize page, and proves it holds the verifier when it exchanges the code.
5
+ * See https://hivesigner.com/docs/pkce
6
+ */
7
+ export interface PkcePair {
8
+ /** Kept by the app until the exchange. Never put it in a URL. */
9
+ codeVerifier: string;
10
+ /** Sent as `code_challenge` with `code_challenge_method=S256`. */
11
+ codeChallenge: string;
12
+ }
13
+ /** BASE64URL(SHA256(verifier)), the S256 challenge of a verifier. */
14
+ export declare function pkceChallengeFor(codeVerifier: string): Promise<string>;
15
+ /** A new verifier (32 random bytes, 43 characters) and its S256 challenge. */
16
+ export declare function createPkcePair(): Promise<PkcePair>;
@@ -1 +1 @@
1
- export declare type CallbackFunction = (error: any, response: any) => any;
1
+ export type CallbackFunction = (error: any, response: any) => any;
@@ -0,0 +1,13 @@
1
+ export interface Operation {
2
+ 0: string;
3
+ 1: {
4
+ [key: string]: any;
5
+ };
6
+ }
7
+ export interface Transaction {
8
+ ref_block_num: number;
9
+ ref_block_prefix: number;
10
+ expiration: string;
11
+ operations: Operation[];
12
+ extensions: any[];
13
+ }
@@ -3,3 +3,5 @@ export * from './sign-errors.interface';
3
3
  export * from './callback-function.type';
4
4
  export * from './login-options.interface';
5
5
  export * from './send-response.type';
6
+ export * from './hive.types';
7
+ export * from './token-response.interface';
@@ -1,3 +1,5 @@
1
1
  export interface LoginOptions {
2
2
  state: string;
3
+ /** From createPkcePair: protects a code flow sign-in with PKCE. */
4
+ codeChallenge?: string;
3
5
  }
@@ -1 +1 @@
1
- export declare type SendResponse = any;
1
+ export type SendResponse = any;
@@ -0,0 +1,8 @@
1
+ /** What /api/oauth2/token answers. `refresh_token` is absent for a PKCE
2
+ exchange without a client secret. */
3
+ export interface TokenResponse {
4
+ access_token: string;
5
+ refresh_token?: string;
6
+ expires_in: number;
7
+ username: string;
8
+ }