Feature/upgrade simplewebauthn dependencies to version 4.1.0 (#365)
* Upgrade @simplewebauthn dependencies to version 4.1.0 * @simplewebauthn/browser * @simplewebauthn/server * Update changelog
This commit is contained in:
parent
39f315aba0
commit
5ea455b98b
@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Added the annualized performance to the portfolio summary tab on the home page
|
- Added the annualized performance to the portfolio summary tab on the home page
|
||||||
- Added the Ghostfolio Slack channel to the about page
|
- Added the Ghostfolio Slack channel to the about page
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Upgraded `@simplewebauthn/browser` and `@simplewebauthn/server` from version `3.0.0` to `4.1.0`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed the sign in with fingerprint for some android devices
|
||||||
|
|
||||||
## 1.51.0 - 11.09.2021
|
## 1.51.0 - 11.09.2021
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -62,10 +62,10 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('webauthn/generate-attestation-options')
|
@Get('webauthn/generate-registration-options')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async generateAttestationOptions() {
|
public async generateRegistrationOptions() {
|
||||||
return this.webAuthService.generateAttestationOptions();
|
return this.webAuthService.generateRegistrationOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('webauthn/verify-attestation')
|
@Post('webauthn/verify-attestation')
|
||||||
|
@ -11,16 +11,16 @@ import {
|
|||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import {
|
import {
|
||||||
GenerateAssertionOptionsOpts,
|
GenerateAuthenticationOptionsOpts,
|
||||||
GenerateAttestationOptionsOpts,
|
GenerateRegistrationOptionsOpts,
|
||||||
VerifiedAssertion,
|
VerifiedAuthenticationResponse,
|
||||||
VerifiedAttestation,
|
VerifiedRegistrationResponse,
|
||||||
VerifyAssertionResponseOpts,
|
VerifyAuthenticationResponseOpts,
|
||||||
VerifyAttestationResponseOpts,
|
VerifyRegistrationResponseOpts,
|
||||||
generateAssertionOptions,
|
generateAuthenticationOptions,
|
||||||
generateAttestationOptions,
|
generateRegistrationOptions,
|
||||||
verifyAssertionResponse,
|
verifyAuthenticationResponse,
|
||||||
verifyAttestationResponse
|
verifyRegistrationResponse
|
||||||
} from '@simplewebauthn/server';
|
} from '@simplewebauthn/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -46,10 +46,10 @@ export class WebAuthService {
|
|||||||
return this.configurationService.get('ROOT_URL');
|
return this.configurationService.get('ROOT_URL');
|
||||||
}
|
}
|
||||||
|
|
||||||
public async generateAttestationOptions() {
|
public async generateRegistrationOptions() {
|
||||||
const user = this.request.user;
|
const user = this.request.user;
|
||||||
|
|
||||||
const opts: GenerateAttestationOptionsOpts = {
|
const opts: GenerateRegistrationOptionsOpts = {
|
||||||
rpName: 'Ghostfolio',
|
rpName: 'Ghostfolio',
|
||||||
rpID: this.rpID,
|
rpID: this.rpID,
|
||||||
userID: user.id,
|
userID: user.id,
|
||||||
@ -63,7 +63,7 @@ export class WebAuthService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = generateAttestationOptions(opts);
|
const options = generateRegistrationOptions(opts);
|
||||||
|
|
||||||
await this.userService.updateUser({
|
await this.userService.updateUser({
|
||||||
data: {
|
data: {
|
||||||
@ -84,27 +84,27 @@ export class WebAuthService {
|
|||||||
const user = this.request.user;
|
const user = this.request.user;
|
||||||
const expectedChallenge = user.authChallenge;
|
const expectedChallenge = user.authChallenge;
|
||||||
|
|
||||||
let verification: VerifiedAttestation;
|
let verification: VerifiedRegistrationResponse;
|
||||||
try {
|
try {
|
||||||
const opts: VerifyAttestationResponseOpts = {
|
const opts: VerifyRegistrationResponseOpts = {
|
||||||
credential,
|
credential,
|
||||||
expectedChallenge,
|
expectedChallenge,
|
||||||
expectedOrigin: this.expectedOrigin,
|
expectedOrigin: this.expectedOrigin,
|
||||||
expectedRPID: this.rpID
|
expectedRPID: this.rpID
|
||||||
};
|
};
|
||||||
verification = await verifyAttestationResponse(opts);
|
verification = await verifyRegistrationResponse(opts);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw new InternalServerErrorException(error.message);
|
throw new InternalServerErrorException(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { verified, attestationInfo } = verification;
|
const { registrationInfo, verified } = verification;
|
||||||
|
|
||||||
const devices = await this.deviceService.authDevices({
|
const devices = await this.deviceService.authDevices({
|
||||||
where: { userId: user.id }
|
where: { userId: user.id }
|
||||||
});
|
});
|
||||||
if (verified && attestationInfo) {
|
if (registrationInfo && verified) {
|
||||||
const { credentialPublicKey, credentialID, counter } = attestationInfo;
|
const { counter, credentialID, credentialPublicKey } = registrationInfo;
|
||||||
|
|
||||||
let existingDevice = devices.find(
|
let existingDevice = devices.find(
|
||||||
(device) => device.credentialId === credentialID
|
(device) => device.credentialId === credentialID
|
||||||
@ -115,9 +115,9 @@ export class WebAuthService {
|
|||||||
* Add the returned device to the user's list of devices
|
* Add the returned device to the user's list of devices
|
||||||
*/
|
*/
|
||||||
existingDevice = await this.deviceService.createAuthDevice({
|
existingDevice = await this.deviceService.createAuthDevice({
|
||||||
|
counter,
|
||||||
credentialPublicKey,
|
credentialPublicKey,
|
||||||
credentialId: credentialID,
|
credentialId: credentialID,
|
||||||
counter,
|
|
||||||
User: { connect: { id: user.id } }
|
User: { connect: { id: user.id } }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -138,20 +138,20 @@ export class WebAuthService {
|
|||||||
throw new Error('Device not found');
|
throw new Error('Device not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const opts: GenerateAssertionOptionsOpts = {
|
const opts: GenerateAuthenticationOptionsOpts = {
|
||||||
timeout: 60000,
|
|
||||||
allowCredentials: [
|
allowCredentials: [
|
||||||
{
|
{
|
||||||
id: device.credentialId,
|
id: device.credentialId,
|
||||||
type: 'public-key',
|
transports: ['internal'],
|
||||||
transports: ['internal']
|
type: 'public-key'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
userVerification: 'preferred',
|
rpID: this.rpID,
|
||||||
rpID: this.rpID
|
timeout: 60000,
|
||||||
|
userVerification: 'preferred'
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = generateAssertionOptions(opts);
|
const options = generateAuthenticationOptions(opts);
|
||||||
|
|
||||||
await this.userService.updateUser({
|
await this.userService.updateUser({
|
||||||
data: {
|
data: {
|
||||||
@ -177,29 +177,29 @@ export class WebAuthService {
|
|||||||
|
|
||||||
const user = await this.userService.user({ id: device.userId });
|
const user = await this.userService.user({ id: device.userId });
|
||||||
|
|
||||||
let verification: VerifiedAssertion;
|
let verification: VerifiedAuthenticationResponse;
|
||||||
try {
|
try {
|
||||||
const opts: VerifyAssertionResponseOpts = {
|
const opts: VerifyAuthenticationResponseOpts = {
|
||||||
credential,
|
credential,
|
||||||
expectedChallenge: `${user.authChallenge}`,
|
|
||||||
expectedOrigin: this.expectedOrigin,
|
|
||||||
expectedRPID: this.rpID,
|
|
||||||
authenticator: {
|
authenticator: {
|
||||||
credentialID: device.credentialId,
|
credentialID: device.credentialId,
|
||||||
credentialPublicKey: device.credentialPublicKey,
|
credentialPublicKey: device.credentialPublicKey,
|
||||||
counter: device.counter
|
counter: device.counter
|
||||||
}
|
},
|
||||||
|
expectedChallenge: `${user.authChallenge}`,
|
||||||
|
expectedOrigin: this.expectedOrigin,
|
||||||
|
expectedRPID: this.rpID
|
||||||
};
|
};
|
||||||
verification = verifyAssertionResponse(opts);
|
verification = verifyAuthenticationResponse(opts);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw new InternalServerErrorException({ error: error.message });
|
throw new InternalServerErrorException({ error: error.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { verified, assertionInfo } = verification;
|
const { verified, authenticationInfo } = verification;
|
||||||
|
|
||||||
if (verified) {
|
if (verified) {
|
||||||
device.counter = assertionInfo.newCounter;
|
device.counter = authenticationInfo.newCounter;
|
||||||
|
|
||||||
await this.deviceService.updateAuthDevice({
|
await this.deviceService.updateAuthDevice({
|
||||||
data: device,
|
data: device,
|
||||||
|
@ -2,8 +2,8 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { GfLogoModule } from '@ghostfolio/ui/logo';
|
|
||||||
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
import { WebauthnPageComponent } from '@ghostfolio/client/pages/webauthn/webauthn-page.component';
|
||||||
|
import { GfLogoModule } from '@ghostfolio/ui/logo';
|
||||||
|
|
||||||
import { WebauthnPageRoutingModule } from './webauthn-page-routing.module';
|
import { WebauthnPageRoutingModule } from './webauthn-page-routing.module';
|
||||||
|
|
||||||
|
@ -6,7 +6,10 @@ import {
|
|||||||
PublicKeyCredentialRequestOptionsJSON
|
PublicKeyCredentialRequestOptionsJSON
|
||||||
} from '@ghostfolio/api/app/auth/interfaces/simplewebauthn';
|
} from '@ghostfolio/api/app/auth/interfaces/simplewebauthn';
|
||||||
import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service';
|
import { SettingsStorageService } from '@ghostfolio/client/services/settings-storage.service';
|
||||||
import { startAssertion, startAttestation } from '@simplewebauthn/browser';
|
import {
|
||||||
|
startAuthentication,
|
||||||
|
startRegistration
|
||||||
|
} from '@simplewebauthn/browser';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { catchError, switchMap, tap } from 'rxjs/operators';
|
import { catchError, switchMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ export class WebAuthnService {
|
|||||||
public register() {
|
public register() {
|
||||||
return this.http
|
return this.http
|
||||||
.get<PublicKeyCredentialCreationOptionsJSON>(
|
.get<PublicKeyCredentialCreationOptionsJSON>(
|
||||||
`/api/auth/webauthn/generate-attestation-options`,
|
`/api/auth/webauthn/generate-registration-options`,
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -41,7 +44,7 @@ export class WebAuthnService {
|
|||||||
return of(null);
|
return of(null);
|
||||||
}),
|
}),
|
||||||
switchMap((attOps) => {
|
switchMap((attOps) => {
|
||||||
return startAttestation(attOps);
|
return startRegistration(attOps);
|
||||||
}),
|
}),
|
||||||
switchMap((attResp) => {
|
switchMap((attResp) => {
|
||||||
return this.http.post<AuthDeviceDto>(
|
return this.http.post<AuthDeviceDto>(
|
||||||
@ -83,7 +86,7 @@ export class WebAuthnService {
|
|||||||
{ deviceId }
|
{ deviceId }
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(startAssertion),
|
switchMap(startAuthentication),
|
||||||
switchMap((assertionResponse) => {
|
switchMap((assertionResponse) => {
|
||||||
return this.http.post<{ authToken: string }>(
|
return this.http.post<{ authToken: string }>(
|
||||||
`/api/auth/webauthn/verify-assertion`,
|
`/api/auth/webauthn/verify-assertion`,
|
||||||
|
@ -68,9 +68,9 @@
|
|||||||
"@nestjs/serve-static": "2.1.4",
|
"@nestjs/serve-static": "2.1.4",
|
||||||
"@nrwl/angular": "12.8.0",
|
"@nrwl/angular": "12.8.0",
|
||||||
"@prisma/client": "2.30.2",
|
"@prisma/client": "2.30.2",
|
||||||
"@simplewebauthn/browser": "3.0.0",
|
"@simplewebauthn/browser": "4.1.0",
|
||||||
"@simplewebauthn/server": "3.0.0",
|
"@simplewebauthn/server": "4.1.0",
|
||||||
"@simplewebauthn/typescript-types": "3.0.0",
|
"@simplewebauthn/typescript-types": "4.0.0",
|
||||||
"@stripe/stripe-js": "1.15.0",
|
"@stripe/stripe-js": "1.15.0",
|
||||||
"alphavantage": "2.2.0",
|
"alphavantage": "2.2.0",
|
||||||
"angular-material-css-vars": "2.1.2",
|
"angular-material-css-vars": "2.1.2",
|
||||||
|
42
yarn.lock
42
yarn.lock
@ -2519,7 +2519,7 @@
|
|||||||
consola "^2.15.0"
|
consola "^2.15.0"
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
|
|
||||||
"@peculiar/asn1-android@^2.0.26":
|
"@peculiar/asn1-android@^2.0.38":
|
||||||
version "2.0.38"
|
version "2.0.38"
|
||||||
resolved "https://registry.yarnpkg.com/@peculiar/asn1-android/-/asn1-android-2.0.38.tgz#193281f5a232e323d6f2c069c7a8e8e8f4a994bd"
|
resolved "https://registry.yarnpkg.com/@peculiar/asn1-android/-/asn1-android-2.0.38.tgz#193281f5a232e323d6f2c069c7a8e8e8f4a994bd"
|
||||||
integrity sha512-krWyggV6FgYf3fEPKVNjHVecLcQWlAu3/YhOyN+/L43dNKcsmqiEvuhqplh3aiXF62Ds0pqzqttWmdvoVqmSVQ==
|
integrity sha512-krWyggV6FgYf3fEPKVNjHVecLcQWlAu3/YhOyN+/L43dNKcsmqiEvuhqplh3aiXF62Ds0pqzqttWmdvoVqmSVQ==
|
||||||
@ -2528,7 +2528,7 @@
|
|||||||
asn1js "^2.1.1"
|
asn1js "^2.1.1"
|
||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
|
|
||||||
"@peculiar/asn1-schema@^2.0.26", "@peculiar/asn1-schema@^2.0.38":
|
"@peculiar/asn1-schema@^2.0.38":
|
||||||
version "2.0.38"
|
version "2.0.38"
|
||||||
resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.38.tgz#98b6f12daad275ecd6774dfe31fb62f362900412"
|
resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.0.38.tgz#98b6f12daad275ecd6774dfe31fb62f362900412"
|
||||||
integrity sha512-zZ64UpCTm9me15nuCpPgJghSdbEm8atcDQPCyK+bKXjZAQ1735NCZXCSCfbckbQ4MH36Rm9403n/qMq77LFDzQ==
|
integrity sha512-zZ64UpCTm9me15nuCpPgJghSdbEm8atcDQPCyK+bKXjZAQ1735NCZXCSCfbckbQ4MH36Rm9403n/qMq77LFDzQ==
|
||||||
@ -2538,7 +2538,7 @@
|
|||||||
pvtsutils "^1.2.0"
|
pvtsutils "^1.2.0"
|
||||||
tslib "^2.3.0"
|
tslib "^2.3.0"
|
||||||
|
|
||||||
"@peculiar/asn1-x509@^2.0.26":
|
"@peculiar/asn1-x509@^2.0.38":
|
||||||
version "2.0.38"
|
version "2.0.38"
|
||||||
resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509/-/asn1-x509-2.0.38.tgz#7ff3b5478d9c3784f0eb2fbe7693509da9de0a43"
|
resolved "https://registry.yarnpkg.com/@peculiar/asn1-x509/-/asn1-x509-2.0.38.tgz#7ff3b5478d9c3784f0eb2fbe7693509da9de0a43"
|
||||||
integrity sha512-10aK9fSxlc1DK9nEcwh+WPFNhAheXSE9RbI5MyS7FdBhgq+Mz4Z9JqFfaBZm1Qp+5mPtUMOP6cXVo7aaYlgq7A==
|
integrity sha512-10aK9fSxlc1DK9nEcwh+WPFNhAheXSE9RbI5MyS7FdBhgq+Mz4Z9JqFfaBZm1Qp+5mPtUMOP6cXVo7aaYlgq7A==
|
||||||
@ -2613,32 +2613,32 @@
|
|||||||
"@angular-devkit/schematics" "12.1.4"
|
"@angular-devkit/schematics" "12.1.4"
|
||||||
jsonc-parser "3.0.0"
|
jsonc-parser "3.0.0"
|
||||||
|
|
||||||
"@simplewebauthn/browser@3.0.0":
|
"@simplewebauthn/browser@4.1.0":
|
||||||
version "3.0.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@simplewebauthn/browser/-/browser-3.0.0.tgz#3d76b199c9f474408a7ed75d86004423dd6ae38a"
|
resolved "https://registry.yarnpkg.com/@simplewebauthn/browser/-/browser-4.1.0.tgz#3e7fd66729405d6a2a2a187c93577b90a8e41786"
|
||||||
integrity sha512-P661gZX/QW0Rg2NRAMtW84Q3u4nhXkPef9LLU4btLJFYoXO8RBFfxcmyqwyf2QEb4B7+lFdp5EWfZV5T7FvuHw==
|
integrity sha512-tIsEfShC1rrqrsNb44tOFuSriAFCz4tkdDnCjHfn2rYxgz+t+yqEvuIRfJHQpFrWSnZPdsjrAHtasj6lzfGI6w==
|
||||||
|
|
||||||
"@simplewebauthn/server@3.0.0":
|
"@simplewebauthn/server@4.1.0":
|
||||||
version "3.0.0"
|
version "4.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@simplewebauthn/server/-/server-3.0.0.tgz#eb1a5bbe2ecdda54363b178f4bb3e134f25641f0"
|
resolved "https://registry.yarnpkg.com/@simplewebauthn/server/-/server-4.1.0.tgz#9ad2e32cffa83833ff8a633775b2ace5e6926fa0"
|
||||||
integrity sha512-ymGX2obBrhY9R3OxrpCYaNGAovFHmMlQrGoNdVOe2R2JUBXC1Rg5JEUl1lGyaRykN1SyZqLgz86wAjDVuRITTA==
|
integrity sha512-52X5/U+5Fo0XYG1TuBBGgG0ap9c0ffpeq0GZfFio/DZDW4He0Arb7Q/XkHw96JK0X1sfRKNmnfC+NImplvIimA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@peculiar/asn1-android" "^2.0.26"
|
"@peculiar/asn1-android" "^2.0.38"
|
||||||
"@peculiar/asn1-schema" "^2.0.26"
|
"@peculiar/asn1-schema" "^2.0.38"
|
||||||
"@peculiar/asn1-x509" "^2.0.26"
|
"@peculiar/asn1-x509" "^2.0.38"
|
||||||
"@simplewebauthn/typescript-types" "^3.0.0"
|
"@simplewebauthn/typescript-types" "^4.0.0"
|
||||||
base64url "^3.0.1"
|
base64url "^3.0.1"
|
||||||
cbor "^5.1.0"
|
cbor "^5.1.0"
|
||||||
elliptic "^6.5.3"
|
elliptic "^6.5.3"
|
||||||
jsrsasign "^10.2.0"
|
jsrsasign "^10.4.0"
|
||||||
jwk-to-pem "^2.0.4"
|
jwk-to-pem "^2.0.4"
|
||||||
node-fetch "^2.6.0"
|
node-fetch "^2.6.0"
|
||||||
node-rsa "^1.1.1"
|
node-rsa "^1.1.1"
|
||||||
|
|
||||||
"@simplewebauthn/typescript-types@3.0.0", "@simplewebauthn/typescript-types@^3.0.0":
|
"@simplewebauthn/typescript-types@4.0.0", "@simplewebauthn/typescript-types@^4.0.0":
|
||||||
version "3.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@simplewebauthn/typescript-types/-/typescript-types-3.0.0.tgz#6712e9619d860f54f571cd27dbe167b2d9e5ab87"
|
resolved "https://registry.yarnpkg.com/@simplewebauthn/typescript-types/-/typescript-types-4.0.0.tgz#46ae4e69cb07305c57093a3ed99555437dfe0d49"
|
||||||
integrity sha512-bsk3EQWzPOZwP9C+ETVhcFDpZywY5sTqmNuGkNm3aNpc9Xh/mqZjy8nL0Sm7xwrlhY0zWAlOaIWQ3LvN5SoFhg==
|
integrity sha512-jqQ0bCeBO96CytB397vSrQ8ipozQzAmI57izA7izyglyu35JBV90I7+75fSX+ZGNHmMwDNnA3EGYtBLOIpkJEg==
|
||||||
|
|
||||||
"@sinonjs/commons@^1.7.0":
|
"@sinonjs/commons@^1.7.0":
|
||||||
version "1.8.3"
|
version "1.8.3"
|
||||||
@ -11343,7 +11343,7 @@ jsprim@^1.2.2:
|
|||||||
json-schema "0.2.3"
|
json-schema "0.2.3"
|
||||||
verror "1.10.0"
|
verror "1.10.0"
|
||||||
|
|
||||||
jsrsasign@^10.2.0:
|
jsrsasign@^10.4.0:
|
||||||
version "10.4.0"
|
version "10.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.4.0.tgz#362cc787079c03a363a958c03eb68d8545ba92f7"
|
resolved "https://registry.yarnpkg.com/jsrsasign/-/jsrsasign-10.4.0.tgz#362cc787079c03a363a958c03eb68d8545ba92f7"
|
||||||
integrity sha512-C8qLhiAssh/b74KJpGhWuFGG9cFhJqMCVuuHXRibb3Z5vPuAW0ue0jUirpoExCdpdhv4nD3sZ1DAwJURYJTm9g==
|
integrity sha512-C8qLhiAssh/b74KJpGhWuFGG9cFhJqMCVuuHXRibb3Z5vPuAW0ue0jUirpoExCdpdhv4nD3sZ1DAwJURYJTm9g==
|
||||||
|
Loading…
x
Reference in New Issue
Block a user