|
|
|
@ -11,16 +11,16 @@ import {
|
|
|
|
|
import { REQUEST } from '@nestjs/core';
|
|
|
|
|
import { JwtService } from '@nestjs/jwt';
|
|
|
|
|
import {
|
|
|
|
|
GenerateAssertionOptionsOpts,
|
|
|
|
|
GenerateAttestationOptionsOpts,
|
|
|
|
|
VerifiedAssertion,
|
|
|
|
|
VerifiedAttestation,
|
|
|
|
|
VerifyAssertionResponseOpts,
|
|
|
|
|
VerifyAttestationResponseOpts,
|
|
|
|
|
generateAssertionOptions,
|
|
|
|
|
generateAttestationOptions,
|
|
|
|
|
verifyAssertionResponse,
|
|
|
|
|
verifyAttestationResponse
|
|
|
|
|
GenerateAuthenticationOptionsOpts,
|
|
|
|
|
GenerateRegistrationOptionsOpts,
|
|
|
|
|
VerifiedAuthenticationResponse,
|
|
|
|
|
VerifiedRegistrationResponse,
|
|
|
|
|
VerifyAuthenticationResponseOpts,
|
|
|
|
|
VerifyRegistrationResponseOpts,
|
|
|
|
|
generateAuthenticationOptions,
|
|
|
|
|
generateRegistrationOptions,
|
|
|
|
|
verifyAuthenticationResponse,
|
|
|
|
|
verifyRegistrationResponse
|
|
|
|
|
} from '@simplewebauthn/server';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
@ -46,10 +46,10 @@ export class WebAuthService {
|
|
|
|
|
return this.configurationService.get('ROOT_URL');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async generateAttestationOptions() {
|
|
|
|
|
public async generateRegistrationOptions() {
|
|
|
|
|
const user = this.request.user;
|
|
|
|
|
|
|
|
|
|
const opts: GenerateAttestationOptionsOpts = {
|
|
|
|
|
const opts: GenerateRegistrationOptionsOpts = {
|
|
|
|
|
rpName: 'Ghostfolio',
|
|
|
|
|
rpID: this.rpID,
|
|
|
|
|
userID: user.id,
|
|
|
|
@ -63,7 +63,7 @@ export class WebAuthService {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const options = generateAttestationOptions(opts);
|
|
|
|
|
const options = generateRegistrationOptions(opts);
|
|
|
|
|
|
|
|
|
|
await this.userService.updateUser({
|
|
|
|
|
data: {
|
|
|
|
@ -84,27 +84,27 @@ export class WebAuthService {
|
|
|
|
|
const user = this.request.user;
|
|
|
|
|
const expectedChallenge = user.authChallenge;
|
|
|
|
|
|
|
|
|
|
let verification: VerifiedAttestation;
|
|
|
|
|
let verification: VerifiedRegistrationResponse;
|
|
|
|
|
try {
|
|
|
|
|
const opts: VerifyAttestationResponseOpts = {
|
|
|
|
|
const opts: VerifyRegistrationResponseOpts = {
|
|
|
|
|
credential,
|
|
|
|
|
expectedChallenge,
|
|
|
|
|
expectedOrigin: this.expectedOrigin,
|
|
|
|
|
expectedRPID: this.rpID
|
|
|
|
|
};
|
|
|
|
|
verification = await verifyAttestationResponse(opts);
|
|
|
|
|
verification = await verifyRegistrationResponse(opts);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
throw new InternalServerErrorException(error.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { verified, attestationInfo } = verification;
|
|
|
|
|
const { registrationInfo, verified } = verification;
|
|
|
|
|
|
|
|
|
|
const devices = await this.deviceService.authDevices({
|
|
|
|
|
where: { userId: user.id }
|
|
|
|
|
});
|
|
|
|
|
if (verified && attestationInfo) {
|
|
|
|
|
const { credentialPublicKey, credentialID, counter } = attestationInfo;
|
|
|
|
|
if (registrationInfo && verified) {
|
|
|
|
|
const { counter, credentialID, credentialPublicKey } = registrationInfo;
|
|
|
|
|
|
|
|
|
|
let existingDevice = devices.find(
|
|
|
|
|
(device) => device.credentialId === credentialID
|
|
|
|
@ -115,9 +115,9 @@ export class WebAuthService {
|
|
|
|
|
* Add the returned device to the user's list of devices
|
|
|
|
|
*/
|
|
|
|
|
existingDevice = await this.deviceService.createAuthDevice({
|
|
|
|
|
counter,
|
|
|
|
|
credentialPublicKey,
|
|
|
|
|
credentialId: credentialID,
|
|
|
|
|
counter,
|
|
|
|
|
User: { connect: { id: user.id } }
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -138,20 +138,20 @@ export class WebAuthService {
|
|
|
|
|
throw new Error('Device not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const opts: GenerateAssertionOptionsOpts = {
|
|
|
|
|
timeout: 60000,
|
|
|
|
|
const opts: GenerateAuthenticationOptionsOpts = {
|
|
|
|
|
allowCredentials: [
|
|
|
|
|
{
|
|
|
|
|
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({
|
|
|
|
|
data: {
|
|
|
|
@ -177,29 +177,29 @@ export class WebAuthService {
|
|
|
|
|
|
|
|
|
|
const user = await this.userService.user({ id: device.userId });
|
|
|
|
|
|
|
|
|
|
let verification: VerifiedAssertion;
|
|
|
|
|
let verification: VerifiedAuthenticationResponse;
|
|
|
|
|
try {
|
|
|
|
|
const opts: VerifyAssertionResponseOpts = {
|
|
|
|
|
const opts: VerifyAuthenticationResponseOpts = {
|
|
|
|
|
credential,
|
|
|
|
|
expectedChallenge: `${user.authChallenge}`,
|
|
|
|
|
expectedOrigin: this.expectedOrigin,
|
|
|
|
|
expectedRPID: this.rpID,
|
|
|
|
|
authenticator: {
|
|
|
|
|
credentialID: device.credentialId,
|
|
|
|
|
credentialPublicKey: device.credentialPublicKey,
|
|
|
|
|
counter: device.counter
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
expectedChallenge: `${user.authChallenge}`,
|
|
|
|
|
expectedOrigin: this.expectedOrigin,
|
|
|
|
|
expectedRPID: this.rpID
|
|
|
|
|
};
|
|
|
|
|
verification = verifyAssertionResponse(opts);
|
|
|
|
|
verification = verifyAuthenticationResponse(opts);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
throw new InternalServerErrorException({ error: error.message });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { verified, assertionInfo } = verification;
|
|
|
|
|
const { verified, authenticationInfo } = verification;
|
|
|
|
|
|
|
|
|
|
if (verified) {
|
|
|
|
|
device.counter = assertionInfo.newCounter;
|
|
|
|
|
device.counter = authenticationInfo.newCounter;
|
|
|
|
|
|
|
|
|
|
await this.deviceService.updateAuthDevice({
|
|
|
|
|
data: device,
|
|
|
|
|