VerifyAuthenticatorRegistrationResponse.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. import { Dates, Result, UniqueEntityId, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
  2. import { VerifiedRegistrationResponse, verifyRegistrationResponse } from '@simplewebauthn/server'
  3. import { FeatureIdentifier } from '@standardnotes/features'
  4. import { AuthenticatorChallengeRepositoryInterface } from '../../Authenticator/AuthenticatorChallengeRepositoryInterface'
  5. import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
  6. import { Authenticator } from '../../Authenticator/Authenticator'
  7. import { VerifyAuthenticatorRegistrationResponseDTO } from './VerifyAuthenticatorRegistrationResponseDTO'
  8. import { FeatureServiceInterface } from '../../Feature/FeatureServiceInterface'
  9. import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
  10. export class VerifyAuthenticatorRegistrationResponse implements UseCaseInterface<UniqueEntityId> {
  11. constructor(
  12. private authenticatorRepository: AuthenticatorRepositoryInterface,
  13. private authenticatorChallengeRepository: AuthenticatorChallengeRepositoryInterface,
  14. private relyingPartyId: string,
  15. private expectedOrigin: string[],
  16. private requireUserVerification: boolean,
  17. private userRepository: UserRepositoryInterface,
  18. private featureService: FeatureServiceInterface,
  19. ) {}
  20. async execute(dto: VerifyAuthenticatorRegistrationResponseDTO): Promise<Result<UniqueEntityId>> {
  21. const userUuidOrError = Uuid.create(dto.userUuid)
  22. if (userUuidOrError.isFailed()) {
  23. return Result.fail(`Could not verify authenticator registration response: ${userUuidOrError.getError()}`)
  24. }
  25. const userUuid = userUuidOrError.getValue()
  26. const user = await this.userRepository.findOneByUuid(userUuid)
  27. if (user === null) {
  28. return Result.fail('Could not verify authenticator registration response: user not found.')
  29. }
  30. const userIsEntitledToU2F = await this.featureService.userIsEntitledToFeature(
  31. user,
  32. FeatureIdentifier.UniversalSecondFactor,
  33. )
  34. if (!userIsEntitledToU2F) {
  35. return Result.fail('Could not verify authenticator registration response: user is not entitled to U2F.')
  36. }
  37. const authenticatorChallenge = await this.authenticatorChallengeRepository.findByUserUuid(userUuid)
  38. if (!authenticatorChallenge) {
  39. return Result.fail('Could not verify authenticator registration response: challenge not found')
  40. }
  41. let verification: VerifiedRegistrationResponse
  42. try {
  43. verification = await verifyRegistrationResponse({
  44. response: dto.attestationResponse,
  45. expectedChallenge: authenticatorChallenge.props.challenge.toString(),
  46. expectedOrigin: this.expectedOrigin,
  47. expectedRPID: this.relyingPartyId,
  48. requireUserVerification: this.requireUserVerification,
  49. })
  50. if (!verification.verified) {
  51. return Result.fail('Could not verify authenticator registration response: verification failed')
  52. }
  53. } catch (error) {
  54. return Result.fail(`Could not verify authenticator registration response: ${(error as Error).message}`)
  55. }
  56. if (!verification.registrationInfo) {
  57. return Result.fail('Could not verify authenticator registration response: registration info not found')
  58. }
  59. const authenticatorOrError = Authenticator.create({
  60. userUuid,
  61. counter: verification.registrationInfo.counter,
  62. credentialBackedUp: verification.registrationInfo.credentialBackedUp,
  63. credentialDeviceType: verification.registrationInfo.credentialDeviceType,
  64. credentialId: verification.registrationInfo.credentialID,
  65. credentialPublicKey: verification.registrationInfo.credentialPublicKey,
  66. dates: Dates.create(new Date(), new Date()).getValue(),
  67. transports: dto.attestationResponse.response.transports,
  68. })
  69. if (authenticatorOrError.isFailed()) {
  70. return Result.fail(`Could not verify authenticator registration response: ${authenticatorOrError.getError()}`)
  71. }
  72. const authenticator = authenticatorOrError.getValue()
  73. await this.authenticatorRepository.save(authenticator)
  74. return Result.ok(authenticator.id)
  75. }
  76. }