@multiplatform.one/web3 7.23.1 → 7.24.0
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/README.md +150 -11
- package/dist/cjs/clients/evm.cjs +299 -11
- package/dist/cjs/clients/evm.native.js +500 -12
- package/dist/cjs/clients/evm.native.js.map +1 -1
- package/dist/cjs/clients/index.cjs +5 -0
- package/dist/cjs/clients/index.native.js +5 -0
- package/dist/cjs/clients/index.native.js.map +1 -1
- package/dist/cjs/clients/solana.cjs +39 -21
- package/dist/cjs/clients/solana.native.js +48 -23
- package/dist/cjs/clients/solana.native.js.map +1 -1
- package/dist/cjs/connectkit/GpgProvider.cjs +44 -0
- package/dist/cjs/connectkit/GpgProvider.native.js +51 -0
- package/dist/cjs/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/EthereumAdapter.cjs +6 -15
- package/dist/cjs/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/cjs/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/SolanaAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/adapters/SuiAdapter.cjs +5 -14
- package/dist/cjs/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/cjs/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/cjs/connectkit/connectors/createWalletMessage.cjs +2 -2
- package/dist/cjs/connectkit/connectors/createWalletMessage.native.js +2 -2
- package/dist/cjs/connectkit/connectors/createWalletMessage.native.js.map +1 -1
- package/dist/cjs/connectkit/index.cjs +4 -3
- package/dist/cjs/connectkit/index.native.js +4 -3
- package/dist/cjs/connectkit/index.native.js.map +1 -1
- package/dist/cjs/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/cjs/connectkit/siwe/SIWEProvider.cjs +18 -3
- package/dist/cjs/connectkit/siwe/SIWEProvider.native.js +18 -3
- package/dist/cjs/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/keycloak/KeycloakWalletLogin.cjs +255 -0
- package/dist/cjs/keycloak/KeycloakWalletLogin.native.js +33 -0
- package/dist/cjs/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/cjs/keycloak/browserFlow.cjs +83 -0
- package/dist/cjs/keycloak/browserFlow.native.js +90 -0
- package/dist/cjs/keycloak/browserFlow.native.js.map +1 -0
- package/dist/cjs/keycloak/device.cjs +36 -0
- package/dist/cjs/keycloak/device.native.js +45 -0
- package/dist/cjs/keycloak/device.native.js.map +1 -0
- package/dist/cjs/keycloak/directGrant.cjs +189 -0
- package/dist/cjs/keycloak/directGrant.native.js +233 -0
- package/dist/cjs/keycloak/directGrant.native.js.map +1 -0
- package/dist/cjs/keycloak/iife.cjs +4 -0
- package/dist/cjs/keycloak/iife.native.js +7 -0
- package/dist/cjs/keycloak/iife.native.js.map +1 -0
- package/dist/cjs/keycloak/index.cjs +53 -0
- package/dist/cjs/keycloak/index.native.js +56 -0
- package/dist/cjs/keycloak/index.native.js.map +1 -0
- package/dist/cjs/keycloak/keycloakWallet.cjs +214 -0
- package/dist/cjs/keycloak/keycloakWallet.native.js +285 -0
- package/dist/cjs/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/cjs/keycloak/mount.cjs +53 -0
- package/dist/cjs/keycloak/mount.native.js +33 -0
- package/dist/cjs/keycloak/mount.native.js.map +1 -0
- package/dist/cjs/keycloak/registerWallet.cjs +74 -0
- package/dist/cjs/keycloak/registerWallet.native.js +81 -0
- package/dist/cjs/keycloak/registerWallet.native.js.map +1 -0
- package/dist/cjs/keycloak/types.cjs +46 -0
- package/dist/cjs/keycloak/types.native.js +179 -0
- package/dist/cjs/keycloak/types.native.js.map +1 -0
- package/dist/cjs/keycloak/useDirectLogin.cjs +57 -0
- package/dist/cjs/keycloak/useDirectLogin.native.js +77 -0
- package/dist/cjs/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/cjs/keycloak/useWalletLogin.cjs +97 -0
- package/dist/cjs/keycloak/useWalletLogin.native.js +33 -0
- package/dist/cjs/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/cjs/message/buildWalletMessage.cjs +113 -0
- package/dist/cjs/message/buildWalletMessage.native.js +121 -0
- package/dist/cjs/message/buildWalletMessage.native.js.map +1 -0
- package/dist/cjs/message/index.cjs +29 -0
- package/dist/cjs/message/index.native.js +32 -0
- package/dist/cjs/message/index.native.js.map +1 -0
- package/dist/cjs/providers/WalletProvider.cjs +8 -2
- package/dist/cjs/siws/message.cjs +5 -16
- package/dist/cjs/siws/message.native.js +6 -16
- package/dist/cjs/siws/message.native.js.map +1 -1
- package/dist/cjs/solana.native.js.map +1 -1
- package/dist/esm/clients/evm.mjs +295 -12
- package/dist/esm/clients/evm.mjs.map +1 -1
- package/dist/esm/clients/evm.native.js +496 -13
- package/dist/esm/clients/evm.native.js.map +1 -1
- package/dist/esm/clients/index.mjs +2 -2
- package/dist/esm/clients/index.mjs.map +1 -1
- package/dist/esm/clients/index.native.js +2 -2
- package/dist/esm/clients/index.native.js.map +1 -1
- package/dist/esm/clients/solana.mjs +39 -21
- package/dist/esm/clients/solana.mjs.map +1 -1
- package/dist/esm/clients/solana.native.js +48 -23
- package/dist/esm/clients/solana.native.js.map +1 -1
- package/dist/esm/connectkit/GpgProvider.mjs +19 -0
- package/dist/esm/connectkit/GpgProvider.mjs.map +1 -0
- package/dist/esm/connectkit/GpgProvider.native.js +23 -0
- package/dist/esm/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/esm/connectkit/adapters/BitcoinAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/BitcoinAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/EthereumAdapter.mjs +6 -15
- package/dist/esm/connectkit/adapters/EthereumAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/esm/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/SolanaAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/SolanaAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/adapters/SuiAdapter.mjs +5 -14
- package/dist/esm/connectkit/adapters/SuiAdapter.mjs.map +1 -1
- package/dist/esm/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/esm/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/esm/connectkit/connectors/createWalletMessage.mjs +1 -1
- package/dist/esm/connectkit/connectors/createWalletMessage.native.js +1 -1
- package/dist/esm/connectkit/index.mjs +3 -2
- package/dist/esm/connectkit/index.mjs.map +1 -1
- package/dist/esm/connectkit/index.native.js +3 -2
- package/dist/esm/connectkit/index.native.js.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEContext.mjs.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEProvider.mjs +19 -4
- package/dist/esm/connectkit/siwe/SIWEProvider.mjs.map +1 -1
- package/dist/esm/connectkit/siwe/SIWEProvider.native.js +19 -4
- package/dist/esm/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/keycloak/KeycloakWalletLogin.mjs +218 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.mjs.map +1 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.native.js +5 -0
- package/dist/esm/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/esm/keycloak/browserFlow.mjs +58 -0
- package/dist/esm/keycloak/browserFlow.mjs.map +1 -0
- package/dist/esm/keycloak/browserFlow.native.js +62 -0
- package/dist/esm/keycloak/browserFlow.native.js.map +1 -0
- package/dist/esm/keycloak/device.mjs +10 -0
- package/dist/esm/keycloak/device.mjs.map +1 -0
- package/dist/esm/keycloak/device.native.js +16 -0
- package/dist/esm/keycloak/device.native.js.map +1 -0
- package/dist/esm/keycloak/directGrant.mjs +164 -0
- package/dist/esm/keycloak/directGrant.mjs.map +1 -0
- package/dist/esm/keycloak/directGrant.native.js +205 -0
- package/dist/esm/keycloak/directGrant.native.js.map +1 -0
- package/dist/esm/keycloak/iife.mjs +5 -0
- package/dist/esm/keycloak/iife.mjs.map +1 -0
- package/dist/esm/keycloak/iife.native.js +5 -0
- package/dist/esm/keycloak/iife.native.js.map +1 -0
- package/dist/esm/keycloak/index.mjs +13 -0
- package/dist/esm/keycloak/index.mjs.map +1 -0
- package/dist/esm/keycloak/index.native.js +13 -0
- package/dist/esm/keycloak/index.native.js.map +1 -0
- package/dist/esm/keycloak/keycloakWallet.mjs +186 -0
- package/dist/esm/keycloak/keycloakWallet.mjs.map +1 -0
- package/dist/esm/keycloak/keycloakWallet.native.js +254 -0
- package/dist/esm/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/esm/keycloak/mount.mjs +28 -0
- package/dist/esm/keycloak/mount.mjs.map +1 -0
- package/dist/esm/keycloak/mount.native.js +5 -0
- package/dist/esm/keycloak/mount.native.js.map +1 -0
- package/dist/esm/keycloak/registerWallet.mjs +49 -0
- package/dist/esm/keycloak/registerWallet.mjs.map +1 -0
- package/dist/esm/keycloak/registerWallet.native.js +53 -0
- package/dist/esm/keycloak/registerWallet.native.js.map +1 -0
- package/dist/esm/keycloak/types.mjs +20 -0
- package/dist/esm/keycloak/types.mjs.map +1 -0
- package/dist/esm/keycloak/types.native.js +150 -0
- package/dist/esm/keycloak/types.native.js.map +1 -0
- package/dist/esm/keycloak/useDirectLogin.mjs +32 -0
- package/dist/esm/keycloak/useDirectLogin.mjs.map +1 -0
- package/dist/esm/keycloak/useDirectLogin.native.js +49 -0
- package/dist/esm/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/esm/keycloak/useWalletLogin.mjs +72 -0
- package/dist/esm/keycloak/useWalletLogin.mjs.map +1 -0
- package/dist/esm/keycloak/useWalletLogin.native.js +5 -0
- package/dist/esm/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/esm/message/buildWalletMessage.mjs +87 -0
- package/dist/esm/message/buildWalletMessage.mjs.map +1 -0
- package/dist/esm/message/buildWalletMessage.native.js +92 -0
- package/dist/esm/message/buildWalletMessage.native.js.map +1 -0
- package/dist/esm/message/index.mjs +3 -0
- package/dist/esm/message/index.mjs.map +1 -0
- package/dist/esm/message/index.native.js +3 -0
- package/dist/esm/message/index.native.js.map +1 -0
- package/dist/esm/providers/WalletProvider.mjs +9 -3
- package/dist/esm/providers/WalletProvider.mjs.map +1 -1
- package/dist/esm/siws/message.mjs +5 -16
- package/dist/esm/siws/message.mjs.map +1 -1
- package/dist/esm/siws/message.native.js +6 -16
- package/dist/esm/siws/message.native.js.map +1 -1
- package/dist/esm/solana.mjs.map +1 -1
- package/dist/esm/solana.native.js.map +1 -1
- package/dist/jsx/clients/evm.mjs +295 -12
- package/dist/jsx/clients/evm.mjs.map +1 -1
- package/dist/jsx/clients/evm.native.js +500 -12
- package/dist/jsx/clients/evm.native.js.map +1 -1
- package/dist/jsx/clients/index.mjs +2 -2
- package/dist/jsx/clients/index.mjs.map +1 -1
- package/dist/jsx/clients/index.native.js +5 -0
- package/dist/jsx/clients/index.native.js.map +1 -1
- package/dist/jsx/clients/solana.mjs +39 -21
- package/dist/jsx/clients/solana.mjs.map +1 -1
- package/dist/jsx/clients/solana.native.js +48 -23
- package/dist/jsx/clients/solana.native.js.map +1 -1
- package/dist/jsx/connectkit/GpgProvider.mjs +19 -0
- package/dist/jsx/connectkit/GpgProvider.mjs.map +1 -0
- package/dist/jsx/connectkit/GpgProvider.native.js +51 -0
- package/dist/jsx/connectkit/GpgProvider.native.js.map +1 -0
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/BitcoinAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/EthereumAdapter.mjs +6 -15
- package/dist/jsx/connectkit/adapters/EthereumAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/EthereumAdapter.native.js +7 -16
- package/dist/jsx/connectkit/adapters/EthereumAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/SolanaAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/SolanaAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/SolanaAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/SolanaAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/adapters/SuiAdapter.mjs +5 -14
- package/dist/jsx/connectkit/adapters/SuiAdapter.mjs.map +1 -1
- package/dist/jsx/connectkit/adapters/SuiAdapter.native.js +5 -15
- package/dist/jsx/connectkit/adapters/SuiAdapter.native.js.map +1 -1
- package/dist/jsx/connectkit/connectors/createWalletMessage.mjs +1 -1
- package/dist/jsx/connectkit/connectors/createWalletMessage.native.js +2 -2
- package/dist/jsx/connectkit/index.mjs +3 -2
- package/dist/jsx/connectkit/index.mjs.map +1 -1
- package/dist/jsx/connectkit/index.native.js +4 -3
- package/dist/jsx/connectkit/index.native.js.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEContext.mjs.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEContext.native.js.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEProvider.mjs +19 -4
- package/dist/jsx/connectkit/siwe/SIWEProvider.mjs.map +1 -1
- package/dist/jsx/connectkit/siwe/SIWEProvider.native.js +18 -3
- package/dist/jsx/connectkit/siwe/SIWEProvider.native.js.map +1 -1
- package/dist/jsx/index.js +1 -0
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +1 -0
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +1 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/keycloak/KeycloakWalletLogin.mjs +218 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.native.js +33 -0
- package/dist/jsx/keycloak/KeycloakWalletLogin.native.js.map +1 -0
- package/dist/jsx/keycloak/browserFlow.mjs +58 -0
- package/dist/jsx/keycloak/browserFlow.mjs.map +1 -0
- package/dist/jsx/keycloak/browserFlow.native.js +90 -0
- package/dist/jsx/keycloak/browserFlow.native.js.map +1 -0
- package/dist/jsx/keycloak/device.mjs +10 -0
- package/dist/jsx/keycloak/device.mjs.map +1 -0
- package/dist/jsx/keycloak/device.native.js +45 -0
- package/dist/jsx/keycloak/device.native.js.map +1 -0
- package/dist/jsx/keycloak/directGrant.mjs +164 -0
- package/dist/jsx/keycloak/directGrant.mjs.map +1 -0
- package/dist/jsx/keycloak/directGrant.native.js +233 -0
- package/dist/jsx/keycloak/directGrant.native.js.map +1 -0
- package/dist/jsx/keycloak/iife.mjs +5 -0
- package/dist/jsx/keycloak/iife.mjs.map +1 -0
- package/dist/jsx/keycloak/iife.native.js +7 -0
- package/dist/jsx/keycloak/iife.native.js.map +1 -0
- package/dist/jsx/keycloak/index.mjs +13 -0
- package/dist/jsx/keycloak/index.mjs.map +1 -0
- package/dist/jsx/keycloak/index.native.js +56 -0
- package/dist/jsx/keycloak/index.native.js.map +1 -0
- package/dist/jsx/keycloak/keycloakWallet.mjs +186 -0
- package/dist/jsx/keycloak/keycloakWallet.mjs.map +1 -0
- package/dist/jsx/keycloak/keycloakWallet.native.js +285 -0
- package/dist/jsx/keycloak/keycloakWallet.native.js.map +1 -0
- package/dist/jsx/keycloak/mount.mjs +28 -0
- package/dist/jsx/keycloak/mount.mjs.map +1 -0
- package/dist/jsx/keycloak/mount.native.js +33 -0
- package/dist/jsx/keycloak/mount.native.js.map +1 -0
- package/dist/jsx/keycloak/registerWallet.mjs +49 -0
- package/dist/jsx/keycloak/registerWallet.mjs.map +1 -0
- package/dist/jsx/keycloak/registerWallet.native.js +81 -0
- package/dist/jsx/keycloak/registerWallet.native.js.map +1 -0
- package/dist/jsx/keycloak/types.mjs +20 -0
- package/dist/jsx/keycloak/types.mjs.map +1 -0
- package/dist/jsx/keycloak/types.native.js +179 -0
- package/dist/jsx/keycloak/types.native.js.map +1 -0
- package/dist/jsx/keycloak/useDirectLogin.mjs +32 -0
- package/dist/jsx/keycloak/useDirectLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/useDirectLogin.native.js +77 -0
- package/dist/jsx/keycloak/useDirectLogin.native.js.map +1 -0
- package/dist/jsx/keycloak/useWalletLogin.mjs +72 -0
- package/dist/jsx/keycloak/useWalletLogin.mjs.map +1 -0
- package/dist/jsx/keycloak/useWalletLogin.native.js +33 -0
- package/dist/jsx/keycloak/useWalletLogin.native.js.map +1 -0
- package/dist/jsx/message/buildWalletMessage.mjs +87 -0
- package/dist/jsx/message/buildWalletMessage.mjs.map +1 -0
- package/dist/jsx/message/buildWalletMessage.native.js +121 -0
- package/dist/jsx/message/buildWalletMessage.native.js.map +1 -0
- package/dist/jsx/message/index.mjs +3 -0
- package/dist/jsx/message/index.mjs.map +1 -0
- package/dist/jsx/message/index.native.js +32 -0
- package/dist/jsx/message/index.native.js.map +1 -0
- package/dist/jsx/providers/WalletProvider.mjs +9 -3
- package/dist/jsx/providers/WalletProvider.mjs.map +1 -1
- package/dist/jsx/siws/message.mjs +5 -16
- package/dist/jsx/siws/message.mjs.map +1 -1
- package/dist/jsx/siws/message.native.js +6 -16
- package/dist/jsx/siws/message.native.js.map +1 -1
- package/dist/jsx/solana.mjs.map +1 -1
- package/dist/jsx/solana.native.js.map +1 -1
- package/dist/keycloak/login.iife.js +3604 -0
- package/package.json +27 -8
- package/src/clients/evm.spec.ts +644 -10
- package/src/clients/evm.ts +498 -10
- package/src/clients/index.ts +8 -0
- package/src/clients/solana.spec.ts +116 -0
- package/src/clients/solana.ts +51 -31
- package/src/connectkit/GpgProvider.tsx +16 -0
- package/src/connectkit/adapters/BitcoinAdapter.ts +5 -16
- package/src/connectkit/adapters/EthereumAdapter.ts +6 -17
- package/src/connectkit/adapters/SolanaAdapter.ts +5 -16
- package/src/connectkit/adapters/SuiAdapter.ts +5 -16
- package/src/connectkit/connectors/createWalletMessage.ts +1 -1
- package/src/connectkit/index.ts +2 -6
- package/src/connectkit/openConnector/modal.spec.tsx +1 -1
- package/src/connectkit/siwe/SIWEContext.tsx +6 -2
- package/src/connectkit/siwe/SIWEProvider.tsx +22 -5
- package/src/index.ts +3 -0
- package/src/keycloak/KeycloakWalletLogin.native.tsx +7 -0
- package/src/keycloak/KeycloakWalletLogin.spec.tsx +168 -0
- package/src/keycloak/KeycloakWalletLogin.tsx +251 -0
- package/src/keycloak/browserFlow.spec.ts +108 -0
- package/src/keycloak/browserFlow.ts +84 -0
- package/src/keycloak/device.ts +17 -0
- package/src/keycloak/directGrant.spec.ts +331 -0
- package/src/keycloak/directGrant.ts +214 -0
- package/src/keycloak/iife.ts +3 -0
- package/src/keycloak/index.ts +44 -0
- package/src/keycloak/keycloakWallet.spec.ts +216 -0
- package/src/keycloak/keycloakWallet.ts +275 -0
- package/src/keycloak/mount.native.ts +11 -0
- package/src/keycloak/mount.tsx +45 -0
- package/src/keycloak/nonceReset.spec.tsx +205 -0
- package/src/keycloak/registerWallet.spec.ts +88 -0
- package/src/keycloak/registerWallet.ts +59 -0
- package/src/keycloak/types.ts +66 -0
- package/src/keycloak/useDirectLogin.spec.tsx +112 -0
- package/src/keycloak/useDirectLogin.ts +74 -0
- package/src/keycloak/useWalletLogin.native.ts +7 -0
- package/src/keycloak/useWalletLogin.ts +99 -0
- package/src/message/buildWalletMessage.spec.ts +291 -0
- package/src/message/buildWalletMessage.ts +150 -0
- package/src/message/index.ts +8 -0
- package/src/providers/WalletProvider.tsx +15 -2
- package/src/siws/message.ts +3 -22
- package/src/solana.ts +1 -0
- package/types/clients/evm.d.ts +144 -2
- package/types/clients/evm.d.ts.map +1 -1
- package/types/clients/index.d.ts +1 -1
- package/types/clients/index.d.ts.map +1 -1
- package/types/clients/solana.d.ts +14 -5
- package/types/clients/solana.d.ts.map +1 -1
- package/types/connectkit/GpgProvider.d.ts +10 -0
- package/types/connectkit/GpgProvider.d.ts.map +1 -0
- package/types/connectkit/adapters/BitcoinAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/EthereumAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/SolanaAdapter.d.ts.map +1 -1
- package/types/connectkit/adapters/SuiAdapter.d.ts.map +1 -1
- package/types/connectkit/connectors/createWalletMessage.d.ts +1 -1
- package/types/connectkit/connectors/createWalletMessage.d.ts.map +1 -1
- package/types/connectkit/index.d.ts +3 -2
- package/types/connectkit/index.d.ts.map +1 -1
- package/types/connectkit/siwe/SIWEContext.d.ts +2 -1
- package/types/connectkit/siwe/SIWEContext.d.ts.map +1 -1
- package/types/connectkit/siwe/SIWEProvider.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/keycloak/KeycloakWalletLogin.d.ts +45 -0
- package/types/keycloak/KeycloakWalletLogin.d.ts.map +1 -0
- package/types/keycloak/KeycloakWalletLogin.native.d.ts +4 -0
- package/types/keycloak/KeycloakWalletLogin.native.d.ts.map +1 -0
- package/types/keycloak/browserFlow.d.ts +27 -0
- package/types/keycloak/browserFlow.d.ts.map +1 -0
- package/types/keycloak/device.d.ts +14 -0
- package/types/keycloak/device.d.ts.map +1 -0
- package/types/keycloak/directGrant.d.ts +43 -0
- package/types/keycloak/directGrant.d.ts.map +1 -0
- package/types/keycloak/iife.d.ts +2 -0
- package/types/keycloak/iife.d.ts.map +1 -0
- package/types/keycloak/index.d.ts +12 -0
- package/types/keycloak/index.d.ts.map +1 -0
- package/types/keycloak/keycloakWallet.d.ts +120 -0
- package/types/keycloak/keycloakWallet.d.ts.map +1 -0
- package/types/keycloak/mount.d.ts +16 -0
- package/types/keycloak/mount.d.ts.map +1 -0
- package/types/keycloak/mount.native.d.ts +5 -0
- package/types/keycloak/mount.native.d.ts.map +1 -0
- package/types/keycloak/registerWallet.d.ts +22 -0
- package/types/keycloak/registerWallet.d.ts.map +1 -0
- package/types/keycloak/types.d.ts +50 -0
- package/types/keycloak/types.d.ts.map +1 -0
- package/types/keycloak/useDirectLogin.d.ts +29 -0
- package/types/keycloak/useDirectLogin.d.ts.map +1 -0
- package/types/keycloak/useWalletLogin.d.ts +26 -0
- package/types/keycloak/useWalletLogin.d.ts.map +1 -0
- package/types/keycloak/useWalletLogin.native.d.ts +4 -0
- package/types/keycloak/useWalletLogin.native.d.ts.map +1 -0
- package/types/message/buildWalletMessage.d.ts +46 -0
- package/types/message/buildWalletMessage.d.ts.map +1 -0
- package/types/message/index.d.ts +2 -0
- package/types/message/index.d.ts.map +1 -0
- package/types/providers/WalletProvider.d.ts +8 -1
- package/types/providers/WalletProvider.d.ts.map +1 -1
- package/types/siws/message.d.ts.map +1 -1
- package/types/solana.d.ts +1 -1
- package/types/solana.d.ts.map +1 -1
- package/dist/cjs/connectkit/adapters/messages.cjs +0 -73
- package/dist/cjs/connectkit/adapters/messages.native.js +0 -79
- package/dist/cjs/connectkit/adapters/messages.native.js.map +0 -1
- package/dist/esm/connectkit/adapters/messages.mjs +0 -48
- package/dist/esm/connectkit/adapters/messages.mjs.map +0 -1
- package/dist/esm/connectkit/adapters/messages.native.js +0 -51
- package/dist/esm/connectkit/adapters/messages.native.js.map +0 -1
- package/dist/jsx/connectkit/adapters/messages.mjs +0 -48
- package/dist/jsx/connectkit/adapters/messages.mjs.map +0 -1
- package/dist/jsx/connectkit/adapters/messages.native.js +0 -79
- package/dist/jsx/connectkit/adapters/messages.native.js.map +0 -1
- package/src/connectkit/adapters/messages.ts +0 -84
- package/types/connectkit/adapters/messages.d.ts +0 -16
- package/types/connectkit/adapters/messages.d.ts.map +0 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { createConfig, createStorage, http } from "wagmi";
|
|
5
|
+
import { mainnet } from "wagmi/chains";
|
|
6
|
+
import { mock } from "wagmi/connectors";
|
|
7
|
+
import { KeycloakWalletLogin, type KeycloakWalletLoginProps } from "./KeycloakWalletLogin";
|
|
8
|
+
|
|
9
|
+
const ANDROID_UA =
|
|
10
|
+
"Mozilla/5.0 (Linux; Android 16; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Mobile Safari/537.36";
|
|
11
|
+
const DESKTOP_UA =
|
|
12
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";
|
|
13
|
+
const ACCOUNT = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
|
14
|
+
const LOGIN_ACTION =
|
|
15
|
+
"https://kc.example.com/realms/main/login-actions/authenticate?session_code=sc&execution=ex&client_id=promanager&tab_id=tb";
|
|
16
|
+
const PGP_BLOB =
|
|
17
|
+
"-----BEGIN PGP SIGNATURE-----\nsig\n-----END PGP SIGNATURE-----\n-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey\n-----END PGP PUBLIC KEY BLOCK-----";
|
|
18
|
+
|
|
19
|
+
function setUserAgent(ua: string) {
|
|
20
|
+
Object.defineProperty(window.navigator, "userAgent", { value: ua, configurable: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function memoryStorage() {
|
|
24
|
+
const map = new Map<string, string>();
|
|
25
|
+
return {
|
|
26
|
+
getItem: (key: string) => map.get(key) ?? null,
|
|
27
|
+
setItem: (key: string, value: string) => void map.set(key, value),
|
|
28
|
+
removeItem: (key: string) => void map.delete(key),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function makeConfig() {
|
|
33
|
+
return createConfig({
|
|
34
|
+
chains: [mainnet],
|
|
35
|
+
connectors: [mock({ accounts: [ACCOUNT], features: {} })],
|
|
36
|
+
transports: { [mainnet.id]: http() },
|
|
37
|
+
storage: createStorage({ storage: memoryStorage() }),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function renderLogin(props: KeycloakWalletLoginProps = {}) {
|
|
42
|
+
const config = props.wagmiConfig ?? makeConfig();
|
|
43
|
+
const connectSpy = vi.spyOn(config.connectors[0], "connect");
|
|
44
|
+
render(
|
|
45
|
+
<KeycloakWalletLogin
|
|
46
|
+
baseUrl="https://kc.example.com"
|
|
47
|
+
realm="main"
|
|
48
|
+
resource="wallet"
|
|
49
|
+
wagmiConfig={config}
|
|
50
|
+
{...props}
|
|
51
|
+
/>,
|
|
52
|
+
);
|
|
53
|
+
const walletButton = document.getElementById("mpo-wallet-login") as HTMLButtonElement;
|
|
54
|
+
return { config, connectSpy, walletButton };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const nonceRequests: string[] = [];
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
nonceRequests.length = 0;
|
|
61
|
+
vi.stubGlobal(
|
|
62
|
+
"fetch",
|
|
63
|
+
vi.fn(async (input: RequestInfo | URL) => {
|
|
64
|
+
const url = String(input);
|
|
65
|
+
if (url.endsWith("/nonce")) {
|
|
66
|
+
nonceRequests.push(url);
|
|
67
|
+
return new Response("nonce123abc");
|
|
68
|
+
}
|
|
69
|
+
return new Response("not found", { status: 404 });
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
cleanup();
|
|
76
|
+
vi.unstubAllGlobals();
|
|
77
|
+
vi.restoreAllMocks();
|
|
78
|
+
window.sessionStorage.clear();
|
|
79
|
+
document.querySelectorAll("form").forEach((form) => form.remove());
|
|
80
|
+
setUserAgent(DESKTOP_UA);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe("KeycloakWalletLogin mobile.preferredConnector", () => {
|
|
84
|
+
it("a phone's tap goes straight to the preferred wallet, starting its connect inside the tap", async () => {
|
|
85
|
+
setUserAgent(ANDROID_UA);
|
|
86
|
+
const { config, connectSpy, walletButton } = renderLogin({
|
|
87
|
+
mobile: { preferredConnector: "mock" },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
fireEvent.click(walletButton);
|
|
91
|
+
// Still inside the click's task: only microtasks have run, no timer.
|
|
92
|
+
for (let i = 0; i < 50 && connectSpy.mock.calls.length === 0; i++) await Promise.resolve();
|
|
93
|
+
|
|
94
|
+
expect(connectSpy).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(screen.queryByText("Mock Connector")).not.toBeNull();
|
|
96
|
+
expect(document.querySelector("[data-screen-entry]")).toBeNull();
|
|
97
|
+
await waitFor(() => expect(config.state.status).toBe("connected"));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("desktop keeps the wallet list and connects nothing on the tap", async () => {
|
|
101
|
+
setUserAgent(DESKTOP_UA);
|
|
102
|
+
const { connectSpy, walletButton } = renderLogin({ mobile: { preferredConnector: "mock" } });
|
|
103
|
+
|
|
104
|
+
fireEvent.click(walletButton);
|
|
105
|
+
|
|
106
|
+
await screen.findByText("Mock Connector");
|
|
107
|
+
expect(document.querySelector('[data-screen-entry="signInWithGpg"]')).not.toBeNull();
|
|
108
|
+
expect(connectSpy).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("a phone with no preferred connector gets the wallet list", async () => {
|
|
112
|
+
setUserAgent(ANDROID_UA);
|
|
113
|
+
const { connectSpy, walletButton } = renderLogin();
|
|
114
|
+
|
|
115
|
+
fireEvent.click(walletButton);
|
|
116
|
+
|
|
117
|
+
await screen.findByText("Mock Connector");
|
|
118
|
+
expect(connectSpy).not.toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("KeycloakWalletLogin gpg", () => {
|
|
123
|
+
it("gpg: false drops the GPG button and the GPG row in the wallet list", async () => {
|
|
124
|
+
const { walletButton } = renderLogin({ gpg: false });
|
|
125
|
+
expect(document.getElementById("mpo-gpg-login")).toBeNull();
|
|
126
|
+
|
|
127
|
+
fireEvent.click(walletButton);
|
|
128
|
+
|
|
129
|
+
await screen.findByText("Mock Connector");
|
|
130
|
+
expect(document.querySelector('[data-screen-entry="signInWithGpg"]')).toBeNull();
|
|
131
|
+
expect(screen.queryByText("GPG")).toBeNull();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("offers GPG by default, as a button and as a row in the wallet list", async () => {
|
|
135
|
+
const { walletButton } = renderLogin();
|
|
136
|
+
expect(document.getElementById("mpo-gpg-login")).not.toBeNull();
|
|
137
|
+
|
|
138
|
+
fireEvent.click(walletButton);
|
|
139
|
+
|
|
140
|
+
await screen.findByText("Mock Connector");
|
|
141
|
+
expect(document.querySelector('[data-screen-entry="signInWithGpg"]')).not.toBeNull();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("KeycloakWalletLogin target", () => {
|
|
146
|
+
it("{ action } posts the credential to a keycloakify page's login action", async () => {
|
|
147
|
+
const submit = vi
|
|
148
|
+
.spyOn(HTMLFormElement.prototype, "submit")
|
|
149
|
+
.mockImplementation(function (this: HTMLFormElement) {});
|
|
150
|
+
renderLogin({ target: { action: LOGIN_ACTION } });
|
|
151
|
+
|
|
152
|
+
fireEvent.click(document.getElementById("mpo-gpg-login") as HTMLButtonElement);
|
|
153
|
+
const pasteBox = await screen.findByPlaceholderText(/BEGIN PGP SIGNATURE/);
|
|
154
|
+
await waitFor(() => expect(screen.queryByDisplayValue(/nonce123abc/)).not.toBeNull());
|
|
155
|
+
fireEvent.change(pasteBox, { target: { value: PGP_BLOB } });
|
|
156
|
+
fireEvent.click(screen.getByText("Sign in"));
|
|
157
|
+
|
|
158
|
+
await waitFor(() => expect(submit).toHaveBeenCalledTimes(1));
|
|
159
|
+
const form = submit.mock.contexts[0] as HTMLFormElement;
|
|
160
|
+
expect(form.action).toBe(LOGIN_ACTION);
|
|
161
|
+
expect(form.method).toBe("post");
|
|
162
|
+
const field = (name: string) =>
|
|
163
|
+
(form.querySelector(`input[name="${name}"]`) as HTMLInputElement | null)?.value;
|
|
164
|
+
expect(atob(field("username") ?? "")).toBe(PGP_BLOB);
|
|
165
|
+
expect(atob(field("password") ?? "")).toContain("nonce123abc");
|
|
166
|
+
expect(nonceRequests).toEqual(["https://kc.example.com/realms/main/wallet/nonce"]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { GpgProvider } from "@multiplatform.one/gpg";
|
|
2
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3
|
+
import { type CSSProperties, useMemo } from "react";
|
|
4
|
+
import type { Chain } from "viem";
|
|
5
|
+
import { type Config, WagmiProvider, createConfig } from "wagmi";
|
|
6
|
+
import { arbitrum, mainnet, optimism, polygon } from "wagmi/chains";
|
|
7
|
+
import getDefaultConfig from "../connectkit/defaultConfig";
|
|
8
|
+
import { SIWEProvider } from "../connectkit/siwe";
|
|
9
|
+
import { StatusState } from "../connectkit/siwe/SIWEContext";
|
|
10
|
+
import { useWeb3Modal } from "../connectkit/useWeb3Modal";
|
|
11
|
+
import type { MobileConnectOptions } from "../connectkit/openConnector";
|
|
12
|
+
import { ConnectKitProvider } from "../connectkit/web3Provider";
|
|
13
|
+
import type { BrowserFlowOptions } from "./browserFlow";
|
|
14
|
+
import { deriveKeycloakFromLocation, keycloakWallet } from "./keycloakWallet";
|
|
15
|
+
import type { KeycloakWalletResource } from "./types";
|
|
16
|
+
import { useWalletLogin } from "./useWalletLogin";
|
|
17
|
+
|
|
18
|
+
export interface KeycloakWalletLoginProps {
|
|
19
|
+
/** Defaults to the Keycloak base of the page it runs on. */
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
/** Defaults to the realm of the page it runs on. */
|
|
22
|
+
realm?: string;
|
|
23
|
+
resource?: KeycloakWalletResource;
|
|
24
|
+
domain?: string;
|
|
25
|
+
uri?: string;
|
|
26
|
+
target?: BrowserFlowOptions["target"];
|
|
27
|
+
whenVisible?: boolean;
|
|
28
|
+
appName?: string;
|
|
29
|
+
appDescription?: string;
|
|
30
|
+
appUrl?: string;
|
|
31
|
+
appIcon?: string;
|
|
32
|
+
chains?: readonly [Chain, ...Chain[]];
|
|
33
|
+
/** Offer GPG sign-in: its button and its row in the wallet list. Default true. */
|
|
34
|
+
gpg?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Phone behaviour of the wallet button. `{ preferredConnector: "metaMaskSDK" }`
|
|
37
|
+
* sends a phone straight to MetaMask from the tap, skipping the wallet list.
|
|
38
|
+
*/
|
|
39
|
+
mobile?: MobileConnectOptions;
|
|
40
|
+
walletLabel?: string;
|
|
41
|
+
gpgLabel?: string;
|
|
42
|
+
className?: string;
|
|
43
|
+
/** Replaces the built-in button look, e.g. the theme's `kcButtonClass`. */
|
|
44
|
+
buttonClassName?: string;
|
|
45
|
+
/** The wagmi config to sign with; built from the app props and `chains` when absent. */
|
|
46
|
+
wagmiConfig?: Config;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const defaultChains: readonly [Chain, ...Chain[]] = [mainnet, polygon, optimism, arbitrum];
|
|
50
|
+
|
|
51
|
+
/** The wagmi config `<KeycloakWalletLogin>` builds when it is given none. */
|
|
52
|
+
export function createKeycloakWalletConfig({
|
|
53
|
+
appName = "Sign in",
|
|
54
|
+
appDescription = "Sign in with your wallet",
|
|
55
|
+
appUrl = (globalThis as { location?: Location }).location?.origin ?? "",
|
|
56
|
+
appIcon,
|
|
57
|
+
chains = defaultChains,
|
|
58
|
+
}: Pick<
|
|
59
|
+
KeycloakWalletLoginProps,
|
|
60
|
+
"appName" | "appDescription" | "appUrl" | "appIcon" | "chains"
|
|
61
|
+
> = {}): Config {
|
|
62
|
+
return createConfig(
|
|
63
|
+
getDefaultConfig({
|
|
64
|
+
appName,
|
|
65
|
+
appDescription,
|
|
66
|
+
appUrl,
|
|
67
|
+
appIcon,
|
|
68
|
+
chains,
|
|
69
|
+
// Aave Account signs through an app.family.co iframe, which a Keycloak
|
|
70
|
+
// page's default CSP (frame-src 'self') blocks.
|
|
71
|
+
enableAaveAccount: false,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const buttonStyle: CSSProperties = {
|
|
77
|
+
display: "flex",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
justifyContent: "center",
|
|
80
|
+
gap: 8,
|
|
81
|
+
width: "100%",
|
|
82
|
+
padding: "10px 14px",
|
|
83
|
+
marginTop: 8,
|
|
84
|
+
fontSize: 14,
|
|
85
|
+
cursor: "pointer",
|
|
86
|
+
border: "1px solid #c4c4c4",
|
|
87
|
+
borderRadius: 4,
|
|
88
|
+
background: "#fff",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wallet and GPG sign-in for a Keycloak login page, on the browser flow: the
|
|
93
|
+
* signed credential posts to the page's authenticator. Carries its own wagmi,
|
|
94
|
+
* query client and ConnectKit modal, so it drops into any theme; a page that is
|
|
95
|
+
* not React 19 mounts it with the IIFE build's `MpoKeycloakWallet.mount`.
|
|
96
|
+
*/
|
|
97
|
+
export function KeycloakWalletLogin(props: KeycloakWalletLoginProps) {
|
|
98
|
+
const {
|
|
99
|
+
resource,
|
|
100
|
+
domain,
|
|
101
|
+
uri,
|
|
102
|
+
target = "ftl",
|
|
103
|
+
whenVisible = true,
|
|
104
|
+
appName,
|
|
105
|
+
appDescription,
|
|
106
|
+
appUrl,
|
|
107
|
+
appIcon,
|
|
108
|
+
chains,
|
|
109
|
+
gpg = true,
|
|
110
|
+
mobile,
|
|
111
|
+
wagmiConfig,
|
|
112
|
+
} = props;
|
|
113
|
+
const page = deriveKeycloakFromLocation();
|
|
114
|
+
const baseUrl = props.baseUrl ?? page?.baseUrl;
|
|
115
|
+
const realm = props.realm ?? page?.realm;
|
|
116
|
+
if (!baseUrl || !realm) {
|
|
117
|
+
throw new Error("KeycloakWalletLogin needs baseUrl and realm off a Keycloak page");
|
|
118
|
+
}
|
|
119
|
+
const action = typeof target === "string" ? undefined : target.action;
|
|
120
|
+
|
|
121
|
+
const kc = useMemo(
|
|
122
|
+
() => keycloakWallet({ baseUrl, realm, resource, domain, uri }),
|
|
123
|
+
[baseUrl, realm, resource, domain, uri],
|
|
124
|
+
);
|
|
125
|
+
const browserTarget = useMemo<BrowserFlowOptions["target"]>(
|
|
126
|
+
() => (action === undefined ? "ftl" : { action }),
|
|
127
|
+
[action],
|
|
128
|
+
);
|
|
129
|
+
const onSign = useMemo(
|
|
130
|
+
() => kc.browserFlow({ target: browserTarget, whenVisible }),
|
|
131
|
+
[kc, browserTarget, whenVisible],
|
|
132
|
+
);
|
|
133
|
+
const config = useMemo(
|
|
134
|
+
() =>
|
|
135
|
+
wagmiConfig ??
|
|
136
|
+
createKeycloakWalletConfig({ appName, appDescription, appUrl, appIcon, chains }),
|
|
137
|
+
[wagmiConfig, appName, appDescription, appUrl, appIcon, chains],
|
|
138
|
+
);
|
|
139
|
+
const connectKitOptions = useMemo(() => (mobile ? { mobile } : undefined), [mobile]);
|
|
140
|
+
const queryClient = useMemo(
|
|
141
|
+
() =>
|
|
142
|
+
new QueryClient({
|
|
143
|
+
defaultOptions: {
|
|
144
|
+
queries: { refetchOnWindowFocus: false, refetchOnReconnect: false, staleTime: Infinity },
|
|
145
|
+
},
|
|
146
|
+
}),
|
|
147
|
+
[],
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<WagmiProvider config={config}>
|
|
152
|
+
<QueryClientProvider client={queryClient}>
|
|
153
|
+
<GpgProvider {...kc.gpgConfig(onSign)} enabled={gpg}>
|
|
154
|
+
<SIWEProvider {...kc.siweConfig(onSign)}>
|
|
155
|
+
<ConnectKitProvider options={connectKitOptions}>
|
|
156
|
+
<LoginButtons {...props} />
|
|
157
|
+
</ConnectKitProvider>
|
|
158
|
+
</SIWEProvider>
|
|
159
|
+
</GpgProvider>
|
|
160
|
+
</QueryClientProvider>
|
|
161
|
+
</WagmiProvider>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function LoginButtons({
|
|
166
|
+
gpg = true,
|
|
167
|
+
walletLabel = "Sign in with Wallet",
|
|
168
|
+
gpgLabel = "Sign in with GPG",
|
|
169
|
+
className,
|
|
170
|
+
buttonClassName,
|
|
171
|
+
}: KeycloakWalletLoginProps) {
|
|
172
|
+
const { login, status } = useWalletLogin();
|
|
173
|
+
const { openGpg } = useWeb3Modal();
|
|
174
|
+
const style = buttonClassName ? undefined : buttonStyle;
|
|
175
|
+
const busy = status === StatusState.LOADING || status === StatusState.SUCCESS;
|
|
176
|
+
return (
|
|
177
|
+
<div className={className ?? "mpo-keycloak-wallet-login"}>
|
|
178
|
+
<button
|
|
179
|
+
type="button"
|
|
180
|
+
id="mpo-wallet-login"
|
|
181
|
+
className={buttonClassName}
|
|
182
|
+
style={style}
|
|
183
|
+
disabled={busy}
|
|
184
|
+
onClick={(e) => {
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
void login();
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
<WalletIcon />
|
|
190
|
+
<span>{busy ? "Signing…" : walletLabel}</span>
|
|
191
|
+
</button>
|
|
192
|
+
{gpg && (
|
|
193
|
+
<button
|
|
194
|
+
type="button"
|
|
195
|
+
id="mpo-gpg-login"
|
|
196
|
+
className={buttonClassName}
|
|
197
|
+
style={style}
|
|
198
|
+
onClick={(e) => {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
openGpg();
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
<GpgIcon />
|
|
204
|
+
<span>{gpgLabel}</span>
|
|
205
|
+
</button>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function WalletIcon() {
|
|
212
|
+
return (
|
|
213
|
+
<svg
|
|
214
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
215
|
+
width="18"
|
|
216
|
+
height="18"
|
|
217
|
+
viewBox="0 0 24 24"
|
|
218
|
+
fill="none"
|
|
219
|
+
stroke="currentColor"
|
|
220
|
+
strokeWidth="2"
|
|
221
|
+
strokeLinecap="round"
|
|
222
|
+
strokeLinejoin="round"
|
|
223
|
+
aria-hidden="true"
|
|
224
|
+
>
|
|
225
|
+
<path d="M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0-2 2v0a2 2 0 0 0 2 2h13a1 1 0 0 1 1 1v4" />
|
|
226
|
+
<path d="M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4" />
|
|
227
|
+
<rect width="7" height="5" x="14" y="10" rx="1" />
|
|
228
|
+
</svg>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function GpgIcon() {
|
|
233
|
+
return (
|
|
234
|
+
<svg
|
|
235
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
236
|
+
width="18"
|
|
237
|
+
height="18"
|
|
238
|
+
viewBox="0 0 24 24"
|
|
239
|
+
fill="none"
|
|
240
|
+
stroke="currentColor"
|
|
241
|
+
strokeWidth="2"
|
|
242
|
+
strokeLinecap="round"
|
|
243
|
+
strokeLinejoin="round"
|
|
244
|
+
aria-hidden="true"
|
|
245
|
+
>
|
|
246
|
+
<circle cx="7.5" cy="15.5" r="5.5" />
|
|
247
|
+
<path d="m21 2-9.6 9.6" />
|
|
248
|
+
<path d="m15.5 7.5 3 3L22 7l-3-3" />
|
|
249
|
+
</svg>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { browserFlow } from "./browserFlow";
|
|
4
|
+
|
|
5
|
+
let visibility: DocumentVisibilityState = "visible";
|
|
6
|
+
const credentials = { username: "0xsig", password: "bWVzc2FnZQ==", message: "message" };
|
|
7
|
+
|
|
8
|
+
function setVisibility(next: DocumentVisibilityState) {
|
|
9
|
+
visibility = next;
|
|
10
|
+
document.dispatchEvent(new Event("visibilitychange"));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function ftlPage() {
|
|
14
|
+
document.body.innerHTML = `
|
|
15
|
+
<form id="kc-form-login" action="https://kc.example.com/realms/main/login-actions/authenticate" method="post">
|
|
16
|
+
<input id="username" name="username" />
|
|
17
|
+
<input id="password" name="password" type="password" />
|
|
18
|
+
</form>`;
|
|
19
|
+
return document.getElementById("kc-form-login") as HTMLFormElement;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("browserFlow", () => {
|
|
23
|
+
let submitted: HTMLFormElement[];
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
visibility = "visible";
|
|
27
|
+
Object.defineProperty(document, "visibilityState", {
|
|
28
|
+
configurable: true,
|
|
29
|
+
get: () => visibility,
|
|
30
|
+
});
|
|
31
|
+
submitted = [];
|
|
32
|
+
vi.spyOn(HTMLFormElement.prototype, "submit").mockImplementation(
|
|
33
|
+
function (this: HTMLFormElement) {
|
|
34
|
+
submitted.push(this);
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
vi.restoreAllMocks();
|
|
41
|
+
document.body.innerHTML = "";
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("ftl: writes the credential into the login form and submits it", async () => {
|
|
45
|
+
const form = ftlPage();
|
|
46
|
+
await expect(browserFlow()(credentials)).resolves.toBe(true);
|
|
47
|
+
expect(submitted).toEqual([form]);
|
|
48
|
+
expect((document.getElementById("username") as HTMLInputElement).value).toBe("0xsig");
|
|
49
|
+
expect((document.getElementById("password") as HTMLInputElement).value).toBe("bWVzc2FnZQ==");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("ftl: resolves false when the page has no login form", async () => {
|
|
53
|
+
await expect(browserFlow()(credentials)).resolves.toBe(false);
|
|
54
|
+
expect(submitted).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("action: posts through a hidden form of its own and leaves the page's fields alone", async () => {
|
|
58
|
+
ftlPage();
|
|
59
|
+
const action = "https://kc.example.com/realms/main/login-actions/authenticate?session_code=x";
|
|
60
|
+
await browserFlow({ target: { action } })(credentials);
|
|
61
|
+
expect(submitted).toHaveLength(1);
|
|
62
|
+
const [form] = submitted;
|
|
63
|
+
expect(form.id).toBe("");
|
|
64
|
+
expect(form.action).toBe(action);
|
|
65
|
+
expect(form.method).toBe("post");
|
|
66
|
+
expect(form.style.display).toBe("none");
|
|
67
|
+
expect(Object.fromEntries(new FormData(form))).toEqual({
|
|
68
|
+
username: "0xsig",
|
|
69
|
+
password: "bWVzc2FnZQ==",
|
|
70
|
+
});
|
|
71
|
+
expect((document.getElementById("username") as HTMLInputElement).value).toBe("");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("whenVisible: a signature that lands while the page is hidden waits until it shows", async () => {
|
|
75
|
+
ftlPage();
|
|
76
|
+
setVisibility("hidden");
|
|
77
|
+
let settled = false;
|
|
78
|
+
const done = browserFlow()(credentials).then((result) => {
|
|
79
|
+
settled = true;
|
|
80
|
+
return result;
|
|
81
|
+
});
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
83
|
+
expect(settled).toBe(false);
|
|
84
|
+
expect(submitted).toEqual([]);
|
|
85
|
+
setVisibility("hidden");
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
87
|
+
expect(submitted).toEqual([]);
|
|
88
|
+
setVisibility("visible");
|
|
89
|
+
await expect(done).resolves.toBe(true);
|
|
90
|
+
expect(submitted).toHaveLength(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("whenVisible false submits at once, even hidden", async () => {
|
|
94
|
+
ftlPage();
|
|
95
|
+
setVisibility("hidden");
|
|
96
|
+
await expect(browserFlow({ whenVisible: false })(credentials)).resolves.toBe(true);
|
|
97
|
+
expect(submitted).toHaveLength(1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("submits once: a late second credential (a re-driven signature) does not post again", async () => {
|
|
101
|
+
ftlPage();
|
|
102
|
+
const onSign = browserFlow();
|
|
103
|
+
await onSign(credentials);
|
|
104
|
+
await expect(onSign({ ...credentials, username: "0xother" })).resolves.toBe(true);
|
|
105
|
+
expect(submitted).toHaveLength(1);
|
|
106
|
+
expect((document.getElementById("username") as HTMLInputElement).value).toBe("0xsig");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { OnSign } from "./types";
|
|
2
|
+
|
|
3
|
+
export interface BrowserFlowOptions {
|
|
4
|
+
/**
|
|
5
|
+
* `ftl`: write the credential into the login page's own `#kc-form-login`
|
|
6
|
+
* (`#username` / `#password`) and submit it. `{ action }`: post it through a
|
|
7
|
+
* hidden form of its own to that login-action URL, leaving the page's
|
|
8
|
+
* visible fields alone.
|
|
9
|
+
*/
|
|
10
|
+
target?: "ftl" | { action: string };
|
|
11
|
+
/**
|
|
12
|
+
* Hold the submit until the page is visible. Chrome on Android drops the
|
|
13
|
+
* redirect back to the app while the tab is in the background, so a
|
|
14
|
+
* signature that lands before the user switches back waits. Default true.
|
|
15
|
+
*/
|
|
16
|
+
whenVisible?: boolean;
|
|
17
|
+
document?: Document;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function whenPageVisible(doc: Document): Promise<void> {
|
|
21
|
+
if (doc.visibilityState !== "hidden") return Promise.resolve();
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const onChange = () => {
|
|
24
|
+
if (doc.visibilityState === "hidden") return;
|
|
25
|
+
doc.removeEventListener("visibilitychange", onChange);
|
|
26
|
+
resolve();
|
|
27
|
+
};
|
|
28
|
+
doc.addEventListener("visibilitychange", onChange);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function ftlForm(doc: Document, username: string, password: string): HTMLFormElement | null {
|
|
33
|
+
const form = doc.getElementById("kc-form-login") as HTMLFormElement | null;
|
|
34
|
+
const user = doc.getElementById("username") as HTMLInputElement | null;
|
|
35
|
+
const pass = doc.getElementById("password") as HTMLInputElement | null;
|
|
36
|
+
if (!form || !user || !pass) return null;
|
|
37
|
+
user.value = username;
|
|
38
|
+
pass.value = password;
|
|
39
|
+
return form;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function actionForm(doc: Document, action: string, username: string, password: string) {
|
|
43
|
+
const form = doc.createElement("form");
|
|
44
|
+
form.method = "post";
|
|
45
|
+
form.action = action;
|
|
46
|
+
form.style.display = "none";
|
|
47
|
+
for (const [name, value] of [
|
|
48
|
+
["username", username],
|
|
49
|
+
["password", password],
|
|
50
|
+
]) {
|
|
51
|
+
const input = doc.createElement("input");
|
|
52
|
+
input.type = "hidden";
|
|
53
|
+
input.name = name;
|
|
54
|
+
input.value = value;
|
|
55
|
+
form.appendChild(input);
|
|
56
|
+
}
|
|
57
|
+
doc.body.appendChild(form);
|
|
58
|
+
return form;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* `onSign` for a Keycloak login page: posts the signed credential to the
|
|
63
|
+
* browser flow's authenticator, which verifies it and finishes the login. Pass
|
|
64
|
+
* it to `<SIWEProvider onSign>` or `<GpgProvider onSign>`. Resolves false when
|
|
65
|
+
* the `ftl` form is missing; a second credential after a submit is ignored.
|
|
66
|
+
*/
|
|
67
|
+
export function browserFlow(opts: BrowserFlowOptions = {}): OnSign {
|
|
68
|
+
const { target = "ftl", whenVisible = true } = opts;
|
|
69
|
+
let submitted = false;
|
|
70
|
+
return async ({ username, password }) => {
|
|
71
|
+
const doc = opts.document ?? (globalThis as { document?: Document }).document;
|
|
72
|
+
if (!doc) throw new Error("browserFlow needs a DOM");
|
|
73
|
+
if (whenVisible) await whenPageVisible(doc);
|
|
74
|
+
if (submitted) return true;
|
|
75
|
+
const form =
|
|
76
|
+
target === "ftl"
|
|
77
|
+
? ftlForm(doc, username, password)
|
|
78
|
+
: actionForm(doc, target.action, username, password);
|
|
79
|
+
if (!form) return false;
|
|
80
|
+
submitted = true;
|
|
81
|
+
form.submit();
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type NavigatorLike = {
|
|
2
|
+
userAgent?: string;
|
|
3
|
+
userAgentData?: { mobile?: boolean };
|
|
4
|
+
userActivation?: { isActive: boolean };
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const nav = () => (globalThis as { navigator?: NavigatorLike }).navigator;
|
|
8
|
+
|
|
9
|
+
export function isMobileBrowser(navigator: NavigatorLike | undefined = nav()): boolean {
|
|
10
|
+
if (typeof navigator?.userAgentData?.mobile === "boolean") return navigator.userAgentData.mobile;
|
|
11
|
+
return /Android|iPhone|iPad|iPod/i.test(navigator?.userAgent ?? "");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Whether the page holds a user gesture; true where the browser does not report it. */
|
|
15
|
+
export function hasUserActivation(navigator: NavigatorLike | undefined = nav()): boolean {
|
|
16
|
+
return navigator?.userActivation ? navigator.userActivation.isActive : true;
|
|
17
|
+
}
|