diff --git a/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts b/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts index b58fbb0ed..5c6fc57e2 100644 --- a/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts +++ b/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts @@ -11,6 +11,11 @@ export class AuthenticatorsController extends BaseHttpController { super() } + @httpGet('/') + async list(request: Request, response: Response): Promise { + await this.httpService.callAuthServer(request, response, 'authenticators/', request.body) + } + @httpGet('/generate-registration-options') async generateRegistrationOptions(request: Request, response: Response): Promise { await this.httpService.callAuthServer( diff --git a/packages/auth/migrations/1672317378817-add_authenticator_name.ts b/packages/auth/migrations/1672317378817-add_authenticator_name.ts new file mode 100644 index 000000000..e2eb81e49 --- /dev/null +++ b/packages/auth/migrations/1672317378817-add_authenticator_name.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class addAuthenticatorName1672317378817 implements MigrationInterface { + name = 'addAuthenticatorName1672317378817' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE `authenticators` ADD `name` varchar(255) NOT NULL') + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE `authenticators` DROP COLUMN `name`') + } +} diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index e7bf832e3..43692321b 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -218,6 +218,9 @@ import { VerifyAuthenticatorRegistrationResponse } from '../Domain/UseCase/Verif import { GenerateAuthenticatorAuthenticationOptions } from '../Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions' import { VerifyAuthenticatorAuthenticationResponse } from '../Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse' import { AuthenticatorsController } from '../Controller/AuthenticatorsController' +import { ListAuthenticators } from '../Domain/UseCase/ListAuthenticators/ListAuthenticators' +import { AuthenticatorHttpProjection } from '../Infra/Http/Projection/AuthenticatorHttpProjection' +import { AuthenticatorHttpMapper } from '../Mapping/AuthenticatorHttpMapper' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -298,6 +301,9 @@ export class ContainerConfigLoader { container .bind>(TYPES.AuthenticatorPersistenceMapper) .toConstantValue(new AuthenticatorPersistenceMapper()) + container + .bind>(TYPES.AuthenticatorHttpMapper) + .toConstantValue(new AuthenticatorHttpMapper()) container .bind>( TYPES.AuthenticatorChallengePersistenceMapper, @@ -583,6 +589,9 @@ export class ContainerConfigLoader { container.get(TYPES.AuthenticatorChallengeRepository), ), ) + container + .bind(TYPES.ListAuthenticators) + .toConstantValue(new ListAuthenticators(container.get(TYPES.AuthenticatorRepository))) container .bind(TYPES.CleanupSessionTraces) @@ -647,6 +656,8 @@ export class ContainerConfigLoader { container.get(TYPES.VerifyAuthenticatorRegistrationResponse), container.get(TYPES.GenerateAuthenticatorAuthenticationOptions), container.get(TYPES.VerifyAuthenticatorAuthenticationResponse), + container.get(TYPES.ListAuthenticators), + container.get(TYPES.AuthenticatorHttpMapper), ), ) container.bind(TYPES.SubscriptionInvitesController).to(SubscriptionInvitesController) diff --git a/packages/auth/src/Bootstrap/Types.ts b/packages/auth/src/Bootstrap/Types.ts index deb409b4a..c5e3ffbc3 100644 --- a/packages/auth/src/Bootstrap/Types.ts +++ b/packages/auth/src/Bootstrap/Types.ts @@ -7,6 +7,7 @@ const TYPES = { SessionTracePersistenceMapper: Symbol.for('SessionTracePersistenceMapper'), AuthenticatorChallengePersistenceMapper: Symbol.for('AuthenticatorChallengePersistenceMapper'), AuthenticatorPersistenceMapper: Symbol.for('AuthenticatorPersistenceMapper'), + AuthenticatorHttpMapper: Symbol.for('AuthenticatorHttpMapper'), // Controller AuthController: Symbol.for('AuthController'), AuthenticatorsController: Symbol.for('AuthenticatorsController'), @@ -138,6 +139,7 @@ const TYPES = { VerifyAuthenticatorRegistrationResponse: Symbol.for('VerifyAuthenticatorRegistrationResponse'), GenerateAuthenticatorAuthenticationOptions: Symbol.for('GenerateAuthenticatorAuthenticationOptions'), VerifyAuthenticatorAuthenticationResponse: Symbol.for('VerifyAuthenticatorAuthenticationResponse'), + ListAuthenticators: Symbol.for('ListAuthenticators'), // Handlers UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'), AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'), diff --git a/packages/auth/src/Controller/AuthenticatorsController.ts b/packages/auth/src/Controller/AuthenticatorsController.ts index eac45e7b3..4c5ed11ae 100644 --- a/packages/auth/src/Controller/AuthenticatorsController.ts +++ b/packages/auth/src/Controller/AuthenticatorsController.ts @@ -1,15 +1,21 @@ import { HttpStatusCode } from '@standardnotes/api' +import { MapperInterface } from '@standardnotes/domain-core' +import { Authenticator } from '../Domain/Authenticator/Authenticator' import { GenerateAuthenticatorAuthenticationOptions } from '../Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions' import { GenerateAuthenticatorRegistrationOptions } from '../Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions' +import { ListAuthenticators } from '../Domain/UseCase/ListAuthenticators/ListAuthenticators' import { VerifyAuthenticatorAuthenticationResponse } from '../Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse' import { VerifyAuthenticatorRegistrationResponse } from '../Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse' +import { AuthenticatorHttpProjection } from '../Infra/Http/Projection/AuthenticatorHttpProjection' import { GenerateAuthenticatorAuthenticationOptionsRequestParams } from '../Infra/Http/Request/GenerateAuthenticatorAuthenticationOptionsRequestParams' import { GenerateAuthenticatorRegistrationOptionsRequestParams } from '../Infra/Http/Request/GenerateAuthenticatorRegistrationOptionsRequestParams' +import { ListAuthenticatorsRequestParams } from '../Infra/Http/Request/ListAuthenticatorsRequestParams' import { VerifyAuthenticatorAuthenticationResponseRequestParams } from '../Infra/Http/Request/VerifyAuthenticatorAuthenticationResponseRequestParams' import { VerifyAuthenticatorRegistrationResponseRequestParams } from '../Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams' import { GenerateAuthenticatorAuthenticationOptionsResponse } from '../Infra/Http/Response/GenerateAuthenticatorAuthenticationOptionsResponse' import { GenerateAuthenticatorRegistrationOptionsResponse } from '../Infra/Http/Response/GenerateAuthenticatorRegistrationOptionsResponse' +import { ListAuthenticatorsResponse } from '../Infra/Http/Response/ListAuthenticatorsResponse' import { VerifyAuthenticatorAuthenticationResponseResponse } from '../Infra/Http/Response/VerifyAuthenticatorAuthenticationResponseResponse' import { VerifyAuthenticatorRegistrationResponseResponse } from '../Infra/Http/Response/VerifyAuthenticatorRegistrationResponseResponse' @@ -19,8 +25,25 @@ export class AuthenticatorsController { private verifyAuthenticatorRegistrationResponse: VerifyAuthenticatorRegistrationResponse, private generateAuthenticatorAuthenticationOptions: GenerateAuthenticatorAuthenticationOptions, private verifyAuthenticatorAuthenticationResponse: VerifyAuthenticatorAuthenticationResponse, + private listAuthenticators: ListAuthenticators, + private authenticatorHttpMapper: MapperInterface, ) {} + async list(params: ListAuthenticatorsRequestParams): Promise { + const result = await this.listAuthenticators.execute({ + userUuid: params.userUuid, + }) + + return { + status: HttpStatusCode.Success, + data: { + authenticators: result + .getValue() + .map((authenticator) => this.authenticatorHttpMapper.toProjection(authenticator)), + }, + } + } + async generateRegistrationOptions( params: GenerateAuthenticatorRegistrationOptionsRequestParams, ): Promise { @@ -51,6 +74,7 @@ export class AuthenticatorsController { ): Promise { const result = await this.verifyAuthenticatorRegistrationResponse.execute({ userUuid: params.userUuid, + name: params.name, registrationCredential: params.registrationCredential, }) diff --git a/packages/auth/src/Domain/Authenticator/Authenticator.spec.ts b/packages/auth/src/Domain/Authenticator/Authenticator.spec.ts index c0fa4c7a4..16c555110 100644 --- a/packages/auth/src/Domain/Authenticator/Authenticator.spec.ts +++ b/packages/auth/src/Domain/Authenticator/Authenticator.spec.ts @@ -6,6 +6,7 @@ describe('Authenticator', () => { it('should create an entity', () => { const entityOrError = Authenticator.create({ counter: 1, + name: 'my-key', credentialBackedUp: true, credentialDeviceType: 'singleDevice', credentialId: Buffer.from('credentialId'), diff --git a/packages/auth/src/Domain/Authenticator/AuthenticatorProps.ts b/packages/auth/src/Domain/Authenticator/AuthenticatorProps.ts index 270437296..def60bae7 100644 --- a/packages/auth/src/Domain/Authenticator/AuthenticatorProps.ts +++ b/packages/auth/src/Domain/Authenticator/AuthenticatorProps.ts @@ -1,6 +1,7 @@ import { Dates, Uuid } from '@standardnotes/domain-core' export interface AuthenticatorProps { + name: string userUuid: Uuid credentialId: Buffer credentialPublicKey: Buffer diff --git a/packages/auth/src/Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions.spec.ts b/packages/auth/src/Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions.spec.ts index 142b8e63a..f0c28f12d 100644 --- a/packages/auth/src/Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions.spec.ts +++ b/packages/auth/src/Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions.spec.ts @@ -16,6 +16,7 @@ describe('GenerateAuthenticatorAuthenticationOptions', () => { beforeEach(() => { const authenticator = Authenticator.create({ counter: 1, + name: 'my-key', credentialBackedUp: true, credentialDeviceType: 'singleDevice', credentialId: Buffer.from('credentialId'), diff --git a/packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.spec.ts b/packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.spec.ts index 832a6f568..0f9d3bbc9 100644 --- a/packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.spec.ts +++ b/packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.spec.ts @@ -16,6 +16,7 @@ describe('GenerateAuthenticatorRegistrationOptions', () => { beforeEach(() => { const authenticator = Authenticator.create({ counter: 1, + name: 'my-key', credentialBackedUp: true, credentialDeviceType: 'singleDevice', credentialId: Buffer.from('credentialId'), diff --git a/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.spec.ts b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.spec.ts new file mode 100644 index 000000000..af6b0953a --- /dev/null +++ b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.spec.ts @@ -0,0 +1,30 @@ +import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface' +import { ListAuthenticators } from './ListAuthenticators' + +describe('ListAuthenticators', () => { + let authenticatorRepository: AuthenticatorRepositoryInterface + + const createUseCase = () => new ListAuthenticators(authenticatorRepository) + + beforeEach(() => { + authenticatorRepository = {} as jest.Mocked + authenticatorRepository.findByUserUuid = jest.fn().mockReturnValue([]) + }) + + it('should list authenticators', async () => { + const useCase = createUseCase() + + const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000' }) + + expect(result.isFailed()).toBeFalsy() + expect(authenticatorRepository.findByUserUuid).toHaveBeenCalled() + }) + + it('should fail if user uuid is invalid', async () => { + const useCase = createUseCase() + + const result = await useCase.execute({ userUuid: 'invalid' }) + + expect(result.isFailed()).toBeTruthy() + }) +}) diff --git a/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.ts b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.ts new file mode 100644 index 000000000..6e14fdf52 --- /dev/null +++ b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.ts @@ -0,0 +1,20 @@ +import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core' + +import { Authenticator } from '../../Authenticator/Authenticator' +import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface' +import { ListAuthenticatorsDTO } from './ListAuthenticatorsDTO' + +export class ListAuthenticators implements UseCaseInterface { + constructor(private authenticatorRepository: AuthenticatorRepositoryInterface) {} + async execute(dto: ListAuthenticatorsDTO): Promise> { + const userUuidOrError = Uuid.create(dto.userUuid) + if (userUuidOrError.isFailed()) { + return Result.fail(`Could not list authenticators: ${userUuidOrError.getError()}`) + } + const userUuid = userUuidOrError.getValue() + + const authenticators = await this.authenticatorRepository.findByUserUuid(userUuid) + + return Result.ok(authenticators) + } +} diff --git a/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticatorsDTO.ts b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticatorsDTO.ts new file mode 100644 index 000000000..54f6a6bda --- /dev/null +++ b/packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticatorsDTO.ts @@ -0,0 +1,3 @@ +export interface ListAuthenticatorsDTO { + userUuid: string +} diff --git a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse.spec.ts b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse.spec.ts index aa5631700..eb66eb670 100644 --- a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse.spec.ts +++ b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorAuthenticationResponse/VerifyAuthenticatorAuthenticationResponse.spec.ts @@ -18,6 +18,7 @@ describe('VerifyAuthenticatorAuthenticationResponse', () => { beforeEach(() => { const authenticator = Authenticator.create({ counter: 1, + name: 'my-key', credentialBackedUp: true, credentialDeviceType: 'singleDevice', credentialId: Buffer.from('credentialId'), diff --git a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.spec.ts b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.spec.ts index e67a7dcc3..e9bc565bc 100644 --- a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.spec.ts +++ b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.spec.ts @@ -32,6 +32,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: 'invalid', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -49,6 +50,27 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { ) }) + it('should return error if name is invalid', async () => { + const useCase = createUseCase() + + const result = await useCase.execute({ + userUuid: '00000000-0000-0000-0000-000000000000', + name: '', + registrationCredential: { + id: Buffer.from('id'), + rawId: Buffer.from('rawId'), + response: { + attestationObject: Buffer.from('attestationObject'), + clientDataJSON: Buffer.from('clientDataJSON'), + }, + type: 'type', + }, + }) + + expect(result.isFailed()).toBeTruthy() + expect(result.getError()).toEqual('Could not verify authenticator registration response: Given value is empty: ') + }) + it('should return error if challenge is not found', async () => { authenticatorChallengeRepository.findByUserUuid = jest.fn().mockReturnValue(null) @@ -56,6 +78,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -96,6 +119,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -129,6 +153,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -164,6 +189,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -213,6 +239,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), @@ -256,6 +283,7 @@ describe('VerifyAuthenticatorRegistrationResponse', () => { const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000', + name: 'name', registrationCredential: { id: Buffer.from('id'), rawId: Buffer.from('rawId'), diff --git a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts index ed462934f..4ead63f24 100644 --- a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts +++ b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts @@ -1,4 +1,4 @@ -import { Dates, Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core' +import { Dates, Result, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core' import { VerifiedRegistrationResponse, verifyRegistrationResponse } from '@simplewebauthn/server' import { AuthenticatorChallengeRepositoryInterface } from '../../Authenticator/AuthenticatorChallengeRepositoryInterface' @@ -20,6 +20,11 @@ export class VerifyAuthenticatorRegistrationResponse implements UseCaseInterface } const userUuid = userUuidOrError.getValue() + const nameValidation = Validator.isNotEmpty(dto.name) + if (nameValidation.isFailed()) { + return Result.fail(`Could not verify authenticator registration response: ${nameValidation.getError()}`) + } + const authenticatorChallenge = await this.authenticatorChallengeRepository.findByUserUuid(userUuid) if (!authenticatorChallenge) { return Result.fail('Could not verify authenticator registration response: challenge not found') @@ -47,6 +52,7 @@ export class VerifyAuthenticatorRegistrationResponse implements UseCaseInterface const authenticatorOrError = Authenticator.create({ userUuid, + name: dto.name, counter: verification.registrationInfo.counter, credentialBackedUp: verification.registrationInfo.credentialBackedUp, credentialDeviceType: verification.registrationInfo.credentialDeviceType, diff --git a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponseDTO.ts b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponseDTO.ts index bc2e76b60..cdc82a3df 100644 --- a/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponseDTO.ts +++ b/packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponseDTO.ts @@ -1,4 +1,5 @@ export interface VerifyAuthenticatorRegistrationResponseDTO { userUuid: string + name: string registrationCredential: Record } diff --git a/packages/auth/src/Infra/Http/Projection/AuthenticatorHttpProjection.ts b/packages/auth/src/Infra/Http/Projection/AuthenticatorHttpProjection.ts new file mode 100644 index 000000000..e7bdafb2e --- /dev/null +++ b/packages/auth/src/Infra/Http/Projection/AuthenticatorHttpProjection.ts @@ -0,0 +1,4 @@ +export interface AuthenticatorHttpProjection { + id: string + name: string +} diff --git a/packages/auth/src/Infra/Http/Request/ListAuthenticatorsRequestParams.ts b/packages/auth/src/Infra/Http/Request/ListAuthenticatorsRequestParams.ts new file mode 100644 index 000000000..14be3bc27 --- /dev/null +++ b/packages/auth/src/Infra/Http/Request/ListAuthenticatorsRequestParams.ts @@ -0,0 +1,3 @@ +export interface ListAuthenticatorsRequestParams { + userUuid: string +} diff --git a/packages/auth/src/Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams.ts b/packages/auth/src/Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams.ts index 82e290eb6..c0ffe0992 100644 --- a/packages/auth/src/Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams.ts +++ b/packages/auth/src/Infra/Http/Request/VerifyAuthenticatorRegistrationResponseRequestParams.ts @@ -1,4 +1,5 @@ export interface VerifyAuthenticatorRegistrationResponseRequestParams { userUuid: string + name: string registrationCredential: Record } diff --git a/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponse.ts b/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponse.ts new file mode 100644 index 000000000..05649cafd --- /dev/null +++ b/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponse.ts @@ -0,0 +1,8 @@ +import { HttpErrorResponseBody, HttpResponse } from '@standardnotes/api' +import { Either } from '@standardnotes/common' + +import { ListAuthenticatorsResponseBody } from './ListAuthenticatorsResponseBody' + +export interface ListAuthenticatorsResponse extends HttpResponse { + data: Either +} diff --git a/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponseBody.ts b/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponseBody.ts new file mode 100644 index 000000000..791be5292 --- /dev/null +++ b/packages/auth/src/Infra/Http/Response/ListAuthenticatorsResponseBody.ts @@ -0,0 +1,5 @@ +import { AuthenticatorHttpProjection } from '../Projection/AuthenticatorHttpProjection' + +export interface ListAuthenticatorsResponseBody { + authenticators: AuthenticatorHttpProjection[] +} diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts index 07cb508f5..5b8f1d2e0 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts @@ -17,6 +17,15 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController super() } + @httpGet('/') + async list(_request: Request, response: Response): Promise { + const result = await this.authenticatorsController.list({ + userUuid: response.locals.user.uuid, + }) + + return this.json(result.data, result.status) + } + @httpGet('/generate-registration-options') async generateRegistrationOptions(_request: Request, response: Response): Promise { const result = await this.authenticatorsController.generateRegistrationOptions({ @@ -31,7 +40,8 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController async verifyRegistration(request: Request, response: Response): Promise { const result = await this.authenticatorsController.verifyRegistrationResponse({ userUuid: response.locals.user.uuid, - registrationCredential: request.body, + registrationCredential: request.body.registrationCredential, + name: request.body.name, }) return this.json(result.data, result.status) diff --git a/packages/auth/src/Infra/TypeORM/TypeORMAuthenticator.ts b/packages/auth/src/Infra/TypeORM/TypeORMAuthenticator.ts index 135b683b4..f619d5593 100644 --- a/packages/auth/src/Infra/TypeORM/TypeORMAuthenticator.ts +++ b/packages/auth/src/Infra/TypeORM/TypeORMAuthenticator.ts @@ -11,6 +11,13 @@ export class TypeORMAuthenticator { }) declare userUuid: string + @Column({ + name: 'name', + type: 'varchar', + length: 255, + }) + declare name: string + @Column({ name: 'credential_id', type: 'varbinary', diff --git a/packages/auth/src/Mapping/AuthenticatorHttpMapper.ts b/packages/auth/src/Mapping/AuthenticatorHttpMapper.ts new file mode 100644 index 000000000..6e1951ee5 --- /dev/null +++ b/packages/auth/src/Mapping/AuthenticatorHttpMapper.ts @@ -0,0 +1,17 @@ +import { MapperInterface } from '@standardnotes/domain-core' + +import { Authenticator } from '../Domain/Authenticator/Authenticator' +import { AuthenticatorHttpProjection } from '../Infra/Http/Projection/AuthenticatorHttpProjection' + +export class AuthenticatorHttpMapper implements MapperInterface { + toDomain(_projection: AuthenticatorHttpProjection): Authenticator { + throw new Error('Not implemented yet.') + } + + toProjection(domain: Authenticator): AuthenticatorHttpProjection { + return { + id: domain.id.toString(), + name: domain.props.name, + } + } +} diff --git a/packages/auth/src/Mapping/AuthenticatorPersistenceMapper.ts b/packages/auth/src/Mapping/AuthenticatorPersistenceMapper.ts index fb2678f22..eab00ead4 100644 --- a/packages/auth/src/Mapping/AuthenticatorPersistenceMapper.ts +++ b/packages/auth/src/Mapping/AuthenticatorPersistenceMapper.ts @@ -20,6 +20,7 @@ export class AuthenticatorPersistenceMapper implements MapperInterface