diff options
author | Matthew Miller <matthew@millerti.me> | 2021-02-06 16:01:48 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-06 16:01:48 -0800 |
commit | abfb8a510069f4231d95bc594bd2e3b16a7bac5c (patch) | |
tree | 9789f61491ba38b7703d5fdef25ad3efb0362bee | |
parent | c09135e9b4166f8aa65a3396f0cd240704c2546b (diff) | |
parent | 6d4de49262bc9b655d8033af53b047b6caa23ec2 (diff) |
Merge pull request #94 from MasterKale/feature/browser-extensions-support
feature/browser-extensions-support
13 files changed, 198 insertions, 78 deletions
diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b4d50a8..eeafaba 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,6 +11,17 @@ "problemMatcher": [], "label": "npm: build:server", "detail": "lerna bootstrap --scope=@simplewebauthn/server" + }, + { + "type": "npm", + "script": "build:browser", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [], + "label": "npm: build:browser", + "detail": "lerna bootstrap --scope=@simplewebauthn/browser" } ] } diff --git a/packages/browser/src/methods/startAssertion.test.ts b/packages/browser/src/methods/startAssertion.test.ts index c74b88f..c077219 100644 --- a/packages/browser/src/methods/startAssertion.test.ts +++ b/packages/browser/src/methods/startAssertion.test.ts @@ -1,6 +1,8 @@ import { AssertionCredential, PublicKeyCredentialRequestOptionsJSON, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, } from '@simplewebauthn/typescript-types'; import supportsWebauthn from '../helpers/supportsWebauthn'; @@ -40,13 +42,6 @@ const goodOpts2UTF8: PublicKeyCredentialRequestOptionsJSON = { }; beforeEach(() => { - mockNavigatorGet.mockReset(); - mockSupportsWebauthn.mockReset(); -}); - -test('should convert options before passing to navigator.credentials.get(...)', async done => { - mockSupportsWebauthn.mockReturnValue(true); - // Stub out a response so the method won't throw mockNavigatorGet.mockImplementation( (): Promise<any> => { @@ -59,6 +54,15 @@ test('should convert options before passing to navigator.credentials.get(...)', }, ); + mockSupportsWebauthn.mockReturnValue(true); +}); + +afterEach(() => { + mockNavigatorGet.mockReset(); + mockSupportsWebauthn.mockReset(); +}); + +test('should convert options before passing to navigator.credentials.get(...)', async done => { await startAssertion(goodOpts1); const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; @@ -73,20 +77,6 @@ test('should convert options before passing to navigator.credentials.get(...)', }); test('should support optional allowCredential', async () => { - mockSupportsWebauthn.mockReturnValue(true); - - // Stub out a response so the method won't throw - mockNavigatorGet.mockImplementation( - (): Promise<any> => { - return new Promise(resolve => { - resolve({ - response: {}, - getClientExtensionResults: () => ({}), - }); - }); - }, - ); - await startAssertion({ challenge: bufferToBase64URLString(toUint8Array('fizz')), timeout: 1, @@ -96,20 +86,6 @@ test('should support optional allowCredential', async () => { }); test('should convert allow allowCredential to undefined when empty', async () => { - mockSupportsWebauthn.mockReturnValue(true); - - // Stub out a response so the method won't throw - mockNavigatorGet.mockImplementation( - (): Promise<any> => { - return new Promise(resolve => { - resolve({ - response: {}, - getClientExtensionResults: () => ({}), - }); - }); - }, - ); - await startAssertion({ challenge: bufferToBase64URLString(toUint8Array('fizz')), timeout: 1, @@ -119,8 +95,6 @@ test('should convert allow allowCredential to undefined when empty', async () => }); test('should return base64url-encoded response values', async done => { - mockSupportsWebauthn.mockReturnValue(true); - mockNavigatorGet.mockImplementation( (): Promise<AssertionCredential> => { return new Promise(resolve => { @@ -162,8 +136,6 @@ test("should throw error if WebAuthn isn't supported", async done => { }); test('should throw error if assertion is cancelled for some reason', async done => { - mockSupportsWebauthn.mockReturnValue(true); - mockNavigatorGet.mockImplementation( (): Promise<null> => { return new Promise(resolve => { @@ -178,20 +150,6 @@ test('should throw error if assertion is cancelled for some reason', async done }); test('should handle UTF-8 challenges', async done => { - mockSupportsWebauthn.mockReturnValue(true); - - // Stub out a response so the method won't throw - mockNavigatorGet.mockImplementation( - (): Promise<any> => { - return new Promise(resolve => { - resolve({ - response: {}, - getClientExtensionResults: () => ({}), - }); - }); - }, - ); - await startAssertion(goodOpts2UTF8); const argsPublicKey = mockNavigatorGet.mock.calls[0][0].publicKey; @@ -221,3 +179,66 @@ test('should handle UTF-8 challenges', async done => { done(); }); + +test('should send extensions to authenticator if present in options', async done => { + const extensions: AuthenticationExtensionsClientInputs = { + credProps: true, + appid: 'appidHere', + uvm: true, + appidExclude: 'appidExcludeHere', + }; + const optsWithExts: PublicKeyCredentialRequestOptionsJSON = { + ...goodOpts1, + extensions, + }; + await startAssertion(optsWithExts); + + const argsExtensions = mockNavigatorGet.mock.calls[0][0].publicKey.extensions; + + expect(argsExtensions).toEqual(extensions); + + done(); +}); + +test('should not set any extensions if not present in options', async done => { + await startAssertion(goodOpts1); + + const argsExtensions = mockNavigatorGet.mock.calls[0][0].publicKey.extensions; + + expect(argsExtensions).toEqual(undefined); + + done(); +}); + +test('should include extension results', async done => { + const extResults: AuthenticationExtensionsClientOutputs = { + appid: true, + credProps: { + rk: true, + }, + }; + + // Mock extension return values from authenticator + mockNavigatorGet.mockImplementation( + (): Promise<any> => { + return new Promise(resolve => { + resolve({ response: {}, getClientExtensionResults: () => extResults }); + }); + }, + ); + + // Extensions aren't present in this object, but it doesn't matter since we're faking the response + const response = await startAssertion(goodOpts1); + + expect(response.clientExtensionResults).toEqual(extResults); + + done(); +}); + +test('should include extension results when no extensions specified', async done => { + const response = await startAssertion(goodOpts1); + + expect(response.clientExtensionResults).toEqual({}); + + done(); +}); diff --git a/packages/browser/src/methods/startAssertion.ts b/packages/browser/src/methods/startAssertion.ts index e02e577..fa25f47 100644 --- a/packages/browser/src/methods/startAssertion.ts +++ b/packages/browser/src/methods/startAssertion.ts @@ -60,5 +60,6 @@ export default async function startAssertion( userHandle, }, type, + clientExtensionResults: credential.getClientExtensionResults(), }; } diff --git a/packages/browser/src/methods/startAttestation.test.ts b/packages/browser/src/methods/startAttestation.test.ts index 0723213..b4cfa52 100644 --- a/packages/browser/src/methods/startAttestation.test.ts +++ b/packages/browser/src/methods/startAttestation.test.ts @@ -1,5 +1,7 @@ import { AttestationCredential, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, PublicKeyCredentialCreationOptionsJSON, } from '@simplewebauthn/typescript-types'; @@ -46,22 +48,24 @@ const goodOpts1: PublicKeyCredentialCreationOptionsJSON = { }; beforeEach(() => { - mockNavigatorCreate.mockReset(); - mockSupportsWebauthn.mockReset(); -}); - -test('should convert options before passing to navigator.credentials.create(...)', async done => { - mockSupportsWebauthn.mockReturnValue(true); - // Stub out a response so the method won't throw mockNavigatorCreate.mockImplementation( (): Promise<any> => { return new Promise(resolve => { - resolve({ response: {} }); + resolve({ response: {}, getClientExtensionResults: () => ({}) }); }); }, ); + mockSupportsWebauthn.mockReturnValue(true); +}); + +afterEach(() => { + mockNavigatorCreate.mockReset(); + mockSupportsWebauthn.mockReset(); +}); + +test('should convert options before passing to navigator.credentials.create(...)', async done => { await startAttestation(goodOpts1); const argsPublicKey = mockNavigatorCreate.mock.calls[0][0].publicKey; @@ -81,8 +85,6 @@ test('should convert options before passing to navigator.credentials.create(...) }); test('should return base64url-encoded response values', async done => { - mockSupportsWebauthn.mockReturnValue(true); - mockNavigatorCreate.mockImplementation( (): Promise<AttestationCredential> => { return new Promise(resolve => { @@ -120,8 +122,6 @@ test("should throw error if WebAuthn isn't supported", async done => { }); test('should throw error if attestation is cancelled for some reason', async done => { - mockSupportsWebauthn.mockReturnValue(true); - mockNavigatorCreate.mockImplementation( (): Promise<null> => { return new Promise(resolve => { @@ -134,3 +134,66 @@ test('should throw error if attestation is cancelled for some reason', async don done(); }); + +test('should send extensions to authenticator if present in options', async done => { + const extensions: AuthenticationExtensionsClientInputs = { + credProps: true, + appid: 'appidHere', + uvm: true, + appidExclude: 'appidExcludeHere', + }; + const optsWithExts: PublicKeyCredentialCreationOptionsJSON = { + ...goodOpts1, + extensions, + }; + await startAttestation(optsWithExts); + + const argsExtensions = mockNavigatorCreate.mock.calls[0][0].publicKey.extensions; + + expect(argsExtensions).toEqual(extensions); + + done(); +}); + +test('should not set any extensions if not present in options', async done => { + await startAttestation(goodOpts1); + + const argsExtensions = mockNavigatorCreate.mock.calls[0][0].publicKey.extensions; + + expect(argsExtensions).toEqual(undefined); + + done(); +}); + +test('should include extension results', async done => { + const extResults: AuthenticationExtensionsClientOutputs = { + appid: true, + credProps: { + rk: true, + }, + }; + + // Mock extension return values from authenticator + mockNavigatorCreate.mockImplementation( + (): Promise<any> => { + return new Promise(resolve => { + resolve({ response: {}, getClientExtensionResults: () => extResults }); + }); + }, + ); + + // Extensions aren't present in this object, but it doesn't matter since we're faking the response + const response = await startAttestation(goodOpts1); + + expect(response.clientExtensionResults).toEqual(extResults); + + done(); +}); + +test('should include extension results when no extensions specified', async done => { + const response = await startAttestation(goodOpts1); + + expect(response.clientExtensionResults).toEqual({}); + + done(); +}); diff --git a/packages/browser/src/methods/startAttestation.ts b/packages/browser/src/methods/startAttestation.ts index 0b2235d..22ebe64 100644 --- a/packages/browser/src/methods/startAttestation.ts +++ b/packages/browser/src/methods/startAttestation.ts @@ -51,6 +51,7 @@ export default async function startAttestation( clientDataJSON: bufferToBase64URLString(response.clientDataJSON), }, type, + clientExtensionResults: credential.getClientExtensionResults(), }; /** diff --git a/packages/browser/webpack.config.js b/packages/browser/webpack.config.js index 75bb5fe..1c060c3 100644 --- a/packages/browser/webpack.config.js +++ b/packages/browser/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path'); const WebpackAutoInject = require('webpack-auto-inject-version'); +const packageJSON = require('./package.json'); const outputPath = path.resolve(__dirname, 'dist'); module.exports = { @@ -28,7 +29,7 @@ module.exports = { }, plugins: [ new WebpackAutoInject({ - SHORT: '@webauthentine/browser', + SHORT: packageJSON.name, PACKAGE_JSON_INDENT: 2, components: { AutoIncreaseVersion: false, @@ -36,6 +37,7 @@ module.exports = { componentsOptions: { InjectAsComment: { tag: 'Version: {version} - {date}', + multiLineCommentType: true, }, }, }), diff --git a/packages/server/src/assertion/verifyAssertionResponse.test.ts b/packages/server/src/assertion/verifyAssertionResponse.test.ts index 1708f77..705f3cb 100644 --- a/packages/server/src/assertion/verifyAssertionResponse.test.ts +++ b/packages/server/src/assertion/verifyAssertionResponse.test.ts @@ -4,7 +4,7 @@ import verifyAssertionResponse from './verifyAssertionResponse'; import * as decodeClientDataJSON from '../helpers/decodeClientDataJSON'; import * as parseAuthenticatorData from '../helpers/parseAuthenticatorData'; import toHash from '../helpers/toHash'; -import { AuthenticatorDevice } from '@simplewebauthn/typescript-types'; +import { AuthenticatorDevice, AssertionCredentialJSON } from '@simplewebauthn/typescript-types'; let mockDecodeClientData: jest.SpyInstance; let mockParseAuthData: jest.SpyInstance; @@ -194,6 +194,7 @@ test.skip('should verify TPM assertion', () => { userHandle: 'aW50ZXJuYWxVc2VySWQ', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: assertionOrigin, @@ -260,7 +261,7 @@ test('should throw an error if RP ID not in list of possible RP IDs', async () = * Assertion examples below */ -const assertionResponse = { +const assertionResponse: AssertionCredentialJSON = { id: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew', rawId: 'KEbWNCc7NgaYnUyrNeFGX9_3Y-8oJ3KwzjnaiD1d1LVTxR7v3CaKfCz2Vy_g_MHSh7yJ8yL0Pxg6jo_o0hYiew', response: { @@ -273,7 +274,7 @@ const assertionResponse = { 'MEUCIQDYXBOpCWSWq2Ll4558GJKD2RoWg958lvJSB_GdeokxogIgWuEVQ7ee6AswQY0OsuQ6y8Ks6' + 'jhd45bDx92wjXKs900=', }, - getClientExtensionResults: () => ({}), + clientExtensionResults: {}, type: 'public-key', }; const assertionChallenge = base64url.encode('totallyUniqueValueEveryTime'); @@ -292,7 +293,7 @@ const authenticator: AuthenticatorDevice = { /** * Represented a device that's being used on the website for the first time */ -const assertionFirstTimeUsedResponse = { +const assertionFirstTimeUsedResponse: AssertionCredentialJSON = { id: 'wSisR0_4hlzw3Y1tj4uNwwifIhRa-ZxWJwWbnfror0pVK9qPdBPO5pW3gasPqn6wXHb0LNhXB_IrA1nFoSQJ9A', rawId: 'wSisR0_4hlzw3Y1tj4uNwwifIhRa-ZxWJwWbnfror0pVK9qPdBPO5pW3gasPqn6wXHb0LNhXB_IrA1nFoSQJ9A', response: { @@ -303,6 +304,7 @@ const assertionFirstTimeUsedResponse = { 'MEQCIBu6M-DGzu1O8iocGHEj0UaAZm0HmxTeRIE6-nS3_CPjAiBDsmIzy5sacYwwzgpXqfwRt_2vl5yiQZ_OAqWJQBGVsQ', }, type: 'public-key', + clientExtensionResults: {}, }; const assertionFirstTimeUsedChallenge = base64url.encode('totallyUniqueValueEveryAssertion'); const assertionFirstTimeUsedOrigin = 'https://dev.dontneeda.pw'; diff --git a/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts b/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts index 0c4f860..81f1fbe 100644 --- a/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts +++ b/packages/server/src/attestation/verifications/tpm/verifyTPM.test.ts @@ -15,6 +15,7 @@ test('should verify TPM response', async () => { 'eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiJhNGRlMGQzNi0wNTdkLTRlOWQtODMxYS0yYzU3OGZhODkxNzAiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -44,6 +45,7 @@ test('should verify SHA1 TPM response', async () => { 'o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzn__mNzaWdZAQBIwu9LPAl-LgxlRzPlvn7L-0yuMnFFn1XALxXtGnmC5-oMIIqfUJWFbgBbkN2l2zPsqOCRT5GQU8ucKNI6HrlbuDAUIq7wjcxG5TzgQt3YtGMWtgEcrZn2ecUlQFKjY67_wZIuHLy443Ki1SjErNPrMrkIPe9lyFhIalMgrWLCol40gYIVr_9xLfgyX55c7XiB-XbUKhDLUv5uPA3CSAiWeWwWx26K2BTV85vHsaG6f2YFTfcQTFs1cTSwMm7A9C2SiQ7N01ENwM1urVxlCvuEsBgiXapR70Oyq_cfiENYY0ti7_w2fvikmfv0z0O1cJOAyUlYWjnWhT707chrVmkFY3ZlcmMyLjBjeDVjglkEXzCCBFswggNDoAMCAQICDwRsOt2imXnV5Z4BftcqfzANBgkqhkiG9w0BAQsFADBBMT8wPQYDVQQDEzZOQ1UtTlRDLUtFWUlELTM2MTA0Q0U0MEJCQ0MxRjQwRDg0QTRCQkQ1MEJFOTkwMjREOTU3RDQwHhcNMTgwMjAxMDAwMDAwWhcNMjUwMTMxMjM1OTU5WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmw-4ficURR_sgVfW7cs1iRoDGdxjBpCczF233ba_5WTP-RrsYZPlzWgSN9WXptuywzjZoDlbid7NlduSR1ZFsds4bW71LyKDL62eyqaiAc645gocXAyxdDIDJAeo-3N9Dm4vsw-Gy_0sd2v1UEkBhWjuE1gL5hcaB9EtXSDvHPwmrf0eYn_4cWu9AxqSxpn79JIPYEOUrURr2H8zyG4_P0j1a3MVBmtAymhpXBn9ila-bW7K_k0JYXBh5yAYZDsmHgFsXbUauDWdja3HYzkep9jXkFcegXOMjPr_QSqWRjawEvzoprnJ-QqoWNbaRhuD-UnfgCNbwseU8kZ0aQNjBQIDAQABo4IBjzCCAYswDgYDVR0PAQH_BAQDAgeAMAwGA1UdEwEB_wQCMAAwUwYDVR0gAQH_BEkwRzBFBgkrBgEEAYI3FR8wODA2BggrBgEFBQcCAjAqEyhGQUtFIEZJRE8gVENQQSBUcnVzdGVkIFBsYXRmb3JtIElkZW50aXR5MBAGA1UdJQQJMAcGBWeBBQgDMEoGA1UdEQEB_wRAMD6kPDA6MTgwDgYFZ4EFAgMMBWlkOjEzMBAGBWeBBQICDAdOUENUNnh4MBQGBWeBBQIBDAtpZDpGRkZGRjFEMDAfBgNVHSMEGDAWoBRRfyLI5lOlfNVM3TBYfjD_ZzaMXTAdBgNVHQ4EFgQUO6SUmiOhCHVZcq-88acg2uQkQz8weAYIKwYBBQUHAQEEbDBqMGgGCCsGAQUFBzAChlxodHRwczovL2ZpZG9hbGxpYW5jZS5jby5uei90cG1wa2kvTkNVLU5UQy1LRVlJRC0zNjEwNENFNDBCQkNDMUY0MEQ4NEE0QkJENTBCRTk5MDI0RDk1N0Q0LmNydDANBgkqhkiG9w0BAQsFAAOCAQEAIIyVBkck_SD2nbj4KOwUI6cYZHrjwrcULoEiOSXn9TjTIiB5MdBMvqqNyAXiyWoWd1GEc_MI3mKOzu4g5UTVQQqfiOTrqfuZrpoU0tAeojKnZLj2wYj5GpyOfEkPK3m9qVaDxiYrh6aS8a3w_Iog878EiIaoVALbBt5uAfh0TAHHwSdxHtU8DRJrC43yIqcP9byRqssJmgSNcpMAjw_hcKJxDMD2UurvsMasqyWvK533yNA0-VwXvk3HI0ItSOw_g352D-qOTHI82lJIjc3yKoaNeYKn7RzgcLAF7AesTiiJReY2kU_vLyf-wH54-08T3oyBBJpBCHc1y_Lt5d2qWFkGCDCCBgQwggPsoAMCAQICENBTpEeEh5lpTgeR7VT9oQcwDQYJKoZIhvcNAQELBQAwgb8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxNjA0BgNVBAMMLUZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxODExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzAeFw0xNzAyMDEwMDAwMDBaFw0zNTAxMzEyMzU5NTlaMEExPzA9BgNVBAMTNk5DVS1OVEMtS0VZSUQtMzYxMDRDRTQwQkJDQzFGNDBEODRBNEJCRDUwQkU5OTAyNEQ5NTdENDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc-c30RpQd-_LCoiLJbXz3t_vqciOIovwjez79_DtVgi8G9Ph-tPL-lC0ueFGBMSPcKd_RDdSFe2QCYQd9e0DtiFxra-uWGa0olI1hHI7bK2GzNAZSTKEbwgqpf8vXMQ-7SPajg6PfxSOLH_Nj2yd6tkNkUSdlGtWfY8XGB3n-q--nt3UHdUQWEtgUoTe5abBXsG7MQSuTNoad3v6vk-tLd0W44ivM6pbFqFUHchx8mGLApCpjlVXrfROaCoc9E91hG9B-WNvekJ0dM6kJ658Hy7yscQ6JdqIEolYojCtWaWNmwcfv--OE1Ax_4Ub24gl3hpB9EOcBCzpb4UFmLYUECAwEAAaOCAXcwggFzMAsGA1UdDwQEAwIBhjAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzAbBgNVHSUEFDASBgkrBgEEAYI3FSQGBWeBBQgDMBIGA1UdEwEB_wQIMAYBAf8CAQAwHwYDVR0OBBgEFsIUUX8iyOZTpXzVTN0wWH4w_2c2jF0wHwYDVR0jBBgwFqAUXH82LZCtWry6jnXa3jqg7cFOAoswaAYDVR0fBGEwXzBdoFugWYZXaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL2NybC9GSURPIEZha2UgVFBNIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTguY3JsMG8GCCsGAQUFBwEBBGMwYTBfBggrBgEFBQcwAoZTaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL0ZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAG138t55DF9nPJbvbPQZOypmyTPpNne0A5fh69P1fHZ5qdE2PDz3cf5Tl-8OPI4xQniEFNPcXMb7KlhMM6zCl4GkZtNN4MxygdFjQ1gTZOBDpt7Dwziij0MakmwyC0RYTNtbSyVhHUevgw9rnu13EzqxPyL5JD-UqADh2Y51MS0qy7IOgegLQv-eJzSNUgHxFJreUzz4PU6yzSsTyyYDW-H4ZjAQKienVp8ewZf8oHGWHGQFGa5E9m1P8vxCMZ7pIzeQweCVYrs3q7unu4nzBAIXLPI092kYFUgyz3lIaSB3XEiPBokpupX6Zmgrfphb-XX3tbenH5hkxfumueA5RMHTMu5TVjhJXiV0yM3q5W5xrQHdJlF5nOdJDEE-Kb7nm6xaT1DDpafqBc5vEDMkJmBA4AXHUY7JPGqEEzEenT7k6Wn5IQLZg4qc8Irnj__yM7xUhJWJam47KVbLA4WFu-IKvJrkP5GSglZ9qASOCxBHaOL2UcTAg50uvhUSwur2KSak2vlENdmAijwdAL4LLQWrkFd-9NBwcNwTdfK4ekEHP1l4BwJtkNwW6etUgeA5rkW2JLocXoBq5v7GSk4_CBoKhyiahQGQQ9SZFGeBJhzzkK9yN-yKskcVjjjInSHPl-ZpeOK3sI08sEyTH0gxlTtRoX0MKDsMAHEVToe5o1u9Z3B1YkFyZWFZATYAAQALAAYEcgAgnf_L82w4OuaZ-5ho3G3LidcVOIS-KAOSLBJBWL-tIq4AEAAQCAAAAAAAAQCl9siJwqoHJ2pCwEKyLQ_u6zGcZDKZtA0jtvtn1aPlIe7wFAvQNgjI6KDiQsDPTCVeJj_RA441VbV0Z4oX2b68quDY0Gf4VpF4KWfNPdKH6H4E882m8OnBb10mhaNbPxTmDVDZLQZjh3ubX1Z56FNg6cQmz4bEnHF-7X1l7AcNORhzdzgM7uRXhwo9UsAzpu4Io1OCTsb5DaDnng3f3Y9qDn8OG3MI_5IYtm1qGgmY72nSEiIhhPCk2lvmajN6A4tWgUstc7QtdlKEPBd-ITtGdKYTSwqihaHzBQd8D-d_HDqgcOWECLKo51_YqyaEiuGlv6sPon1LMsEL6PlVw47PaGNlcnRJbmZvWKH_VENHgBcAIgALEeaO1E21Ny4UKW4vhKzHg5h1GIGSHjD8IqBvi3PHlFMAFF6MXAvgUX_Rbc04fmdB2TyLG-mdAAAAAUdwF0hVaXtLxoVgpQFzfvmNNFZV-wAiAAuYlrm-5Jg3251TsEdZ8NV11xd4X5O3q0AFLmammw658QAiAAtuzX-04mcxAHq9kO70Ew3vJCOmCS0UvQzZB2CNCeGXpWhhdXRoRGF0YVkBZ0mWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAHXyRLZ-U2RP1Z-Qw5YicxfbACBQkOhQmgaINAX8QRncb_P0t-rXr8oVpe0xOPBNSutGV6QBAwM5__4gWQEApfbIicKqBydqQsBCsi0P7usxnGQymbQNI7b7Z9Wj5SHu8BQL0DYIyOig4kLAz0wlXiY_0QOONVW1dGeKF9m-vKrg2NBn-FaReClnzT3Sh-h-BPPNpvDpwW9dJoWjWz8U5g1Q2S0GY4d7m19WeehTYOnEJs-GxJxxfu19ZewHDTkYc3c4DO7kV4cKPVLAM6buCKNTgk7G-Q2g554N392Pag5_DhtzCP-SGLZtahoJmO9p0hIiIYTwpNpb5mozegOLVoFLLXO0LXZShDwXfiE7RnSmE0sKooWh8wUHfA_nfxw6oHDlhAiyqOdf2KsmhIrhpb-rD6J9SzLBC-j5VcOOzyFDAQAB', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://localhost:44329', @@ -73,6 +75,7 @@ test('should verify SHA256 TPM response', async () => { 'o2NmbXRjdHBtZ2F0dFN0bXSmY2FsZzkBAGNzaWdZAQA6Gh1Oa3-8vCY8bTrpUHA4zp4UCsbuh36tH09G-qWlvQdoqEQsJJQu1Rz61_mFes9CXE2cxiJV8pEwxtUUTSZQWnamVU1x9bBk07qcHqAuamP_NDAahHhZ9D46q9JklT3aVdhbaZVh0y5b8NZB2eUfKqcUmM0JCxLP9ZfSe7XcVguhQVEduM6Qnl9R1zRh7cquOa8UOEpdXkt1-drsOtrA9c0UJPYzkI8qscCDc-xfzo2xv12tLXjRq395JnynHhjzJIz8Ch2IYQUiMSM6TQDcnvzDEvRgril9NC0aIkHd79omIZNnBjEDfjyqOZbBffjGyvt1Eikz4M0EE8e7N4uRY3ZlcmMyLjBjeDVjglkEXzCCBFswggNDoAMCAQICDwQ_ozlil_l5hh6NlMsLzzANBgkqhkiG9w0BAQsFADBBMT8wPQYDVQQDEzZOQ1UtTlRDLUtFWUlELTM2MTA0Q0U0MEJCQ0MxRjQwRDg0QTRCQkQ1MEJFOTkwMjREOTU3RDQwHhcNMTgwMjAxMDAwMDAwWhcNMjUwMTMxMjM1OTU5WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAor_6-4WYizZdOQ9Ia_offaIdL2BVGtGDq8jQxo16ymBSOWCP15gZt9QAkqowS3ayqEh48Pg5SdA7F5kcjD_FqKaZDBOqkjvJivdo7FKv7EaUI2al9B7h0pXIRb97jn2z0zPlXz6RV_RmBe3CCljyxrhav7bTkCXEJUnkNgxsWgLGBIW6VSVct0z42xBB6_6mYekWIej5vXLqB8AuzsqnLbU5jOohfJiI5urFso12j6YCWZ_kXK4j8e4IoHUOjWgtHXdb3kP8PvI948hcJpIEpuuLDZDDOCOPI1wAlryGwz_tJLarODZzD1XhG3BMlXi1TG7x1s-AriC3A7B89wuSpwIDAQABo4IBjzCCAYswDgYDVR0PAQH_BAQDAgeAMAwGA1UdEwEB_wQCMAAwUwYDVR0gAQH_BEkwRzBFBgkrBgEEAYI3FR8wODA2BggrBgEFBQcCAjAqEyhGQUtFIEZJRE8gVENQQSBUcnVzdGVkIFBsYXRmb3JtIElkZW50aXR5MBAGA1UdJQQJMAcGBWeBBQgDMEoGA1UdEQEB_wRAMD6kPDA6MTgwDgYFZ4EFAgMMBWlkOjEzMBAGBWeBBQICDAdOUENUNnh4MBQGBWeBBQIBDAtpZDpGRkZGRjFEMDAfBgNVHSMEGDAWoBRRfyLI5lOlfNVM3TBYfjD_ZzaMXTAdBgNVHQ4EFgQUS1ZtGu6ZoewTH3mq04Ytxa4kOQcweAYIKwYBBQUHAQEEbDBqMGgGCCsGAQUFBzAChlxodHRwczovL2ZpZG9hbGxpYW5jZS5jby5uei90cG1wa2kvTkNVLU5UQy1LRVlJRC0zNjEwNENFNDBCQkNDMUY0MEQ4NEE0QkJENTBCRTk5MDI0RDk1N0Q0LmNydDANBgkqhkiG9w0BAQsFAAOCAQEAbp-Xp9W0vyY08YUHxerc6FnFdXZ6KFuQTZ4hze60BWexCSQOee25gqOoQaQr9ufS3ImLAoV4Ifc3vKVBQvBRwMjG3pJINoWr0p2McI0F2SNclH4M0sXFYHRlmHQ2phZB6Ddd-XL8PsGyiXRI6gVacVw5ZiVEBsRrekLH-Zy25EeqS3SxaBVnEd-HZ6BGGgbflgFtyGP9fQ5YSORC-Btno_uJbmRiZm4iHiEULp9wWEWOJIOXv9tVQKsYpPg58L1_Dgc8oml1YG5a8qK3jaR77tcUgZyYy5GOk1zIsXv36f0SkmLcNTiTjrhdGVcKs2KpW5fQgm_llQ5cvhR1jlY6dFkGCDCCBgQwggPsoAMCAQICENBTpEeEh5lpTgeR7VT9oQcwDQYJKoZIhvcNAQELBQAwgb8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJNWTESMBAGA1UEBwwJV2FrZWZpZWxkMRYwFAYDVQQKDA1GSURPIEFsbGlhbmNlMQwwCgYDVQQLDANDV0cxNjA0BgNVBAMMLUZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxODExMC8GCSqGSIb3DQEJARYiY29uZm9ybWFuY2UtdG9vbHNAZmlkb2FsbGlhbmNlLm9yZzAeFw0xNzAyMDEwMDAwMDBaFw0zNTAxMzEyMzU5NTlaMEExPzA9BgNVBAMTNk5DVS1OVEMtS0VZSUQtMzYxMDRDRTQwQkJDQzFGNDBEODRBNEJCRDUwQkU5OTAyNEQ5NTdENDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANc-c30RpQd-_LCoiLJbXz3t_vqciOIovwjez79_DtVgi8G9Ph-tPL-lC0ueFGBMSPcKd_RDdSFe2QCYQd9e0DtiFxra-uWGa0olI1hHI7bK2GzNAZSTKEbwgqpf8vXMQ-7SPajg6PfxSOLH_Nj2yd6tkNkUSdlGtWfY8XGB3n-q--nt3UHdUQWEtgUoTe5abBXsG7MQSuTNoad3v6vk-tLd0W44ivM6pbFqFUHchx8mGLApCpjlVXrfROaCoc9E91hG9B-WNvekJ0dM6kJ658Hy7yscQ6JdqIEolYojCtWaWNmwcfv--OE1Ax_4Ub24gl3hpB9EOcBCzpb4UFmLYUECAwEAAaOCAXcwggFzMAsGA1UdDwQEAwIBhjAWBgNVHSAEDzANMAsGCSsGAQQBgjcVHzAbBgNVHSUEFDASBgkrBgEEAYI3FSQGBWeBBQgDMBIGA1UdEwEB_wQIMAYBAf8CAQAwHwYDVR0OBBgEFsIUUX8iyOZTpXzVTN0wWH4w_2c2jF0wHwYDVR0jBBgwFqAUXH82LZCtWry6jnXa3jqg7cFOAoswaAYDVR0fBGEwXzBdoFugWYZXaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL2NybC9GSURPIEZha2UgVFBNIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTguY3JsMG8GCCsGAQUFBwEBBGMwYTBfBggrBgEFBQcwAoZTaHR0cHM6Ly9maWRvYWxsaWFuY2UuY28ubnovdHBtcGtpL0ZJRE8gRmFrZSBUUE0gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxOC5jcnQwDQYJKoZIhvcNAQELBQADggIBAG138t55DF9nPJbvbPQZOypmyTPpNne0A5fh69P1fHZ5qdE2PDz3cf5Tl-8OPI4xQniEFNPcXMb7KlhMM6zCl4GkZtNN4MxygdFjQ1gTZOBDpt7Dwziij0MakmwyC0RYTNtbSyVhHUevgw9rnu13EzqxPyL5JD-UqADh2Y51MS0qy7IOgegLQv-eJzSNUgHxFJreUzz4PU6yzSsTyyYDW-H4ZjAQKienVp8ewZf8oHGWHGQFGa5E9m1P8vxCMZ7pIzeQweCVYrs3q7unu4nzBAIXLPI092kYFUgyz3lIaSB3XEiPBokpupX6Zmgrfphb-XX3tbenH5hkxfumueA5RMHTMu5TVjhJXiV0yM3q5W5xrQHdJlF5nOdJDEE-Kb7nm6xaT1DDpafqBc5vEDMkJmBA4AXHUY7JPGqEEzEenT7k6Wn5IQLZg4qc8Irnj__yM7xUhJWJam47KVbLA4WFu-IKvJrkP5GSglZ9qASOCxBHaOL2UcTAg50uvhUSwur2KSak2vlENdmAijwdAL4LLQWrkFd-9NBwcNwTdfK4ekEHP1l4BwJtkNwW6etUgeA5rkW2JLocXoBq5v7GSk4_CBoKhyiahQGQQ9SZFGeBJhzzkK9yN-yKskcVjjjInSHPl-ZpeOK3sI08sEyTH0gxlTtRoX0MKDsMAHEVToe5o1u9Z3B1YkFyZWFZATYAAQALAAYEcgAgnf_L82w4OuaZ-5ho3G3LidcVOIS-KAOSLBJBWL-tIq4AEAAQCAAAAAAAAQDPtSggWlsjcFiQO61-hUF8i-3FPcyvuARcy3p1seZ-_B4ClhNh5U-T0v0flMU5p6nsNDWj4f6-soe-2vVJMTm2d26uKYD2zwdrkrYYXRu5IFqUXqF-kY99v8RcrAF7DQKDo-E4XhiMz6uECvnjEloGfTYZrVuQ1mdjQ8Qki7U-9SQHMW_IsaI8ZKHtupXNhM5YPQyFbDHHXSE_iyPGh2mY4SR466ouesIuG0NccCUk5UDIvS__OUmNaX7aBrKTlnkMFjkCA1ZDFC99ZQoLFCJQHqnOU7m8zSvTJpUyG2feWgAL2Gl05V3I_lb_v5yELXcihFoA33QIOSpDmKqKV3SXaGNlcnRJbmZvWK3_VENHgBcAIgALEeaO1E21Ny4UKW4vhKzHg5h1GIGSHjD8IqBvi3PHlFMAIBo8rAwJFDGsmQjauX_FCBQenvBa2ApBcR_gOx2qW2QAAAAAAUdwF0hVaXtLxoVgpQFzfvmNNFZV-wAiAAsXPoJSq0uhvU6VLf0uIelHBNFHEanasKAoTp-lQ2dRGAAiAAuO1HPzTRRabZhwPvHQh0b1MnLIG8EVGNfpshASWSfjQWhhdXRoRGF0YVkBZ0mWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAEOn1tk6ig0R6JqUps9xBy9zACCH1cyGRV483U-ur0qz9V_AixVm-36OZJFMSd69Nz4oH6QBAwM5AQAgWQEAz7UoIFpbI3BYkDutfoVBfIvtxT3Mr7gEXMt6dbHmfvweApYTYeVPk9L9H5TFOaep7DQ1o-H-vrKHvtr1STE5tndurimA9s8Ha5K2GF0buSBalF6hfpGPfb_EXKwBew0Cg6PhOF4YjM-rhAr54xJaBn02Ga1bkNZnY0PEJIu1PvUkBzFvyLGiPGSh7bqVzYTOWD0MhWwxx10hP4sjxodpmOEkeOuqLnrCLhtDXHAlJOVAyL0v_zlJjWl-2gayk5Z5DBY5AgNWQxQvfWUKCxQiUB6pzlO5vM0r0yaVMhtn3loAC9hpdOVdyP5W_7-chC13IoRaAN90CDkqQ5iqild0lyFDAQAB', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://localhost:44329', @@ -109,6 +112,7 @@ test('should verify TPM response with spec-compliant tcgAtTpm SAN structure', as 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiVmZtWlhLRHhxZG9YRk1IWE8zU0UyUTJiOHU1S2k2NE9MX1hJQ0VMY0dLZyIsIm9yaWdpbiI6Imh0dHBzOi8vZGV2Lm5ldHBhc3Nwb3J0LmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.netpassport.io', @@ -141,6 +145,7 @@ test('should verify TPM response with non-spec-compliant tcgAtTpm SAN structure' 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiNFNUV2dtWHJnSnh6aWdxZTZuRnVJZyIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjQ0MzI5IiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://localhost:44329', diff --git a/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts b/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts index 4e41000..a2dfc59 100644 --- a/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts +++ b/packages/server/src/attestation/verifications/verifyAndroidKey.test.ts @@ -15,6 +15,7 @@ test('should verify Android KeyStore response', async () => { 'eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiI0YWI3ZGZkMS1hNjk1LTQ3NzctOTg1Zi1hZDI5OTM4MjhlOTkiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', diff --git a/packages/server/src/attestation/verifications/verifyApple.test.ts b/packages/server/src/attestation/verifications/verifyApple.test.ts index 8490520..79ac6c6 100644 --- a/packages/server/src/attestation/verifications/verifyApple.test.ts +++ b/packages/server/src/attestation/verifications/verifyApple.test.ts @@ -15,6 +15,7 @@ test('should verify Apple attestation', async () => { 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiaDV4U3lJUk14MklRUHIxbVFrNkdEOThYU1FPQkhnTUhWcEpJa01WOU5rYyIsIm9yaWdpbiI6Imh0dHBzOi8vZGV2LmRvbnRuZWVkYS5wdyJ9', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', diff --git a/packages/server/src/attestation/verifyAttestationResponse.test.ts b/packages/server/src/attestation/verifyAttestationResponse.test.ts index dc75538..96e00db 100644 --- a/packages/server/src/attestation/verifyAttestationResponse.test.ts +++ b/packages/server/src/attestation/verifyAttestationResponse.test.ts @@ -10,6 +10,7 @@ import * as decodeCredentialPublicKey from '../helpers/decodeCredentialPublicKey import * as verifyFIDOU2F from './verifications/verifyFIDOU2F'; import toHash from '../helpers/toHash'; +import { AttestationCredentialJSON } from '@simplewebauthn/typescript-types'; let mockDecodeAttestation: jest.SpyInstance; let mockDecodeClientData: jest.SpyInstance; @@ -145,6 +146,7 @@ test('should verify None attestation w/RSA public key', async () => { 'eyJjaGFsbGVuZ2UiOiJwWVozVlgyeWI4ZFM5eXBsTnhKQ2hpWGhQR0JrOGdaelRBeUoyaVU1eDFrIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -389,6 +391,7 @@ test('should validate TPM RSA response (SHA256)', async () => { 'eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiIzYTA3Y2Y4NS1lN2I2LTQ0N2YtODI3MC1iMjU0MzNmNjAxOGUiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge: expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -421,6 +424,7 @@ test('should validate TPM RSA response (SHA1)', async () => { 'eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiJmNGU4ZDg3Yi1kMzYzLTQ3Y2MtYWI0ZC0xYTg0NjQ3YmYyNDUiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -453,6 +457,7 @@ test('should validate Android-Key response', async () => { 'eyJvcmlnaW4iOiJodHRwczovL2Rldi5kb250bmVlZGEucHciLCJjaGFsbGVuZ2UiOiIxNGUwZDFiNi05YzM2LTQ4NDktYWVlYy1lYTY0Njc2NDQ5ZWYiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0', }, type: 'public-key', + clientExtensionResults: {}, }, expectedChallenge, expectedOrigin: 'https://dev.dontneeda.pw', @@ -520,7 +525,7 @@ test('should throw an error if RP ID not in list of possible RP IDs', async () = * Various Attestations Below */ -const attestationFIDOU2F = { +const attestationFIDOU2F: AttestationCredentialJSON = { id: 'VHzbxaYaJu2P8m1Y2iHn2gRNHrgK0iYbn9E978L3Qi7Q-chFeicIHwYCRophz5lth2nCgEVKcgWirxlgidgbUQ', rawId: 'VHzbxaYaJu2P8m1Y2iHn2gRNHrgK0iYbn9E978L3Qi7Q-chFeicIHwYCRophz5lth2nCgEVKcgWirxlgidgbUQ', response: { @@ -529,12 +534,12 @@ const attestationFIDOU2F = { clientDataJSON: 'eyJjaGFsbGVuZ2UiOiJkRzkwWVd4c2VWVnVhWEYxWlZaaGJIVmxSWFpsY25sQmRIUmxjM1JoZEdsdmJnIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9', }, - getClientExtensionResults: () => ({}), + clientExtensionResults: {}, type: 'public-key', }; const attestationFIDOU2FChallenge = base64url.encode('totallyUniqueValueEveryAttestation'); -const attestationPacked = { +const attestationPacked: AttestationCredentialJSON = { id: 'bbb', rawId: 'bbb', response: { @@ -550,12 +555,12 @@ const attestationPacked = { 'a3M1U0UwIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0' + 'ZSJ9', }, - getClientExtensionResults: () => ({}), + clientExtensionResults: {}, type: 'public-key', }; const attestationPackedChallenge = base64url.encode('s6PIbBnPPnrGNSBxNdtDrT7UrVYJK9HM'); -const attestationPackedX5C = { +const attestationPackedX5C: AttestationCredentialJSON = { // TODO: Grab these from another iPhone attestation id: 'aaa', rawId: 'aaa', @@ -581,12 +586,12 @@ const attestationPackedX5C = { 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZEc5MFlXeHNlVlZ1YVhG' + 'MVpWWmhiSFZsUlhabGNubFVhVzFsIiwib3JpZ2luIjoiaHR0cHM6Ly9kZXYuZG9udG5lZWRhLnB3In0=', }, - getClientExtensionResults: () => ({}), + clientExtensionResults: {}, type: 'public-key', }; const attestationPackedX5CChallenge = base64url.encode('totallyUniqueValueEveryTime'); -const attestationNone = { +const attestationNone: AttestationCredentialJSON = { id: 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY', rawId: 'AdKXJEch1aV5Wo7bj7qLHskVY4OoNaj9qu8TPdJ7kSAgUeRxWNngXlcNIGt4gexZGKVGcqZpqqWordXb_he1izY', response: { @@ -600,7 +605,7 @@ const attestationNone = { 'VURBd1NEQndOV2Q0YURKZmRUVmZVRU0wVG1WWloyUSIsIm9yaWdpbiI6Imh0dHBzOlwvXC9kZXYuZG9udG5lZWRh' + 'LnB3IiwiYW5kcm9pZFBhY2thZ2VOYW1lIjoib3JnLm1vemlsbGEuZmlyZWZveCJ9', }, - getClientExtensionResults: () => ({}), + clientExtensionResults: {}, type: 'public-key', }; const attestationNoneChallenge = base64url.encode('hEccPWuziP00H0p5gxh2_u5_PC4NeYgd'); diff --git a/packages/typescript-types/extract-dom-types.ts b/packages/typescript-types/extract-dom-types.ts index bc16554..856cb63 100644 --- a/packages/typescript-types/extract-dom-types.ts +++ b/packages/typescript-types/extract-dom-types.ts @@ -22,6 +22,7 @@ const types = [ 'AuthenticatorAttestationResponse', 'AuthenticatorTransport', 'AuthenticationExtensionsClientInputs', + 'AuthenticationExtensionsClientOutputs', 'AuthenticatorSelectionCriteria', 'COSEAlgorithmIdentifier', 'PublicKeyCredential', diff --git a/packages/typescript-types/src/index.ts b/packages/typescript-types/src/index.ts index 8ba2297..ab39bd6 100644 --- a/packages/typescript-types/src/index.ts +++ b/packages/typescript-types/src/index.ts @@ -13,6 +13,8 @@ import type { PublicKeyCredentialDescriptor, PublicKeyCredentialRequestOptions, PublicKeyCredentialUserEntity, + AuthenticationExtensionsClientInputs, + AuthenticationExtensionsClientOutputs, } from './dom'; export * from './dom'; @@ -26,6 +28,7 @@ export interface PublicKeyCredentialCreationOptionsJSON user: PublicKeyCredentialUserEntityJSON; challenge: Base64URLString; excludeCredentials: PublicKeyCredentialDescriptorJSON[]; + extensions?: AuthenticationExtensionsClientInputs; } /** @@ -36,6 +39,7 @@ export interface PublicKeyCredentialRequestOptionsJSON extends Omit<PublicKeyCredentialRequestOptions, 'challenge' | 'allowCredentials'> { challenge: Base64URLString; allowCredentials?: PublicKeyCredentialDescriptorJSON[]; + extensions?: AuthenticationExtensionsClientInputs; } export interface PublicKeyCredentialDescriptorJSON @@ -63,6 +67,7 @@ export interface AttestationCredentialJSON extends Omit<AttestationCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { rawId: Base64URLString; response: AuthenticatorAttestationResponseJSON; + clientExtensionResults: AuthenticationExtensionsClientOutputs; transports?: AuthenticatorTransport[]; } @@ -81,6 +86,7 @@ export interface AssertionCredentialJSON extends Omit<AssertionCredential, 'response' | 'rawId' | 'getClientExtensionResults'> { rawId: Base64URLString; response: AuthenticatorAssertionResponseJSON; + clientExtensionResults: AuthenticationExtensionsClientOutputs; } /** |