Skip to content

Commit

Permalink
test: attestation full coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
PhearZero committed Apr 26, 2024
1 parent f72255d commit 00c65f9
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"env": "test",
"timeout": 1800000,
"rpName": "Algorand Foundation FIDO2 Server",
"hostname": "localhost",
"origin": "http://localhost",
"hostname": "catfish-pro-wolf.ngrok-free.app",
"origin": "http://catfish-pro-wolf.ngrok-free.app",
"socket": {
"host": "localhost",
"port": 6379,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
[
"com.fido.my1/10222.0000 (Android 1234; 8; Pixel)",
"com.fido.my2/10223.0000 (Android 1234; 8; Pixel)",
"com.fido.my3/10224.0000 (Android 1234; 8; Pixel)",
"com.fido.my4/10225.0000 (Android 1234; 8; Pixel)",
"com.fido.my5/10226.0000 (Android 1234; 8; Pixel)",
"com.fido.my6/10227.0000 (Android 1234; 8; Pixel)"
"foundation.algorand.demo/1.0 (Android 14; Pixel 8 Pro; google)"
]

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[
{
"username": "B7WYCZ6HRBGCH452D24TYAK7BXKNCHEXY2X7S7FWZXMHDVTDOARAOURJEU",
"displayName": "Test Wallet",
"authenticatorSelection": "AuthenticatorSelectionCriteria",
"attestationType": "AttestationConveyancePreference",
"extensions": "AttestationExtension"
"username": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E",
"displayName": "Liquid Auth User",
"authenticatorSelection": {
"userVerification": "required"
},
"extensions": {
"liquid": true
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[
{
"challenge": "Rr5tMsVFB3MLvWoNsMx8FlrPu9zSFwyOlOSu5vdXsr0",
"rp": {
"name": "Algorand Foundation FIDO2 Server",
"id": "catfish-pro-wolf.ngrok-free.app"
},
"user": {
"id": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E",
"name": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E",
"displayName": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [],
"authenticatorSelection": {
"userVerification": "required"
},
"extensions": {
"liquid": true
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"id": "AWa-UZM4mTlcmw-FwD3rJVqRjAztEHFMzT8TLlV4BcOTHrejej8om5f3EDcp5Nf8fOP07fzCC-YyaHJsIVMrWEc",
"type": "public-key",
"rawId": "AWa-UZM4mTlcmw-FwD3rJVqRjAztEHFMzT8TLlV4BcOTHrejej8om5f3EDcp5Nf8fOP07fzCC-YyaHJsIVMrWEc",
"clientExtensionResults": {
"liquid": {
"type": "algorand",
"requestId": "0.07285776111201803",
"address": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E",
"signature": "yM0bKHTntG3VtAp_1nAgsxK2F__bv5FukQAB6W-SMEkcvGPPkXbAmahudJB9M0HTBCcwymH7rjvnO2qR73F7AA",
"device": "Pixel 8 Pro"
}
},
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUnI1dE1zVkZCM01MdldvTnNNeDhGbHJQdTl6U0Z3eU9sT1N1NXZkWHNyMCIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOlI4eE83cmxRV2FXTDRCbEZ5Z3B0V1JiNXFjS1dkZmp6WklhU1JpdDlYVnciLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJmb3VuZGF0aW9uLmFsZ29yYW5kLmRlbW8ifQ",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjFlpPmT7RcYTDeFJdKhDtiKwzb05n-ojlcqqYw5SomXZBFAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQFmvlGTOJk5XJsPhcA96yVakYwM7RBxTM0_Ey5VeAXDkx63o3o_KJuX9xA3KeTX_Hzj9O38wgvmMmhybCFTK1hHpQECAyYgASFYICaqhlIBYRrCy2ahphjY3x4fKmE_shT7-VFwRghppb8EIlggOduQ2APK_5rnS2JjWi_xvV3KkNRam3RLQIIU9V2M3d0"
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"id": "QsJVONlF673mOkwOtfEICuoxj7CFW-s2DTOMlvt5saE",
"wallet": "UY5OHBV7O7RJXY7MKLDCVE4MCJ7OFSGFMCENS6BHJHCO3ERQU2CBJTVD2E",
"credentials": [
{
"device": "Pixel 8 Pro",
"publicKey": "pQECAyYgASFYICaqhlIBYRrCy2ahphjY3x4fKmE_shT7-VFwRghppb8EIlggOduQ2APK_5rnS2JjWi_xvV3KkNRam3RLQIIU9V2M3d0",
"credId": "AWa-UZM4mTlcmw-FwD3rJVqRjAztEHFMzT8TLlV4BcOTHrejej8om5f3EDcp5Nf8fOP07fzCC-YyaHJsIVMrWEc",
"prevCounter": 0
}
]
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,50 @@ import { Session } from '../auth/session.schema.js';
import mongoose, { Model } from 'mongoose';
import { User, UserSchema } from '../auth/auth.schema.js';
import { getModelToken } from '@nestjs/mongoose';
import { Request } from 'express';
import { AttestationController } from './attestation.controller.js';
import {
accFixture,
dummyUsers,
dummyAttestationOptions,
} from '../../tests/constants.js';
import { mockAuthService } from '../__mocks__/auth.service.mock.js';
import { mockAccountLinkService } from '../__mocks__/account-link.service.mock.js';
import { mockAttestationService } from '../__mocks__/attestation.service.mock.js';
import { AppService } from '../app.service.js';
import { ConfigService } from '@nestjs/config';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AttestationService } from './attestation.service.js';
import { NotFoundException } from '@nestjs/common';
import { NotImplementedException, UnauthorizedException } from '@nestjs/common';
import {
AttestationCredentialJSONDto,
AttestationSelectorDto,
} from './attestation.dto.js';

const dummyAttestationSelectorDto = {
authenticatorSelection: {},
} as AttestationSelectorDto;

const dummyAttestationCredentialJSON = {
id: '',
type: '',
rawId: 'mreh',
response: {
attestationObject: '',
clientDataJSON: '',
},
} as AttestationCredentialJSONDto;

import { AlgodService } from '../algod/algod.service.js';
import configurationFixture from '../__fixtures__/configuration.fixture.json';
import androidUserAgentFixtures from '../__fixtures__/user-agent.android.fixtures.json';
import attestationRequestResponseFixtures from './__fixtures__/attestation.request.response.fixtures.json';
import attestationRequestBodyFixtures from './__fixtures__/attestation.request.body.fixtures.json';
import attestationResponseBodyFixtures from './__fixtures__/attestation.response.body.fixtures.json';
// TODO: Response Fixtures
import attestationResponseResponseFixtures from './__fixtures__/attestation.response.response.fixtures.json';
import { join } from "node:path";

Check failure on line 26 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'join' is defined but never used

Check failure on line 26 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'join' is defined but never used
describe('AttestationController', () => {
let attestationController: AttestationController;
let userModel: Model<User>;

let authService: AuthService;
beforeEach(async () => {
userModel = mongoose.model('User', UserSchema);

const moduleRef: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [() => configurationFixture],
}),
],
controllers: [AttestationController],
providers: [
ConfigService,
AlgodService,
{
provide: AuthService,
useValue: { ...mockAuthService },
},
AppService,
{
provide: AttestationService,
useValue: { ...mockAttestationService },
},
AttestationService,
{
provide: 'ACCOUNT_LINK_SERVICE',
useValue: { ...mockAccountLinkService },
Expand All @@ -68,6 +59,7 @@ describe('AttestationController', () => {
],
}).compile();

authService = moduleRef.get<AuthService>(AuthService);
attestationController = moduleRef.get<AttestationController>(
AttestationController,
);
Expand All @@ -77,46 +69,148 @@ describe('AttestationController', () => {
expect(attestationController).toBeDefined();
});

describe('Post /request', () => {
it('(OK) should create a challenge', async () => {
const session: Record<string, any> = new Session();
session.wallet = accFixture.accs[0].addr;

const body = dummyAttestationSelectorDto;

await expect(attestationController.request(session, body)).resolves.toBe(
dummyAttestationOptions,
);
describe('POST /request', () => {
it('should create PublicKeyCredentialCreationOptions', () => {
attestationRequestBodyFixtures.forEach((fixture, i) => {
const setChallengeSpy = jest.fn();
const setLiquidExtensionSpy = jest.fn();
const response = attestationController.request(
{
set challenge(str: string) {
setChallengeSpy(str);
},
set liquidExtension(val: boolean) {
setLiquidExtensionSpy(val);
},
},
fixture as AttestationSelectorDto,
);
expect(response).toEqual({
...attestationRequestResponseFixtures[i],
challenge: response.challenge,
});
expect(setChallengeSpy).toHaveBeenCalledWith(response.challenge);
expect(setLiquidExtensionSpy).toHaveBeenCalledWith(true);
});
});
it('should fail if liquid extension is not enabled', async () => {
attestationRequestBodyFixtures.forEach((fixture, i) => {

Check failure on line 97 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'i' is defined but never used

Check failure on line 97 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'i' is defined but never used
expect(() =>
attestationController.request({}, {
...fixture,
extensions: {},
} as AttestationSelectorDto),
).toThrow(NotImplementedException);
});
});
});

describe('Post /response', () => {
it('(OK) should register a key', async () => {
const dummyUser = dummyUsers[0];

describe('POST /response', () => {
it('should register a key', async () => {
const session: Record<string, any> = new Session();
session.wallet = accFixture.accs[0].addr;
session.challenge = accFixture.challenge;

const body = dummyAttestationCredentialJSON;
const req = { get: jest.fn() } as any as Request;

authService.addCredential = jest.fn(async (username, credential)=>{

Check failure on line 111 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'username' is defined but never used

Check failure on line 111 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'credential' is defined but never used

Check failure on line 111 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'username' is defined but never used

Check failure on line 111 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'credential' is defined but never used
return attestationResponseResponseFixtures[0];
});
session.challenge = attestationRequestResponseFixtures[0].challenge;
session.liquidExtension = true;
const body =
attestationResponseBodyFixtures[0] as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };
await expect(
attestationController.attestationResponse(session, body, req),
).resolves.toBe(dummyUser);
attestationController.attestationResponse(session, headers, body),
).resolves.toBe(attestationResponseResponseFixtures[0]);
expect(session.challenge).toBeUndefined();
expect(session.liquidExtension).toBeUndefined();
});

it('(FAIL) should fail if the expectedChallenge is not a string', async () => {
it('should set a default device if empty', async () => {
const session: Record<string, any> = new Session();
session.wallet = accFixture.accs[0].addr;
session.challenge = 0;

const body = dummyAttestationCredentialJSON;
const req = { get: jest.fn() } as any as Request;

authService.addCredential = jest.fn(async (username, credential)=>{

Check failure on line 127 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'username' is defined but never used

Check failure on line 127 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'credential' is defined but never used

Check failure on line 127 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'username' is defined but never used

Check failure on line 127 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'credential' is defined but never used
return attestationResponseResponseFixtures[0];
});
session.challenge = attestationRequestResponseFixtures[0].challenge;
session.liquidExtension = true;
const body =
{...attestationResponseBodyFixtures[0], clientExtensionResults: {liquid: {...attestationResponseBodyFixtures[0].clientExtensionResults.liquid, device: null}}} as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };
await expect(
attestationController.attestationResponse(session, body, req),
).rejects.toThrow(NotFoundException);
attestationController.attestationResponse(session, headers, body),
).resolves.toBe(attestationResponseResponseFixtures[0]);
expect(session.challenge).toBeUndefined();
expect(session.liquidExtension).toBeUndefined();
});
it('should fail if the challenge is not a string', async () => {
await Promise.all(
attestationResponseBodyFixtures.map(async (fixture, i) => {
const body = fixture as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };

await expect(() =>
attestationController.attestationResponse(
{
challenge: null,

Check failure on line 150 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'i' is defined but never used

Check failure on line 150 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'i' is defined but never used
},
headers,
body,
),
).rejects.toThrow(UnauthorizedException);
}),
);
});
it(`should fail when liquid is not enabled`, async () => {
await Promise.all(
attestationResponseBodyFixtures.map(async (fixture, i) => {
const body = fixture as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };
const session = {
challenge: attestationRequestResponseFixtures[0].challenge,
};
await expect(
attestationController.attestationResponse(session, headers, body),

Check failure on line 168 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'i' is defined but never used

Check failure on line 168 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'i' is defined but never used
).rejects.toThrow(NotImplementedException);
}),
);
});
it(`should fail when liquid is enabled without client extension results`, async () => {
await Promise.all(
attestationResponseBodyFixtures.map(async (fixture, i) => {
const body = {
...fixture,
clientExtensionResults: {},
} as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };
const session = {
challenge: attestationRequestResponseFixtures[0].challenge,

Check failure on line 182 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'i' is defined but never used

Check failure on line 182 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'i' is defined but never used
liquidExtension: true,
};
await expect(
attestationController.attestationResponse(session, headers, body),
).rejects.toThrow(UnauthorizedException);
}),
);
});
it(`should fail when the extension data is invalid`, async () => {
await Promise.all(
attestationResponseBodyFixtures.map(async (fixture, i) => {
const body = {
...fixture,
clientExtensionResults: {
liquid: {
...fixture.clientExtensionResults.liquid,
signature:
'zM0bKHTntG3VtAp_1nAgsxK2F__bv5FukQAB6W-SMEkcvGPPkXbAmahudJB9M0HTBCcwymH7rjvnO2qR73F7AA',

Check failure on line 200 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'i' is defined but never used

Check failure on line 200 in services/liquid-auth-api-js/src/attestation/attestation.controller.spec.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'i' is defined but never used
},
},
} as AttestationCredentialJSONDto;
const headers = { 'user-agent': androidUserAgentFixtures[0] };
const session = {
challenge: attestationRequestResponseFixtures[0].challenge,
liquidExtension: true,
};
await expect(() =>
attestationController.attestationResponse(session, headers, body),
).rejects.toThrow(UnauthorizedException);
}),
);
});
});
});
Loading

0 comments on commit 00c65f9

Please sign in to comment.