@imtbl/sdk 0.2.0-alpha → 0.2.1-alpha
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/dist/blockchain-data.js +1 -1
- package/dist/browser/checkout.js +2 -2
- package/dist/checkout_widgets.d.ts +4 -4
- package/dist/checkout_widgets.js +1 -1
- package/dist/immutablex_client.js +1 -1
- package/dist/index.cjs +219 -11
- package/dist/index.d.ts +27 -5
- package/dist/index.js +219 -11
- package/dist/orderbook.js +3 -5
- package/dist/passport.d.ts +23 -1
- package/dist/passport.js +215 -4
- package/package.json +2 -1
package/dist/passport.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ImmutableX, Config, generateLegacyStarkPrivateKey, createStarkSigner } from '@imtbl/core-sdk';
|
|
2
2
|
import { UserManager } from 'oidc-client-ts';
|
|
3
|
+
import axios$1 from 'axios';
|
|
4
|
+
import jwt_decode from 'jwt-decode';
|
|
3
5
|
import { Magic } from 'magic-sdk';
|
|
4
6
|
import { OpenIdExtension } from '@magic-ext/oidc';
|
|
5
7
|
import BN from 'bn.js';
|
|
@@ -21,7 +23,7 @@ const createImmutableXConfiguration = ({ basePath, chainID, coreContractAddress,
|
|
|
21
23
|
chainID,
|
|
22
24
|
coreContractAddress,
|
|
23
25
|
registrationContractAddress,
|
|
24
|
-
sdkVersion: 'ts-immutable-sdk-0.2.
|
|
26
|
+
sdkVersion: 'ts-immutable-sdk-0.2.1-alpha',
|
|
25
27
|
});
|
|
26
28
|
/**
|
|
27
29
|
* Sets `sdkVersion` at the time of build
|
|
@@ -4289,6 +4291,43 @@ const withPassportError = async (fn, customErrorType) => {
|
|
|
4289
4291
|
}
|
|
4290
4292
|
};
|
|
4291
4293
|
|
|
4294
|
+
const keyCrendentials = 'passport_credentials';
|
|
4295
|
+
const validCredentialsMinTtlSec = 3600; // 1 hour
|
|
4296
|
+
class DeviceCredentialsManager {
|
|
4297
|
+
saveCredentials(tokenResponse) {
|
|
4298
|
+
localStorage.setItem(keyCrendentials, JSON.stringify(tokenResponse));
|
|
4299
|
+
}
|
|
4300
|
+
getCredentials() {
|
|
4301
|
+
const credentialsJson = localStorage.getItem(keyCrendentials);
|
|
4302
|
+
if (credentialsJson) {
|
|
4303
|
+
return JSON.parse(credentialsJson);
|
|
4304
|
+
}
|
|
4305
|
+
return null;
|
|
4306
|
+
}
|
|
4307
|
+
areValid(tokenResponse) {
|
|
4308
|
+
if (tokenResponse) {
|
|
4309
|
+
const accessTokenValid = this.isTokenValid(tokenResponse.access_token);
|
|
4310
|
+
const idTokenValid = this.isTokenValid(tokenResponse.id_token);
|
|
4311
|
+
return accessTokenValid && idTokenValid;
|
|
4312
|
+
}
|
|
4313
|
+
return false;
|
|
4314
|
+
}
|
|
4315
|
+
isTokenValid(jwt) {
|
|
4316
|
+
const tokenPayload = jwt_decode(jwt);
|
|
4317
|
+
const expiresAt = tokenPayload.exp ?? 0;
|
|
4318
|
+
const now = (Date.now() / 1000) + validCredentialsMinTtlSec;
|
|
4319
|
+
return expiresAt > now;
|
|
4320
|
+
}
|
|
4321
|
+
clearCredentials() {
|
|
4322
|
+
localStorage.removeItem(keyCrendentials);
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
const formUrlEncodedHeader = {
|
|
4327
|
+
headers: {
|
|
4328
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
4329
|
+
},
|
|
4330
|
+
};
|
|
4292
4331
|
const getAuthConfiguration = ({ oidcConfiguration, authenticationDomain, }) => {
|
|
4293
4332
|
const baseConfiguration = {
|
|
4294
4333
|
authority: authenticationDomain,
|
|
@@ -4314,12 +4353,19 @@ const getAuthConfiguration = ({ oidcConfiguration, authenticationDomain, }) => {
|
|
|
4314
4353
|
}
|
|
4315
4354
|
return baseConfiguration;
|
|
4316
4355
|
};
|
|
4356
|
+
function wait$1(ms) {
|
|
4357
|
+
return new Promise((resolve) => {
|
|
4358
|
+
setTimeout(resolve, ms);
|
|
4359
|
+
});
|
|
4360
|
+
}
|
|
4317
4361
|
class AuthManager {
|
|
4318
4362
|
userManager;
|
|
4319
4363
|
config;
|
|
4364
|
+
deviceCredentialsManager;
|
|
4320
4365
|
constructor(config) {
|
|
4321
4366
|
this.config = config;
|
|
4322
4367
|
this.userManager = new UserManager(getAuthConfiguration(config));
|
|
4368
|
+
this.deviceCredentialsManager = new DeviceCredentialsManager();
|
|
4323
4369
|
}
|
|
4324
4370
|
static mapOidcUserToDomainModel = (oidcUser) => {
|
|
4325
4371
|
const passport = oidcUser.profile?.passport;
|
|
@@ -4349,6 +4395,27 @@ class AuthManager {
|
|
|
4349
4395
|
}
|
|
4350
4396
|
return user;
|
|
4351
4397
|
};
|
|
4398
|
+
static mapDeviceTokenResponseToDomainUserModel = (tokenResponse) => {
|
|
4399
|
+
const idTokenPayload = jwt_decode(tokenResponse.id_token);
|
|
4400
|
+
const user = {
|
|
4401
|
+
idToken: tokenResponse.id_token,
|
|
4402
|
+
accessToken: tokenResponse.access_token,
|
|
4403
|
+
refreshToken: tokenResponse.refresh_token,
|
|
4404
|
+
profile: {
|
|
4405
|
+
sub: idTokenPayload.sub,
|
|
4406
|
+
email: idTokenPayload.email,
|
|
4407
|
+
nickname: idTokenPayload.nickname,
|
|
4408
|
+
},
|
|
4409
|
+
};
|
|
4410
|
+
if (idTokenPayload?.passport?.imx_eth_address) {
|
|
4411
|
+
user.imx = {
|
|
4412
|
+
ethAddress: idTokenPayload?.passport?.imx_eth_address,
|
|
4413
|
+
starkAddress: idTokenPayload?.passport?.imx_stark_address,
|
|
4414
|
+
userAdminAddress: idTokenPayload?.passport?.imx_user_admin_address,
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
return user;
|
|
4418
|
+
};
|
|
4352
4419
|
async login() {
|
|
4353
4420
|
return withPassportError(async () => {
|
|
4354
4421
|
const popupWindowFeatures = { width: 410, height: 450 };
|
|
@@ -4361,9 +4428,104 @@ class AuthManager {
|
|
|
4361
4428
|
async loginCallback() {
|
|
4362
4429
|
return withPassportError(async () => this.userManager.signinPopupCallback(), PassportErrorType.AUTHENTICATION_ERROR);
|
|
4363
4430
|
}
|
|
4431
|
+
async loginWithDeviceFlow() {
|
|
4432
|
+
return withPassportError(async () => {
|
|
4433
|
+
const response = await axios$1.post(`${this.config.authenticationDomain}/oauth/device/code`, {
|
|
4434
|
+
client_id: this.config.oidcConfiguration.clientId,
|
|
4435
|
+
scope: this.config.oidcConfiguration.scope,
|
|
4436
|
+
audience: this.config.oidcConfiguration.audience,
|
|
4437
|
+
}, formUrlEncodedHeader);
|
|
4438
|
+
return {
|
|
4439
|
+
code: response.data.user_code,
|
|
4440
|
+
deviceCode: response.data.device_code,
|
|
4441
|
+
url: response.data.verification_uri_complete,
|
|
4442
|
+
interval: response.data.interval,
|
|
4443
|
+
};
|
|
4444
|
+
}, PassportErrorType.AUTHENTICATION_ERROR);
|
|
4445
|
+
}
|
|
4446
|
+
/* eslint-disable no-await-in-loop */
|
|
4447
|
+
async connectImxDeviceFlow(deviceCode, interval, timeoutMs) {
|
|
4448
|
+
return withPassportError(async () => {
|
|
4449
|
+
const startTime = Date.now();
|
|
4450
|
+
const loopCondition = true;
|
|
4451
|
+
while (loopCondition) {
|
|
4452
|
+
if (timeoutMs != null && Date.now() - startTime > timeoutMs) {
|
|
4453
|
+
throw new Error('Timed out');
|
|
4454
|
+
}
|
|
4455
|
+
await wait$1(interval * 1000);
|
|
4456
|
+
try {
|
|
4457
|
+
const tokenResponse = await this.getDeviceFlowToken(deviceCode);
|
|
4458
|
+
const user = AuthManager.mapDeviceTokenResponseToDomainUserModel(tokenResponse);
|
|
4459
|
+
// Only persist credentials that contain the necessary data
|
|
4460
|
+
if (user.imx?.ethAddress && user.imx?.starkAddress && user.imx?.userAdminAddress) {
|
|
4461
|
+
this.deviceCredentialsManager.saveCredentials(tokenResponse);
|
|
4462
|
+
}
|
|
4463
|
+
return user;
|
|
4464
|
+
}
|
|
4465
|
+
catch (error) {
|
|
4466
|
+
if (axios$1.isAxiosError(error)) {
|
|
4467
|
+
const responseError = error.response?.data;
|
|
4468
|
+
switch (responseError.error) {
|
|
4469
|
+
case 'authorization_pending':
|
|
4470
|
+
break;
|
|
4471
|
+
case 'slow_down':
|
|
4472
|
+
break;
|
|
4473
|
+
case 'expired_token':
|
|
4474
|
+
throw new Error('Token expired, please log in again');
|
|
4475
|
+
case 'access_denied':
|
|
4476
|
+
throw new Error('User denied access');
|
|
4477
|
+
default:
|
|
4478
|
+
throw new Error('Error getting token');
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
else {
|
|
4482
|
+
throw error;
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
throw new Error('Failed to get credentials');
|
|
4487
|
+
}, PassportErrorType.AUTHENTICATION_ERROR);
|
|
4488
|
+
}
|
|
4489
|
+
/* eslint-enable no-await-in-loop */
|
|
4490
|
+
async getDeviceFlowToken(deviceCode) {
|
|
4491
|
+
const response = await axios$1.post(`${this.config.authenticationDomain}/oauth/token`, {
|
|
4492
|
+
client_id: this.config.oidcConfiguration.clientId,
|
|
4493
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
|
|
4494
|
+
device_code: deviceCode,
|
|
4495
|
+
}, formUrlEncodedHeader);
|
|
4496
|
+
return response.data;
|
|
4497
|
+
}
|
|
4498
|
+
async connectImxWithCredentials(tokenResponse) {
|
|
4499
|
+
return withPassportError(async () => {
|
|
4500
|
+
if (this.deviceCredentialsManager.areValid(tokenResponse)) {
|
|
4501
|
+
// Credentials exist and are still valid
|
|
4502
|
+
return AuthManager.mapDeviceTokenResponseToDomainUserModel(tokenResponse);
|
|
4503
|
+
}
|
|
4504
|
+
const refreshToken = tokenResponse?.refresh_token ?? null;
|
|
4505
|
+
if (refreshToken) {
|
|
4506
|
+
// Token is no longer valid, but refresh token can be used to a new one
|
|
4507
|
+
const newTokenResponse = await this.refreshToken(refreshToken);
|
|
4508
|
+
return AuthManager.mapDeviceTokenResponseToDomainUserModel(newTokenResponse);
|
|
4509
|
+
}
|
|
4510
|
+
return null;
|
|
4511
|
+
}, PassportErrorType.AUTHENTICATION_ERROR);
|
|
4512
|
+
}
|
|
4513
|
+
async refreshToken(refreshToken) {
|
|
4514
|
+
const response = await axios$1.post(`${this.config.authenticationDomain}/oauth/token`, {
|
|
4515
|
+
client_id: this.config.oidcConfiguration.clientId,
|
|
4516
|
+
grant_type: 'refresh_token',
|
|
4517
|
+
refresh_token: refreshToken,
|
|
4518
|
+
}, formUrlEncodedHeader);
|
|
4519
|
+
return response.data;
|
|
4520
|
+
}
|
|
4364
4521
|
async logout() {
|
|
4365
4522
|
return withPassportError(async () => this.userManager.signoutRedirect(), PassportErrorType.LOGOUT_ERROR);
|
|
4366
4523
|
}
|
|
4524
|
+
async logoutDeviceFlow() {
|
|
4525
|
+
return withPassportError(async () => {
|
|
4526
|
+
this.deviceCredentialsManager.clearCredentials();
|
|
4527
|
+
}, PassportErrorType.LOGOUT_ERROR);
|
|
4528
|
+
}
|
|
4367
4529
|
async loginSilent() {
|
|
4368
4530
|
return withPassportError(async () => {
|
|
4369
4531
|
const existedUser = await this.getUser();
|
|
@@ -4380,12 +4542,28 @@ class AuthManager {
|
|
|
4380
4542
|
async getUser() {
|
|
4381
4543
|
return withPassportError(async () => {
|
|
4382
4544
|
const oidcUser = await this.userManager.getUser();
|
|
4383
|
-
if (
|
|
4384
|
-
return
|
|
4545
|
+
if (oidcUser) {
|
|
4546
|
+
return AuthManager.mapOidcUserToDomainModel(oidcUser);
|
|
4385
4547
|
}
|
|
4386
|
-
|
|
4548
|
+
const deviceToken = this.deviceCredentialsManager.getCredentials();
|
|
4549
|
+
if (deviceToken) {
|
|
4550
|
+
return AuthManager.mapDeviceTokenResponseToDomainUserModel(deviceToken);
|
|
4551
|
+
}
|
|
4552
|
+
return null;
|
|
4387
4553
|
}, PassportErrorType.NOT_LOGGED_IN_ERROR);
|
|
4388
4554
|
}
|
|
4555
|
+
async getUserDeviceFlow() {
|
|
4556
|
+
return withPassportError(async () => {
|
|
4557
|
+
const deviceToken = this.deviceCredentialsManager.getCredentials();
|
|
4558
|
+
if (deviceToken) {
|
|
4559
|
+
return AuthManager.mapDeviceTokenResponseToDomainUserModel(deviceToken);
|
|
4560
|
+
}
|
|
4561
|
+
return null;
|
|
4562
|
+
}, PassportErrorType.NOT_LOGGED_IN_ERROR);
|
|
4563
|
+
}
|
|
4564
|
+
checkStoredDeviceFlowCredentials() {
|
|
4565
|
+
return this.deviceCredentialsManager.getCredentials();
|
|
4566
|
+
}
|
|
4389
4567
|
}
|
|
4390
4568
|
|
|
4391
4569
|
class MagicAdapter {
|
|
@@ -4913,6 +5091,17 @@ class PassportImxProviderFactory {
|
|
|
4913
5091
|
}
|
|
4914
5092
|
return this.createProviderInstance(user);
|
|
4915
5093
|
}
|
|
5094
|
+
async getProviderWithDeviceFlow(deviceCode, interval, timeoutMs) {
|
|
5095
|
+
const user = await this.authManager.connectImxDeviceFlow(deviceCode, interval, timeoutMs);
|
|
5096
|
+
return this.createProviderInstance(user);
|
|
5097
|
+
}
|
|
5098
|
+
async getProviderWithCredentials(tokenResponse) {
|
|
5099
|
+
const user = await this.authManager.connectImxWithCredentials(tokenResponse);
|
|
5100
|
+
if (!user) {
|
|
5101
|
+
return null;
|
|
5102
|
+
}
|
|
5103
|
+
return this.createProviderInstance(user);
|
|
5104
|
+
}
|
|
4916
5105
|
async createProviderInstance(user) {
|
|
4917
5106
|
if (!user.idToken) {
|
|
4918
5107
|
throw new PassportError('Failed to initialise', PassportErrorType.WALLET_CONNECTION_ERROR);
|
|
@@ -5220,16 +5409,38 @@ class Passport {
|
|
|
5220
5409
|
async connectImx() {
|
|
5221
5410
|
return this.passportImxProviderFactory.getProvider();
|
|
5222
5411
|
}
|
|
5412
|
+
async loginWithDeviceFlow() {
|
|
5413
|
+
return this.authManager.loginWithDeviceFlow();
|
|
5414
|
+
}
|
|
5415
|
+
async connectImxDeviceFlow(deviceCode, interval, timeoutMs) {
|
|
5416
|
+
return this.passportImxProviderFactory.getProviderWithDeviceFlow(deviceCode, interval, timeoutMs);
|
|
5417
|
+
}
|
|
5418
|
+
/**
|
|
5419
|
+
* @returns {boolean} the stored device flow credentials if they exist
|
|
5420
|
+
*/
|
|
5421
|
+
checkStoredDeviceFlowCredentials() {
|
|
5422
|
+
return this.authManager.checkStoredDeviceFlowCredentials();
|
|
5423
|
+
}
|
|
5424
|
+
async connectImxWithCredentials(tokenResponse) {
|
|
5425
|
+
return this.passportImxProviderFactory.getProviderWithCredentials(tokenResponse);
|
|
5426
|
+
}
|
|
5223
5427
|
async loginCallback() {
|
|
5224
5428
|
return this.authManager.loginCallback();
|
|
5225
5429
|
}
|
|
5226
5430
|
async logout() {
|
|
5227
5431
|
return this.authManager.logout();
|
|
5228
5432
|
}
|
|
5433
|
+
async logoutDeviceFlow() {
|
|
5434
|
+
return this.authManager.logoutDeviceFlow();
|
|
5435
|
+
}
|
|
5229
5436
|
async getUserInfo() {
|
|
5230
5437
|
const user = await this.authManager.getUser();
|
|
5231
5438
|
return user?.profile;
|
|
5232
5439
|
}
|
|
5440
|
+
async getUserInfoDeviceFlow() {
|
|
5441
|
+
const user = await this.authManager.getUserDeviceFlow();
|
|
5442
|
+
return user?.profile;
|
|
5443
|
+
}
|
|
5233
5444
|
async getIdToken() {
|
|
5234
5445
|
const user = await this.authManager.getUser();
|
|
5235
5446
|
return user?.idToken;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imtbl/sdk",
|
|
3
3
|
"description": "Immutable Unified SDK",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha",
|
|
5
5
|
"author": "Immutable",
|
|
6
6
|
"bugs": "https://github.com/immutable/ts-immutable-sdk/issues",
|
|
7
7
|
"dependencies": {
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"main": "./dist/index.cjs",
|
|
111
111
|
"module": "./dist/index.js",
|
|
112
112
|
"peerDependencies": {
|
|
113
|
+
"ethers": "^5.7.2",
|
|
113
114
|
"react": "^18.2.0",
|
|
114
115
|
"react-dom": "^18.2.0"
|
|
115
116
|
},
|