Browse Source

Refactoring

pull/5137/head
Thomas Kaul 1 month ago
parent
commit
058e0e2fe0
  1. 14
      apps/api/src/app/auth/auth.controller.ts
  2. 46
      apps/api/src/app/auth/web-auth.service.ts
  3. 4
      apps/client/src/app/services/web-authn.service.ts

14
apps/api/src/app/auth/auth.controller.ts

@ -133,17 +133,19 @@ export class AuthController {
return this.webAuthService.verifyAttestation(body.credential); return this.webAuthService.verifyAttestation(body.credential);
} }
@Post('webauthn/generate-assertion-options') @Post('webauthn/generate-authentication-options')
public async generateAssertionOptions(@Body() body: { deviceId: string }) { public async generateAuthenticationOptions(
return this.webAuthService.generateAssertionOptions(body.deviceId); @Body() body: { deviceId: string }
) {
return this.webAuthService.generateAuthenticationOptions(body.deviceId);
} }
@Post('webauthn/verify-assertion') @Post('webauthn/verify-authentication')
public async verifyAssertion( public async verifyAuthentication(
@Body() body: { deviceId: string; credential: AssertionCredentialJSON } @Body() body: { deviceId: string; credential: AssertionCredentialJSON }
) { ) {
try { try {
const authToken = await this.webAuthService.verifyAssertion( const authToken = await this.webAuthService.verifyAuthentication(
body.deviceId, body.deviceId,
body.credential body.credential
); );

46
apps/api/src/app/auth/web-auth.service.ts

@ -25,6 +25,7 @@ import {
VerifyRegistrationResponseOpts VerifyRegistrationResponseOpts
} from '@simplewebauthn/server'; } from '@simplewebauthn/server';
import { isoBase64URL, isoUint8Array } from '@simplewebauthn/server/helpers'; import { isoBase64URL, isoUint8Array } from '@simplewebauthn/server/helpers';
import ms from 'ms';
import { import {
AssertionCredentialJSON, AssertionCredentialJSON,
@ -41,42 +42,42 @@ export class WebAuthService {
@Inject(REQUEST) private readonly request: RequestWithUser @Inject(REQUEST) private readonly request: RequestWithUser
) {} ) {}
get rpID() { private get expectedOrigin() {
return new URL(this.configurationService.get('ROOT_URL')).hostname; return this.configurationService.get('ROOT_URL');
} }
get expectedOrigin() { private get rpID() {
return this.configurationService.get('ROOT_URL'); return new URL(this.configurationService.get('ROOT_URL')).hostname;
} }
public async generateRegistrationOptions() { public async generateRegistrationOptions() {
const user = this.request.user; const user = this.request.user;
const opts: GenerateRegistrationOptionsOpts = { const opts: GenerateRegistrationOptionsOpts = {
rpName: 'Ghostfolio',
rpID: this.rpID,
userID: isoUint8Array.fromUTF8String(user.id),
userName: '',
timeout: 60000,
authenticatorSelection: { authenticatorSelection: {
authenticatorAttachment: 'platform', authenticatorAttachment: 'platform',
residentKey: 'required', residentKey: 'required',
userVerification: 'preferred' userVerification: 'preferred'
} },
rpID: this.rpID,
rpName: 'Ghostfolio',
timeout: ms('60 seconds'),
userID: isoUint8Array.fromUTF8String(user.id),
userName: ''
}; };
const options = await generateRegistrationOptions(opts); const registrationOptions = await generateRegistrationOptions(opts);
await this.userService.updateUser({ await this.userService.updateUser({
data: { data: {
authChallenge: options.challenge authChallenge: registrationOptions.challenge
}, },
where: { where: {
id: user.id id: user.id
} }
}); });
return options; return registrationOptions;
} }
public async verifyAttestation( public async verifyAttestation(
@ -84,8 +85,8 @@ export class WebAuthService {
): Promise<AuthDeviceDto> { ): Promise<AuthDeviceDto> {
const user = this.request.user; const user = this.request.user;
const expectedChallenge = user.authChallenge; const expectedChallenge = user.authChallenge;
let verification: VerifiedRegistrationResponse; let verification: VerifiedRegistrationResponse;
try { try {
const opts: VerifyRegistrationResponseOpts = { const opts: VerifyRegistrationResponseOpts = {
expectedChallenge, expectedChallenge,
@ -100,6 +101,7 @@ export class WebAuthService {
type: 'public-key' type: 'public-key'
} }
}; };
verification = await verifyRegistrationResponse(opts); verification = await verifyRegistrationResponse(opts);
} catch (error) { } catch (error) {
Logger.error(error, 'WebAuthService'); Logger.error(error, 'WebAuthService');
@ -145,7 +147,7 @@ export class WebAuthService {
throw new InternalServerErrorException('An unknown error occurred'); throw new InternalServerErrorException('An unknown error occurred');
} }
public async generateAssertionOptions(deviceId: string) { public async generateAuthenticationOptions(deviceId: string) {
const device = await this.deviceService.authDevice({ id: deviceId }); const device = await this.deviceService.authDevice({ id: deviceId });
if (!device) { if (!device) {
@ -155,25 +157,25 @@ export class WebAuthService {
const opts: GenerateAuthenticationOptionsOpts = { const opts: GenerateAuthenticationOptionsOpts = {
allowCredentials: [], allowCredentials: [],
rpID: this.rpID, rpID: this.rpID,
timeout: 60000, timeout: ms('60 seconds'),
userVerification: 'preferred' userVerification: 'preferred'
}; };
const options = await generateAuthenticationOptions(opts); const authenticationOptions = await generateAuthenticationOptions(opts);
await this.userService.updateUser({ await this.userService.updateUser({
data: { data: {
authChallenge: options.challenge authChallenge: authenticationOptions.challenge
}, },
where: { where: {
id: device.userId id: device.userId
} }
}); });
return options; return authenticationOptions;
} }
public async verifyAssertion( public async verifyAuthentication(
deviceId: string, deviceId: string,
credential: AssertionCredentialJSON credential: AssertionCredentialJSON
) { ) {
@ -186,6 +188,7 @@ export class WebAuthService {
const user = await this.userService.user({ id: device.userId }); const user = await this.userService.user({ id: device.userId });
let verification: VerifiedAuthenticationResponse; let verification: VerifiedAuthenticationResponse;
try { try {
const opts: VerifyAuthenticationResponseOpts = { const opts: VerifyAuthenticationResponseOpts = {
credential: { credential: {
@ -205,13 +208,14 @@ export class WebAuthService {
type: 'public-key' type: 'public-key'
} }
}; };
verification = await verifyAuthenticationResponse(opts); verification = await verifyAuthenticationResponse(opts);
} catch (error) { } catch (error) {
Logger.error(error, 'WebAuthService'); Logger.error(error, 'WebAuthService');
throw new InternalServerErrorException({ error: error.message }); throw new InternalServerErrorException({ error: error.message });
} }
const { verified, authenticationInfo } = verification; const { authenticationInfo, verified } = verification;
if (verified) { if (verified) {
device.counter = authenticationInfo.newCounter; device.counter = authenticationInfo.newCounter;

4
apps/client/src/app/services/web-authn.service.ts

@ -85,7 +85,7 @@ export class WebAuthnService {
return this.http return this.http
.post<PublicKeyCredentialRequestOptionsJSON>( .post<PublicKeyCredentialRequestOptionsJSON>(
`/api/v1/auth/webauthn/generate-assertion-options`, '/api/v1/auth/webauthn/generate-authentication-options',
{ deviceId } { deviceId }
) )
.pipe( .pipe(
@ -94,7 +94,7 @@ export class WebAuthnService {
}), }),
switchMap((credential) => { switchMap((credential) => {
return this.http.post<{ authToken: string }>( return this.http.post<{ authToken: string }>(
`/api/v1/auth/webauthn/verify-assertion`, '/api/v1/auth/webauthn/verify-authentication',
{ {
credential, credential,
deviceId deviceId

Loading…
Cancel
Save