From d887de50d2549c4b1ab1c1b092f0e28972a5969e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:55:33 +0200 Subject: [PATCH] Feature/setup internet identity (#1080) * Setup Internet Identity as social login provider * Update changelog --- CHANGELOG.md | 8 ++ apps/api/src/app/auth/auth.controller.ts | 22 +++- apps/api/src/app/auth/auth.service.ts | 43 +++++-- ...ogin-with-access-token-dialog.component.ts | 18 ++- .../login-with-access-token-dialog.html | 34 +++-- .../login-with-access-token-dialog.scss | 8 ++ .../pages/register/register-page.component.ts | 10 ++ .../src/app/pages/register/register-page.html | 27 ++-- apps/client/src/app/services/data.service.ts | 5 +- .../app/services/internet-identity.service.ts | 55 ++++++++ apps/client/src/assets/icons/google.svg | 1 + .../src/assets/icons/internet-computer.svg | 28 +++++ libs/common/src/lib/interfaces/index.ts | 2 + .../responses/oauth-response.interface.ts | 3 + package.json | 8 +- .../migration.sql | 2 + prisma/schema.prisma | 1 + yarn.lock | 118 +++++++++++++++++- 18 files changed, 362 insertions(+), 31 deletions(-) create mode 100644 apps/client/src/app/services/internet-identity.service.ts create mode 100644 apps/client/src/assets/icons/google.svg create mode 100644 apps/client/src/assets/icons/internet-computer.svg create mode 100644 libs/common/src/lib/interfaces/responses/oauth-response.interface.ts create mode 100644 prisma/migrations/20220716071244_added_internet_identity_to_provider/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5524580..81b356bf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added _Internet Identity_ as a new social login provider + ### Changed - Improved the empty state of the @@ -19,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the distorted tooltip in the performance chart on the home page - Fixed a calculation issue of the current month in the investment timeline grouped by month +### Todo + +- Apply data migration (`yarn database:migrate`) + ## 1.170.0 - 19.07.2022 ### Added diff --git a/apps/api/src/app/auth/auth.controller.ts b/apps/api/src/app/auth/auth.controller.ts index 738f81ab2..9cb6f8132 100644 --- a/apps/api/src/app/auth/auth.controller.ts +++ b/apps/api/src/app/auth/auth.controller.ts @@ -1,5 +1,6 @@ import { WebAuthService } from '@ghostfolio/api/app/auth/web-auth.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; +import { OAuthResponse } from '@ghostfolio/common/interfaces'; import { Body, Controller, @@ -31,7 +32,9 @@ export class AuthController { ) {} @Get('anonymous/:accessToken') - public async accessTokenLogin(@Param('accessToken') accessToken: string) { + public async accessTokenLogin( + @Param('accessToken') accessToken: string + ): Promise { try { const authToken = await this.authService.validateAnonymousLogin( accessToken @@ -65,6 +68,23 @@ export class AuthController { } } + @Get('internet-identity/:principalId') + public async internetIdentityLogin( + @Param('principalId') principalId: string + ): Promise { + try { + const authToken = await this.authService.validateInternetIdentityLogin( + principalId + ); + return { authToken }; + } catch { + throw new HttpException( + getReasonPhrase(StatusCodes.FORBIDDEN), + StatusCodes.FORBIDDEN + ); + } + } + @Get('webauthn/generate-registration-options') @UseGuards(AuthGuard('jwt')) public async generateRegistrationOptions() { diff --git a/apps/api/src/app/auth/auth.service.ts b/apps/api/src/app/auth/auth.service.ts index dbc5024b5..3178ce9ac 100644 --- a/apps/api/src/app/auth/auth.service.ts +++ b/apps/api/src/app/auth/auth.service.ts @@ -2,6 +2,7 @@ import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration.service'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; +import { Provider } from '@prisma/client'; import { ValidateOAuthLoginParams } from './interfaces/interfaces'; @@ -13,7 +14,7 @@ export class AuthService { private readonly userService: UserService ) {} - public async validateAnonymousLogin(accessToken: string) { + public async validateAnonymousLogin(accessToken: string): Promise { return new Promise(async (resolve, reject) => { try { const hashedAccessToken = this.userService.createAccessToken( @@ -26,7 +27,7 @@ export class AuthService { }); if (user) { - const jwt: string = this.jwtService.sign({ + const jwt = this.jwtService.sign({ id: user.id }); @@ -40,6 +41,33 @@ export class AuthService { }); } + public async validateInternetIdentityLogin(principalId: string) { + try { + const provider: Provider = 'INTERNET_IDENTITY'; + + let [user] = await this.userService.users({ + where: { provider, thirdPartyId: principalId } + }); + + if (!user) { + // Create new user if not found + user = await this.userService.createUser({ + provider, + thirdPartyId: principalId + }); + } + + return this.jwtService.sign({ + id: user.id + }); + } catch (error) { + throw new InternalServerErrorException( + 'validateInternetIdentityLogin', + error.message + ); + } + } + public async validateOAuthLogin({ provider, thirdPartyId @@ -57,13 +85,14 @@ export class AuthService { }); } - const jwt: string = this.jwtService.sign({ + return this.jwtService.sign({ id: user.id }); - - return jwt; - } catch (err) { - throw new InternalServerErrorException('validateOAuthLogin', err.message); + } catch (error) { + throw new InternalServerErrorException( + 'validateOAuthLogin', + error.message + ); } } } diff --git a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts index a5579df7f..85af8aaee 100644 --- a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts +++ b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.component.ts @@ -1,10 +1,13 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { MatCheckboxChange } from '@angular/material/checkbox'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { STAY_SIGNED_IN, SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service'; +import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; @Component({ selector: 'gf-login-with-access-token-dialog', @@ -16,7 +19,10 @@ export class LoginWithAccessTokenDialog { public constructor( @Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef, - private settingsStorageService: SettingsStorageService + private internetIdentityService: InternetIdentityService, + private router: Router, + private settingsStorageService: SettingsStorageService, + private tokenStorageService: TokenStorageService ) {} ngOnInit() {} @@ -31,4 +37,14 @@ export class LoginWithAccessTokenDialog { public onClose() { this.dialogRef.close(); } + + public async onLoginWithInternetIdentity() { + try { + const { authToken } = await this.internetIdentityService.login(); + + this.tokenStorageService.saveToken(authToken); + this.dialogRef.close(); + this.router.navigate(['/']); + } catch {} + } } diff --git a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html index 2186368ca..8a2110ef1 100644 --- a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html +++ b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.html @@ -5,16 +5,7 @@ >
-
- - -
or
-
+
Security Token + +
or
+
+ + Sign in with Google +
+
diff --git a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.scss b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.scss index ea9627fa2..717891cbe 100644 --- a/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.scss +++ b/apps/client/src/app/components/login-with-access-token-dialog/login-with-access-token-dialog.scss @@ -12,4 +12,12 @@ } } } + + .mat-form-field { + ::ng-deep { + .mat-form-field-wrapper { + padding-bottom: 0; + } + } + } } diff --git a/apps/client/src/app/pages/register/register-page.component.ts b/apps/client/src/app/pages/register/register-page.component.ts index 3c9b81afd..1a7278304 100644 --- a/apps/client/src/app/pages/register/register-page.component.ts +++ b/apps/client/src/app/pages/register/register-page.component.ts @@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { InternetIdentityService } from '@ghostfolio/client/services/internet-identity.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { InfoItem } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; @@ -34,6 +35,7 @@ export class RegisterPageComponent implements OnDestroy, OnInit { private dataService: DataService, private deviceService: DeviceDetectorService, private dialog: MatDialog, + private internetIdentityService: InternetIdentityService, private router: Router, private tokenStorageService: TokenStorageService ) { @@ -62,6 +64,14 @@ export class RegisterPageComponent implements OnDestroy, OnInit { }); } + public async onLoginWithInternetIdentity() { + try { + const { authToken } = await this.internetIdentityService.login(); + this.tokenStorageService.saveToken(authToken); + this.router.navigate(['/']); + } catch {} + } + public openShowAccessTokenDialog( accessToken: string, authToken: string, diff --git a/apps/client/src/app/pages/register/register-page.html b/apps/client/src/app/pages/register/register-page.html index fd8c07f8d..770beb345 100644 --- a/apps/client/src/app/pages/register/register-page.html +++ b/apps/client/src/app/pages/register/register-page.html @@ -28,16 +28,25 @@ Create Account -
or
+
- Continue with Google + Continue with Internet Identity + + Continue with Google
diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index c3245ce4c..1aeb52298 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -23,6 +23,7 @@ import { Export, Filter, InfoItem, + OAuthResponse, PortfolioChart, PortfolioDetails, PortfolioInvestments, @@ -368,7 +369,9 @@ export class DataService { } public loginAnonymous(accessToken: string) { - return this.http.get(`/api/v1/auth/anonymous/${accessToken}`); + return this.http.get( + `/api/v1/auth/anonymous/${accessToken}` + ); } public postAccess(aAccess: CreateAccessDto) { diff --git a/apps/client/src/app/services/internet-identity.service.ts b/apps/client/src/app/services/internet-identity.service.ts new file mode 100644 index 000000000..9a4551d3e --- /dev/null +++ b/apps/client/src/app/services/internet-identity.service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, OnDestroy } from '@angular/core'; +import { AuthClient } from '@dfinity/auth-client'; +import { OAuthResponse } from '@ghostfolio/common/interfaces'; +import { EMPTY, Subject } from 'rxjs'; +import { catchError, takeUntil } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class InternetIdentityService implements OnDestroy { + private unsubscribeSubject = new Subject(); + + public constructor(private http: HttpClient) {} + + public async login(): Promise { + const authClient = await AuthClient.create({ + idleOptions: { + disableDefaultIdleCallback: true, + disableIdle: true + } + }); + + return new Promise((resolve, reject) => { + authClient.login({ + onError: async () => { + return reject(); + }, + onSuccess: () => { + const principalId = authClient.getIdentity().getPrincipal(); + + this.http + .get( + `/api/v1/auth/internet-identity/${principalId.toText()}` + ) + .pipe( + catchError(() => { + reject(); + return EMPTY; + }), + takeUntil(this.unsubscribeSubject) + ) + .subscribe((response) => { + resolve(response); + }); + } + }); + }); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } +} diff --git a/apps/client/src/assets/icons/google.svg b/apps/client/src/assets/icons/google.svg new file mode 100644 index 000000000..92bfb3b14 --- /dev/null +++ b/apps/client/src/assets/icons/google.svg @@ -0,0 +1 @@ + diff --git a/apps/client/src/assets/icons/internet-computer.svg b/apps/client/src/assets/icons/internet-computer.svg new file mode 100644 index 000000000..6a1bf6c86 --- /dev/null +++ b/apps/client/src/assets/icons/internet-computer.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 0d05f58d4..fc55c0591 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -29,6 +29,7 @@ import { PortfolioSummary } from './portfolio-summary.interface'; import { Position } from './position.interface'; import { BenchmarkResponse } from './responses/benchmark-response.interface'; import { ResponseError } from './responses/errors.interface'; +import { OAuthResponse } from './responses/oauth-response.interface'; import { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface'; import { ScraperConfiguration } from './scraper-configuration.interface'; import { TimelinePosition } from './timeline-position.interface'; @@ -54,6 +55,7 @@ export { FilterGroup, HistoricalDataItem, InfoItem, + OAuthResponse, PortfolioChart, PortfolioDetails, PortfolioInvestments, diff --git a/libs/common/src/lib/interfaces/responses/oauth-response.interface.ts b/libs/common/src/lib/interfaces/responses/oauth-response.interface.ts new file mode 100644 index 000000000..5b33ad651 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/oauth-response.interface.ts @@ -0,0 +1,3 @@ +export interface OAuthResponse { + authToken: string; +} diff --git a/package.json b/package.json index e6a9d92cf..e7cae9d76 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,12 @@ "@angular/platform-browser-dynamic": "14.0.2", "@angular/router": "14.0.2", "@codewithdan/observable-store": "2.2.11", + "@dfinity/agent": "0.12.1", + "@dfinity/auth-client": "0.12.1", + "@dfinity/authentication": "0.12.1", + "@dfinity/candid": "0.12.1", + "@dfinity/identity": "0.12.1", + "@dfinity/principal": "0.12.1", "@dinero.js/currencies": "2.0.0-alpha.8", "@nestjs/bull": "0.5.5", "@nestjs/common": "8.4.7", @@ -172,9 +178,9 @@ "prettier": "2.7.1", "replace-in-file": "6.2.0", "rimraf": "3.0.2", - "tslib": "2.0.0", "ts-jest": "27.1.4", "ts-node": "10.8.1", + "tslib": "2.0.0", "typescript": "4.7.3" }, "engines": { diff --git a/prisma/migrations/20220716071244_added_internet_identity_to_provider/migration.sql b/prisma/migrations/20220716071244_added_internet_identity_to_provider/migration.sql new file mode 100644 index 000000000..707f64078 --- /dev/null +++ b/prisma/migrations/20220716071244_added_internet_identity_to_provider/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "Provider" ADD VALUE 'INTERNET_IDENTITY'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 16249d51f..d11553cd4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -217,6 +217,7 @@ enum ViewMode { enum Provider { ANONYMOUS GOOGLE + INTERNET_IDENTITY } enum Role { diff --git a/yarn.lock b/yarn.lock index a9cba8d64..0127b8ecd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1769,6 +1769,48 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@dfinity/agent@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/agent/-/agent-0.12.1.tgz#c3acd6419712aca77bf94f76057f100caeb69be4" + integrity sha512-/KSKh248k4pjzvqCzIgYNNi3pTv+DBZ40+QiTBQeFzp6VEg3gfSv5bK2UwC0Plq9xwk7TeeeGLiTv6DI3RjCOQ== + dependencies: + base64-arraybuffer "^0.2.0" + bignumber.js "^9.0.0" + borc "^2.1.1" + js-sha256 "0.9.0" + simple-cbor "^0.4.1" + +"@dfinity/auth-client@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/auth-client/-/auth-client-0.12.1.tgz#8043aeafe8ba8a000954f94de25a76a2565acafc" + integrity sha512-iZKSVjk9K+35jp+AY3QfGAv0jBfn5LZTwpSXgBKVqZCez3GRniGJirJVTvk7t9yOj4BXN8tuvjIKxTsezPpgLQ== + +"@dfinity/authentication@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/authentication/-/authentication-0.12.1.tgz#19f157e6eb528518da874f3e10d3f7b2b028e5a4" + integrity sha512-krHR48HNqTOp2NwHoKHirTUXHDfHttWZfSmwBCsQa0xwWkrrLSGb3u+9e1oQjDK1G1eK2TP7T1W2duZmmmrZkg== + +"@dfinity/candid@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/candid/-/candid-0.12.1.tgz#6ba819c56bc3ff55f6f98bdb39470d1d385084b6" + integrity sha512-YX8jfyy/8Qmz4f1mbjqXUqOmtYcGru1gfYWxlRhKFSkeLH0VeZkfPEmD6EQ25k+18ATPk83MQiZnu0b6AWxBUw== + +"@dfinity/identity@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/identity/-/identity-0.12.1.tgz#f19bf2ed6bfbbb1f4e7c859c039e4da8dee81857" + integrity sha512-FNrjV4/gG9PjQfGLIoH1DycqSAMaoTZCxB+cSVJRFCvGQKc3F3kn5tj6rIv9LV+NNV1f1qfmTXE8rYsMCmEecg== + dependencies: + "@types/webappsec-credential-management" "^0.6.2" + borc "^2.1.1" + js-sha256 "^0.9.0" + secp256k1 "^4.0.2" + tweetnacl "^1.0.1" + +"@dfinity/principal@0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@dfinity/principal/-/principal-0.12.1.tgz#d4b1f088beded6c1f1b2efbe68a0632e0b4018f7" + integrity sha512-aL5y0mpzRex6LRSc4LUZyhn2GTFfHyxkakkOZxEM7+ecz8HsKKK+mSo78gL1TCso2QkCL4BqZzxnoIxxKqM1cw== + "@dinero.js/currencies@2.0.0-alpha.8": version "2.0.0-alpha.8" resolved "https://registry.yarnpkg.com/@dinero.js/currencies/-/currencies-2.0.0-alpha.8.tgz#aa02a04ce3685a9b06a7ce12f8c924726386c3fd" @@ -4151,6 +4193,11 @@ resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.3.tgz#3193c0a3c03a7d1189016c62b4fba4b149ef5e33" integrity sha512-DNviAE5OUcZ5s+XEQHRhERLg8fOp8gSgvyJ4aaFASx5wwaObm+PBwTIMXiOFm1QrSee5oYwEAYb7LMzX2O88gA== +"@types/webappsec-credential-management@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@types/webappsec-credential-management/-/webappsec-credential-management-0.6.2.tgz#93491de1ffcf57f6558c78949cc8e6c5d826b94e" + integrity sha512-/6w8wmKQOFh+1CL99fcFhH7lli1/ExBdAawXsVPXFC5MBOS6ww/4cmK4crpCw51RaG6sTr477N17Y84l0G69IA== + "@types/webpack-env@^1.16.0": version "1.17.0" resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" @@ -5484,6 +5531,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-arraybuffer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45" + integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ== + base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -5651,6 +5703,19 @@ bootstrap@4.6.0: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7" integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw== +borc@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.2.tgz#6ce75e7da5ce711b963755117dd1b187f6f8cf19" + integrity sha512-Sy9eoUi4OiKzq7VovMn246iTo17kzuyHJKomCfpWMlI6RpfN1gk95w7d7gH264nApVLg0HZfcpz62/g4VH1Y4w== + dependencies: + bignumber.js "^9.0.0" + buffer "^5.5.0" + commander "^2.15.0" + ieee754 "^1.1.13" + iso-url "~0.4.7" + json-text-sequence "~0.1.0" + readable-stream "^3.6.0" + boxen@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -6582,7 +6647,7 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@2, commander@^2.11.0, commander@^2.20.0: +commander@2, commander@^2.11.0, commander@^2.15.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -7856,6 +7921,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +delimit-stream@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/delimit-stream/-/delimit-stream-0.1.0.tgz#9b8319477c0e5f8aeb3ce357ae305fc25ea1cd2b" + integrity sha512-a02fiQ7poS5CnjiJBAsjGLPp5EwVoGHNeu9sziBd9huppRfsAFIpv5zNLv0V1gbop53ilngAf5Kf331AwcoRBQ== + denque@^1.1.0, denque@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" @@ -11247,6 +11317,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +iso-url@~0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-0.4.7.tgz#de7e48120dae46921079fe78f325ac9e9217a385" + integrity sha512-27fFRDnPAMnHGLq36bWTpKET+eiXct3ENlCcdcMdk+mjXrb2kw3mhBUg1B7ewAC0kVzlOPhADzQgz1SE6Tglog== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -11827,6 +11902,11 @@ jest@27.5.1: import-local "^3.0.2" jest-cli "^27.5.1" +js-sha256@0.9.0, js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -11942,6 +12022,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json-text-sequence@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2" + integrity sha512-L3mEegEWHRekSHjc7+sc8eJhba9Clq1PZ8kMkzf8OxElhXc8O4TS5MwcVlj9aEbm5dr81N90WHC5nAz3UO971w== + dependencies: + delimit-stream "0.1.0" + json5@2.x, json5@^2.1.2, json5@^2.1.3, json5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" @@ -13202,6 +13289,11 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + node-addon-api@^3.0.0, node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -13224,6 +13316,11 @@ node-gyp-build-optional-packages@5.0.2: resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz#3de7d30bd1f9057b5dfbaeab4a4442b7fe9c5901" integrity sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g== +node-gyp-build@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: version "4.4.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" @@ -15735,6 +15832,15 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +secp256k1@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + secure-compare@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" @@ -15950,6 +16056,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-cbor@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/simple-cbor/-/simple-cbor-0.4.1.tgz#0c88312e87db52b94e0e92f6bd1cf634e86f8a22" + integrity sha512-rijcxtwx2b4Bje3sqeIqw5EeW7UlOIC4YfOdwqIKacpvRQ/D78bWg/4/0m5e0U91oKvlGh7LlJuZCu07ISCC7w== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -17120,6 +17231,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== +tweetnacl@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + twitter-api-v2@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/twitter-api-v2/-/twitter-api-v2-1.10.3.tgz#07441bd9c4d27433aa0284d900cf60f6328b8239"