accounts 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/core/Schema.d.ts +12 -12
- package/dist/core/adapters/dialog.d.ts.map +1 -1
- package/dist/core/adapters/dialog.js +3 -1
- package/dist/core/adapters/dialog.js.map +1 -1
- package/dist/core/zod/rpc.d.ts +9 -9
- package/dist/core/zod/rpc.js +1 -1
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/server/CliAuth.d.ts +11 -11
- package/dist/server/CliAuth.js +1 -1
- package/dist/server/CliAuth.js.map +1 -1
- package/dist/server/Handler.d.ts +4 -252
- package/dist/server/Handler.d.ts.map +1 -1
- package/dist/server/Handler.js +4 -573
- package/dist/server/Handler.js.map +1 -1
- package/dist/server/internal/handlers/codeAuth.d.ts +41 -0
- package/dist/server/internal/handlers/codeAuth.d.ts.map +1 -0
- package/dist/server/internal/handlers/codeAuth.js +104 -0
- package/dist/server/internal/handlers/codeAuth.js.map +1 -0
- package/dist/server/internal/handlers/feePayer.d.ts +73 -0
- package/dist/server/internal/handlers/feePayer.d.ts.map +1 -0
- package/dist/server/internal/handlers/feePayer.js +184 -0
- package/dist/server/internal/handlers/feePayer.js.map +1 -0
- package/dist/server/internal/handlers/relay.d.ts +148 -0
- package/dist/server/internal/handlers/relay.d.ts.map +1 -0
- package/dist/server/internal/handlers/relay.js +600 -0
- package/dist/server/internal/handlers/relay.js.map +1 -0
- package/dist/server/internal/handlers/utils.d.ts +12 -0
- package/dist/server/internal/handlers/utils.d.ts.map +1 -0
- package/dist/server/internal/handlers/utils.js +80 -0
- package/dist/server/internal/handlers/utils.js.map +1 -0
- package/dist/server/internal/handlers/webAuthn.d.ts +57 -0
- package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -0
- package/dist/server/internal/handlers/webAuthn.js +143 -0
- package/dist/server/internal/handlers/webAuthn.js.map +1 -0
- package/package.json +2 -2
- package/src/core/Provider.connect.browser.test.ts +23 -2
- package/src/core/adapters/dialog.ts +6 -1
- package/src/core/zod/rpc.ts +1 -1
- package/src/server/CliAuth.ts +1 -1
- package/src/server/Handler.test.ts +3 -418
- package/src/server/Handler.ts +5 -766
- package/src/server/internal/handlers/codeAuth.ts +148 -0
- package/src/server/internal/handlers/feePayer.test.ts +335 -0
- package/src/server/internal/handlers/feePayer.ts +271 -0
- package/src/server/internal/handlers/relay.test.ts +767 -0
- package/src/server/internal/handlers/relay.ts +817 -0
- package/src/server/internal/handlers/utils.ts +96 -0
- package/src/server/internal/handlers/webAuthn.test.ts +170 -0
- package/src/server/internal/handlers/webAuthn.ts +213 -0
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { Elysia } from 'elysia'
|
|
2
2
|
import express from 'express'
|
|
3
3
|
import { Hono } from 'hono'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import { Transaction, withFeePayer } from 'viem/tempo'
|
|
8
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'vp/test'
|
|
9
|
-
|
|
10
|
-
import { accounts, chain, getClient, http } from '../../test/config.js'
|
|
11
|
-
import { createServer, type Server } from '../../test/utils.js'
|
|
12
|
-
import * as WebAuthnCeremony from '../core/WebAuthnCeremony.js'
|
|
4
|
+
import { describe, expect, test } from 'vp/test'
|
|
5
|
+
|
|
6
|
+
import { createServer } from '../../test/utils.js'
|
|
13
7
|
import * as Handler from './Handler.js'
|
|
14
|
-
import * as Kv from './Kv.js'
|
|
15
8
|
|
|
16
9
|
describe('from', () => {
|
|
17
10
|
describe('cors', () => {
|
|
@@ -696,411 +689,3 @@ describe('from', () => {
|
|
|
696
689
|
})
|
|
697
690
|
})
|
|
698
691
|
})
|
|
699
|
-
|
|
700
|
-
describe('feePayer', () => {
|
|
701
|
-
const userAccount = accounts[9]!
|
|
702
|
-
const feePayerAccount = accounts[0]!
|
|
703
|
-
|
|
704
|
-
let server: Server
|
|
705
|
-
let requests: RpcRequest.RpcRequest[] = []
|
|
706
|
-
|
|
707
|
-
beforeAll(async () => {
|
|
708
|
-
server = await createServer(
|
|
709
|
-
Handler.feePayer({
|
|
710
|
-
account: feePayerAccount,
|
|
711
|
-
chains: [chain],
|
|
712
|
-
transports: { [chain.id]: http() },
|
|
713
|
-
onRequest: async (request) => {
|
|
714
|
-
requests.push(request)
|
|
715
|
-
},
|
|
716
|
-
}).listener,
|
|
717
|
-
)
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
afterAll(() => {
|
|
721
|
-
server.close()
|
|
722
|
-
process.on('SIGINT', () => {
|
|
723
|
-
server.close()
|
|
724
|
-
process.exit(0)
|
|
725
|
-
})
|
|
726
|
-
process.on('SIGTERM', () => {
|
|
727
|
-
server.close()
|
|
728
|
-
process.exit(0)
|
|
729
|
-
})
|
|
730
|
-
})
|
|
731
|
-
|
|
732
|
-
afterEach(() => {
|
|
733
|
-
requests = []
|
|
734
|
-
})
|
|
735
|
-
|
|
736
|
-
async function rpc(request: Record<string, unknown>) {
|
|
737
|
-
return await fetch(server.url, {
|
|
738
|
-
body: Json.stringify(request),
|
|
739
|
-
headers: { 'content-type': 'application/json' },
|
|
740
|
-
method: 'POST',
|
|
741
|
-
}).then((response) => response.json())
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/** Signs a sponsor-bound Tempo transaction, preserving the feePayerSignature. */
|
|
745
|
-
async function signSponsoredTx(account: (typeof accounts)[number], transaction: object) {
|
|
746
|
-
const serialized = (await Transaction.serialize(transaction as never)) as `0x76${string}`
|
|
747
|
-
const envelope = TxEnvelopeTempo.deserialize(serialized)
|
|
748
|
-
const signature = await account.sign({
|
|
749
|
-
hash: TxEnvelopeTempo.getSignPayload(envelope),
|
|
750
|
-
})
|
|
751
|
-
return TxEnvelopeTempo.serialize(envelope, {
|
|
752
|
-
signature: SignatureEnvelope.from(signature),
|
|
753
|
-
})
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
describe('POST /', () => {
|
|
757
|
-
test('default: eth_fillTransaction returns a sponsor-bound transaction the sender can broadcast', async () => {
|
|
758
|
-
const response = (await rpc({
|
|
759
|
-
id: 1,
|
|
760
|
-
jsonrpc: '2.0',
|
|
761
|
-
method: 'eth_fillTransaction',
|
|
762
|
-
params: [
|
|
763
|
-
{
|
|
764
|
-
chainId: chain.id,
|
|
765
|
-
feePayer: true,
|
|
766
|
-
from: userAccount.address,
|
|
767
|
-
to: '0x0000000000000000000000000000000000000000',
|
|
768
|
-
},
|
|
769
|
-
],
|
|
770
|
-
})) as {
|
|
771
|
-
result: {
|
|
772
|
-
sponsor: { address: string }
|
|
773
|
-
tx: Record<string, unknown>
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
const prepared = core_Transaction.fromRpc(response.result.tx as never) as {
|
|
777
|
-
feePayerSignature?: unknown
|
|
778
|
-
}
|
|
779
|
-
const signed = await signSponsoredTx(userAccount, prepared)
|
|
780
|
-
const receipt = (await getClient().request({
|
|
781
|
-
method: 'eth_sendRawTransactionSync',
|
|
782
|
-
params: [signed],
|
|
783
|
-
})) as { feePayer?: string | undefined }
|
|
784
|
-
|
|
785
|
-
expect(response.result.sponsor.address).toBe(feePayerAccount.address)
|
|
786
|
-
expect(prepared?.feePayerSignature).toBeDefined()
|
|
787
|
-
expect(receipt.feePayer).toBe(feePayerAccount.address.toLowerCase())
|
|
788
|
-
expect(requests.map(({ method }) => method)).toMatchInlineSnapshot(`
|
|
789
|
-
[
|
|
790
|
-
"eth_fillTransaction",
|
|
791
|
-
]
|
|
792
|
-
`)
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
test('behavior: mutating a sponsor-bound transaction invalidates the fee payer binding', async () => {
|
|
796
|
-
const response = (await rpc({
|
|
797
|
-
id: 1,
|
|
798
|
-
jsonrpc: '2.0',
|
|
799
|
-
method: 'eth_fillTransaction',
|
|
800
|
-
params: [
|
|
801
|
-
{
|
|
802
|
-
chainId: chain.id,
|
|
803
|
-
feePayer: true,
|
|
804
|
-
from: userAccount.address,
|
|
805
|
-
to: '0x0000000000000000000000000000000000000000',
|
|
806
|
-
},
|
|
807
|
-
],
|
|
808
|
-
})) as {
|
|
809
|
-
result: { tx: Record<string, unknown> }
|
|
810
|
-
}
|
|
811
|
-
const prepared = core_Transaction.fromRpc(response.result.tx as never) as {
|
|
812
|
-
gas?: bigint | undefined
|
|
813
|
-
feePayerSignature?: unknown
|
|
814
|
-
}
|
|
815
|
-
const signed = await signSponsoredTx(userAccount, {
|
|
816
|
-
...prepared,
|
|
817
|
-
gas: (prepared?.gas ?? 0n) + 1n,
|
|
818
|
-
})
|
|
819
|
-
|
|
820
|
-
await expect(
|
|
821
|
-
getClient().request({
|
|
822
|
-
method: 'eth_sendRawTransactionSync',
|
|
823
|
-
params: [signed],
|
|
824
|
-
}),
|
|
825
|
-
).rejects.toThrowError()
|
|
826
|
-
})
|
|
827
|
-
|
|
828
|
-
test('behavior: eth_signRawTransaction', async () => {
|
|
829
|
-
const client = getClient({
|
|
830
|
-
account: userAccount,
|
|
831
|
-
transport: withFeePayer(http(), http(server.url)),
|
|
832
|
-
})
|
|
833
|
-
|
|
834
|
-
const receipt = await sendTransactionSync(client, {
|
|
835
|
-
feePayer: true,
|
|
836
|
-
to: '0x0000000000000000000000000000000000000000',
|
|
837
|
-
})
|
|
838
|
-
|
|
839
|
-
expect(receipt.feePayer).toBe(feePayerAccount.address.toLowerCase())
|
|
840
|
-
|
|
841
|
-
expect(requests.map(({ method }) => method)).toMatchInlineSnapshot(`
|
|
842
|
-
[
|
|
843
|
-
"eth_signRawTransaction",
|
|
844
|
-
]
|
|
845
|
-
`)
|
|
846
|
-
})
|
|
847
|
-
|
|
848
|
-
test('behavior: eth_sendRawTransaction', async () => {
|
|
849
|
-
const client = getClient({
|
|
850
|
-
account: userAccount,
|
|
851
|
-
transport: withFeePayer(http(), http(server.url), {
|
|
852
|
-
policy: 'sign-and-broadcast',
|
|
853
|
-
}),
|
|
854
|
-
})
|
|
855
|
-
|
|
856
|
-
const receipt = await sendTransactionSync(client, {
|
|
857
|
-
feePayer: true,
|
|
858
|
-
to: '0x0000000000000000000000000000000000000000',
|
|
859
|
-
})
|
|
860
|
-
|
|
861
|
-
expect(receipt.feePayer).toBe(feePayerAccount.address.toLowerCase())
|
|
862
|
-
|
|
863
|
-
expect(requests.map(({ method }) => method)).toMatchInlineSnapshot(`
|
|
864
|
-
[
|
|
865
|
-
"eth_sendRawTransactionSync",
|
|
866
|
-
]
|
|
867
|
-
`)
|
|
868
|
-
})
|
|
869
|
-
|
|
870
|
-
test('behavior: eth_sendRawTransactionSync', async () => {
|
|
871
|
-
const client = getClient({
|
|
872
|
-
account: userAccount,
|
|
873
|
-
transport: withFeePayer(http(), http(server.url), {
|
|
874
|
-
policy: 'sign-and-broadcast',
|
|
875
|
-
}),
|
|
876
|
-
})
|
|
877
|
-
|
|
878
|
-
const receipt = await sendTransactionSync(client, {
|
|
879
|
-
feePayer: true,
|
|
880
|
-
to: '0x0000000000000000000000000000000000000000',
|
|
881
|
-
})
|
|
882
|
-
|
|
883
|
-
expect(receipt.feePayer).toBe(feePayerAccount.address.toLowerCase())
|
|
884
|
-
|
|
885
|
-
expect(requests.map(({ method }) => method)).toMatchInlineSnapshot(`
|
|
886
|
-
[
|
|
887
|
-
"eth_sendRawTransactionSync",
|
|
888
|
-
]
|
|
889
|
-
`)
|
|
890
|
-
})
|
|
891
|
-
|
|
892
|
-
test('behavior: unsupported method', async () => {
|
|
893
|
-
await expect(
|
|
894
|
-
fetch(server.url, {
|
|
895
|
-
method: 'POST',
|
|
896
|
-
body: JSON.stringify({
|
|
897
|
-
jsonrpc: '2.0',
|
|
898
|
-
id: 1,
|
|
899
|
-
method: 'eth_chainId',
|
|
900
|
-
}),
|
|
901
|
-
}).then((response) => response.json()),
|
|
902
|
-
).resolves.toMatchInlineSnapshot(`
|
|
903
|
-
{
|
|
904
|
-
"error": {
|
|
905
|
-
"code": -32004,
|
|
906
|
-
"name": "RpcResponse.MethodNotSupportedError",
|
|
907
|
-
"stack": "",
|
|
908
|
-
},
|
|
909
|
-
"id": 1,
|
|
910
|
-
"jsonrpc": "2.0",
|
|
911
|
-
}
|
|
912
|
-
`)
|
|
913
|
-
})
|
|
914
|
-
|
|
915
|
-
test('behavior: internal error', async () => {
|
|
916
|
-
const response = await fetch(server.url, {
|
|
917
|
-
method: 'POST',
|
|
918
|
-
body: JSON.stringify({
|
|
919
|
-
jsonrpc: '2.0',
|
|
920
|
-
id: 1,
|
|
921
|
-
method: 'eth_signRawTransaction',
|
|
922
|
-
params: ['0xinvalid'],
|
|
923
|
-
}),
|
|
924
|
-
})
|
|
925
|
-
|
|
926
|
-
const data = await response.json()
|
|
927
|
-
expect(data).toMatchInlineSnapshot(`
|
|
928
|
-
{
|
|
929
|
-
"error": {
|
|
930
|
-
"code": -32602,
|
|
931
|
-
"name": "RpcResponse.InvalidParamsError",
|
|
932
|
-
"stack": "",
|
|
933
|
-
},
|
|
934
|
-
"id": 1,
|
|
935
|
-
"jsonrpc": "2.0",
|
|
936
|
-
}
|
|
937
|
-
`)
|
|
938
|
-
})
|
|
939
|
-
})
|
|
940
|
-
})
|
|
941
|
-
|
|
942
|
-
describe('webauthn', () => {
|
|
943
|
-
let server: Server
|
|
944
|
-
let ceremony: WebAuthnCeremony.WebAuthnCeremony
|
|
945
|
-
|
|
946
|
-
beforeAll(async () => {
|
|
947
|
-
server = await createServer(
|
|
948
|
-
Handler.webAuthn({
|
|
949
|
-
kv: Kv.memory(),
|
|
950
|
-
origin: 'http://localhost',
|
|
951
|
-
rpId: 'localhost',
|
|
952
|
-
}).listener,
|
|
953
|
-
)
|
|
954
|
-
ceremony = WebAuthnCeremony.server({ url: server.url })
|
|
955
|
-
})
|
|
956
|
-
|
|
957
|
-
afterAll(async () => {
|
|
958
|
-
await server.closeAsync()
|
|
959
|
-
})
|
|
960
|
-
|
|
961
|
-
describe('POST /register/options', () => {
|
|
962
|
-
test('default: returns registration options', async () => {
|
|
963
|
-
const { options } = await ceremony.getRegistrationOptions({ name: 'Test' })
|
|
964
|
-
expect(options.publicKey).toBeDefined()
|
|
965
|
-
expect(options.publicKey!.rp.id).toMatchInlineSnapshot(`"localhost"`)
|
|
966
|
-
expect(options.publicKey!.rp.name).toMatchInlineSnapshot(`"localhost"`)
|
|
967
|
-
expect(typeof options.publicKey!.challenge).toMatchInlineSnapshot(`"string"`)
|
|
968
|
-
})
|
|
969
|
-
|
|
970
|
-
test('behavior: each call generates a unique challenge', async () => {
|
|
971
|
-
const { options: a } = await ceremony.getRegistrationOptions({ name: 'Test' })
|
|
972
|
-
const { options: b } = await ceremony.getRegistrationOptions({ name: 'Test' })
|
|
973
|
-
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
|
|
974
|
-
})
|
|
975
|
-
})
|
|
976
|
-
|
|
977
|
-
describe('POST /login/options', () => {
|
|
978
|
-
test('default: returns authentication options', async () => {
|
|
979
|
-
const { options } = await ceremony.getAuthenticationOptions()
|
|
980
|
-
expect(options.publicKey).toBeDefined()
|
|
981
|
-
expect(options.publicKey!.rpId).toMatchInlineSnapshot(`"localhost"`)
|
|
982
|
-
expect(typeof options.publicKey!.challenge).toMatchInlineSnapshot(`"string"`)
|
|
983
|
-
})
|
|
984
|
-
|
|
985
|
-
test('behavior: each call generates a unique challenge', async () => {
|
|
986
|
-
const { options: a } = await ceremony.getAuthenticationOptions()
|
|
987
|
-
const { options: b } = await ceremony.getAuthenticationOptions()
|
|
988
|
-
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
|
|
989
|
-
})
|
|
990
|
-
})
|
|
991
|
-
|
|
992
|
-
describe('POST /register', () => {
|
|
993
|
-
test('error: invalid credential → 400', async () => {
|
|
994
|
-
const response = await fetch(`${server.url}/register`, {
|
|
995
|
-
method: 'POST',
|
|
996
|
-
headers: { 'Content-Type': 'application/json' },
|
|
997
|
-
body: JSON.stringify({ id: 'fake', clientDataJSON: 'bad', attestationObject: 'bad' }),
|
|
998
|
-
})
|
|
999
|
-
expect(response.status).toBe(400)
|
|
1000
|
-
const body = await response.json()
|
|
1001
|
-
expect(body.error).toBeTypeOf('string')
|
|
1002
|
-
})
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1005
|
-
describe('POST /login', () => {
|
|
1006
|
-
test('error: unknown credential → 400', async () => {
|
|
1007
|
-
const response = await fetch(`${server.url}/login`, {
|
|
1008
|
-
method: 'POST',
|
|
1009
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1010
|
-
body: JSON.stringify({
|
|
1011
|
-
id: 'unknown',
|
|
1012
|
-
metadata: { authenticatorData: '0x00', clientDataJSON: '{"challenge":"0xdead"}' },
|
|
1013
|
-
raw: {
|
|
1014
|
-
id: 'unknown',
|
|
1015
|
-
type: 'public-key',
|
|
1016
|
-
authenticatorAttachment: null,
|
|
1017
|
-
rawId: 'unknown',
|
|
1018
|
-
response: { clientDataJSON: 'e30' },
|
|
1019
|
-
},
|
|
1020
|
-
signature: '0x00',
|
|
1021
|
-
}),
|
|
1022
|
-
})
|
|
1023
|
-
expect(response.status).toBe(400)
|
|
1024
|
-
const body = await response.json()
|
|
1025
|
-
expect(body.error).toMatchInlineSnapshot(`"Missing or expired challenge"`)
|
|
1026
|
-
})
|
|
1027
|
-
})
|
|
1028
|
-
|
|
1029
|
-
describe('challenge replay', () => {
|
|
1030
|
-
test('behavior: challenge consumed after register/options → re-fetching is required', async () => {
|
|
1031
|
-
// Get options twice — each should have a unique challenge stored in KV
|
|
1032
|
-
const { options: a } = await ceremony.getRegistrationOptions({ name: 'Replay' })
|
|
1033
|
-
const { options: b } = await ceremony.getRegistrationOptions({ name: 'Replay' })
|
|
1034
|
-
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
|
|
1035
|
-
})
|
|
1036
|
-
|
|
1037
|
-
test('behavior: challenge consumed after login/options → re-fetching is required', async () => {
|
|
1038
|
-
const { options: a } = await ceremony.getAuthenticationOptions()
|
|
1039
|
-
const { options: b } = await ceremony.getAuthenticationOptions()
|
|
1040
|
-
expect(a.publicKey!.challenge).not.toBe(b.publicKey!.challenge)
|
|
1041
|
-
})
|
|
1042
|
-
})
|
|
1043
|
-
|
|
1044
|
-
describe('hooks', () => {
|
|
1045
|
-
test('behavior: onRegister error does not call hook', async () => {
|
|
1046
|
-
let called = false
|
|
1047
|
-
const hookServer = await createServer(
|
|
1048
|
-
Handler.webAuthn({
|
|
1049
|
-
kv: Kv.memory(),
|
|
1050
|
-
origin: 'http://localhost',
|
|
1051
|
-
rpId: 'localhost',
|
|
1052
|
-
onRegister() {
|
|
1053
|
-
called = true
|
|
1054
|
-
return Response.json({ extra: true })
|
|
1055
|
-
},
|
|
1056
|
-
}).listener,
|
|
1057
|
-
)
|
|
1058
|
-
|
|
1059
|
-
const response = await fetch(`${hookServer.url}/register`, {
|
|
1060
|
-
method: 'POST',
|
|
1061
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1062
|
-
body: JSON.stringify({ id: 'fake', clientDataJSON: 'bad', attestationObject: 'bad' }),
|
|
1063
|
-
})
|
|
1064
|
-
expect(response.status).toBe(400)
|
|
1065
|
-
expect(called).toBe(false)
|
|
1066
|
-
|
|
1067
|
-
await hookServer.closeAsync()
|
|
1068
|
-
})
|
|
1069
|
-
|
|
1070
|
-
test('behavior: onAuthenticate error does not call hook', async () => {
|
|
1071
|
-
let called = false
|
|
1072
|
-
const hookServer = await createServer(
|
|
1073
|
-
Handler.webAuthn({
|
|
1074
|
-
kv: Kv.memory(),
|
|
1075
|
-
origin: 'http://localhost',
|
|
1076
|
-
rpId: 'localhost',
|
|
1077
|
-
onAuthenticate() {
|
|
1078
|
-
called = true
|
|
1079
|
-
return Response.json({ extra: true })
|
|
1080
|
-
},
|
|
1081
|
-
}).listener,
|
|
1082
|
-
)
|
|
1083
|
-
|
|
1084
|
-
const response = await fetch(`${hookServer.url}/login`, {
|
|
1085
|
-
method: 'POST',
|
|
1086
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1087
|
-
body: JSON.stringify({
|
|
1088
|
-
id: 'unknown',
|
|
1089
|
-
metadata: { authenticatorData: '0x00', clientDataJSON: '{"challenge":"0xdead"}' },
|
|
1090
|
-
raw: {
|
|
1091
|
-
id: 'unknown',
|
|
1092
|
-
type: 'public-key',
|
|
1093
|
-
authenticatorAttachment: null,
|
|
1094
|
-
rawId: 'unknown',
|
|
1095
|
-
response: { clientDataJSON: 'e30' },
|
|
1096
|
-
},
|
|
1097
|
-
signature: '0x00',
|
|
1098
|
-
}),
|
|
1099
|
-
})
|
|
1100
|
-
expect(response.status).toBe(400)
|
|
1101
|
-
expect(called).toBe(false)
|
|
1102
|
-
|
|
1103
|
-
await hookServer.closeAsync()
|
|
1104
|
-
})
|
|
1105
|
-
})
|
|
1106
|
-
})
|