@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,216 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildWalletMessage } from "../message";
|
|
3
|
+
import {
|
|
4
|
+
deriveKeycloakFromLocation,
|
|
5
|
+
keycloakWallet,
|
|
6
|
+
pkceChallenge,
|
|
7
|
+
toCredentials,
|
|
8
|
+
} from "./keycloakWallet";
|
|
9
|
+
import { fakeKeycloak } from "../../tests/fakeKeycloak";
|
|
10
|
+
|
|
11
|
+
function nonceServer() {
|
|
12
|
+
let issued = 0;
|
|
13
|
+
return fakeKeycloak({
|
|
14
|
+
"/nonce": () => {
|
|
15
|
+
issued += 1;
|
|
16
|
+
return new Response(`nonce-${issued}\n`, {
|
|
17
|
+
headers: { "set-cookie": `KEYCLOAK_SESSION_SIWW=key-${issued}; Path=/; HttpOnly` },
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("keycloakWallet", () => {
|
|
24
|
+
it("resolves the realm, resource and token URLs; siww by default, wallet on request", () => {
|
|
25
|
+
const siww = keycloakWallet({ baseUrl: "https://kc.example.com/auth/", realm: "main" });
|
|
26
|
+
expect(siww.resourceUrl).toBe("https://kc.example.com/auth/realms/main/siww");
|
|
27
|
+
expect(siww.tokenUrl).toBe(
|
|
28
|
+
"https://kc.example.com/auth/realms/main/protocol/openid-connect/token",
|
|
29
|
+
);
|
|
30
|
+
const wallet = keycloakWallet({
|
|
31
|
+
baseUrl: "https://kc.example.com",
|
|
32
|
+
realm: "main",
|
|
33
|
+
resource: "wallet",
|
|
34
|
+
});
|
|
35
|
+
expect(wallet.resourceUrl).toBe("https://kc.example.com/realms/main/wallet");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("browserLoginUrl", () => {
|
|
39
|
+
const kc = keycloakWallet({ baseUrl: "https://kc.example.com/auth", realm: "main" });
|
|
40
|
+
|
|
41
|
+
it("is the realm's authorization-code login with state and PKCE S256, for the profile-incomplete fallback", () => {
|
|
42
|
+
const login = kc.browserLoginUrl({
|
|
43
|
+
clientId: "wallet-onboarding",
|
|
44
|
+
redirectUri: "quickcam://auth/callback",
|
|
45
|
+
});
|
|
46
|
+
const url = new URL(login.url);
|
|
47
|
+
expect(url.origin + url.pathname).toBe(
|
|
48
|
+
"https://kc.example.com/auth/realms/main/protocol/openid-connect/auth",
|
|
49
|
+
);
|
|
50
|
+
expect(Object.fromEntries(url.searchParams)).toEqual({
|
|
51
|
+
client_id: "wallet-onboarding",
|
|
52
|
+
redirect_uri: "quickcam://auth/callback",
|
|
53
|
+
response_type: "code",
|
|
54
|
+
scope: "openid",
|
|
55
|
+
state: login.state,
|
|
56
|
+
code_challenge: pkceChallenge(login.codeVerifier),
|
|
57
|
+
code_challenge_method: "S256",
|
|
58
|
+
});
|
|
59
|
+
expect(login.codeVerifier).toMatch(/^[A-Za-z0-9_-]{43}$/);
|
|
60
|
+
expect(login.state).toMatch(/^[A-Za-z0-9_-]{22}$/);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("draws a fresh state and verifier every call", () => {
|
|
64
|
+
const a = kc.browserLoginUrl({ clientId: "app", redirectUri: "app://auth" });
|
|
65
|
+
const b = kc.browserLoginUrl({ clientId: "app", redirectUri: "app://auth" });
|
|
66
|
+
expect(a.state).not.toBe(b.state);
|
|
67
|
+
expect(a.codeVerifier).not.toBe(b.codeVerifier);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("joins a scope list", () => {
|
|
71
|
+
const { url } = kc.browserLoginUrl({
|
|
72
|
+
clientId: "app",
|
|
73
|
+
redirectUri: "https://app.example.com/cb",
|
|
74
|
+
scope: ["openid", "email"],
|
|
75
|
+
});
|
|
76
|
+
expect(new URL(url).searchParams.get("scope")).toBe("openid email");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("keeps a redirectUri carrying &client_id= encoded inside redirect_uri", () => {
|
|
80
|
+
const redirectUri = "https://app.example.com/cb?x=1&client_id=evil#frag";
|
|
81
|
+
const { url } = kc.browserLoginUrl({ clientId: "app", redirectUri });
|
|
82
|
+
const params = new URL(url).searchParams;
|
|
83
|
+
expect(params.getAll("client_id")).toEqual(["app"]);
|
|
84
|
+
expect(params.get("redirect_uri")).toBe(redirectUri);
|
|
85
|
+
expect(url).toContain(
|
|
86
|
+
"redirect_uri=https%3A%2F%2Fapp.example.com%2Fcb%3Fx%3D1%26client_id%3Devil%23frag",
|
|
87
|
+
);
|
|
88
|
+
expect(url).not.toContain("&client_id=evil");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("pkceChallenge matches RFC 7636 appendix B", () => {
|
|
92
|
+
expect(pkceChallenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")).toBe(
|
|
93
|
+
"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("nonce memo", () => {
|
|
99
|
+
it("shares one nonce between callers until reset, since each /nonce replaces the last", async () => {
|
|
100
|
+
const server = nonceServer();
|
|
101
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main", fetch: server.fetch });
|
|
102
|
+
const [a, b] = await Promise.all([kc.getNonce(), kc.getNonce()]);
|
|
103
|
+
expect([a, b, await kc.getNonce()]).toEqual(["nonce-1", "nonce-1", "nonce-1"]);
|
|
104
|
+
expect(server.requests).toHaveLength(1);
|
|
105
|
+
expect(server.requests[0].url).toBe("https://kc/realms/main/siww/nonce");
|
|
106
|
+
expect(server.requests[0].credentials).toBe("include");
|
|
107
|
+
kc.resetNonce();
|
|
108
|
+
expect(await kc.getNonce()).toBe("nonce-2");
|
|
109
|
+
expect(server.requests).toHaveLength(2);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("a failed fetch is not memoized", async () => {
|
|
113
|
+
let fail = true;
|
|
114
|
+
const server = fakeKeycloak({
|
|
115
|
+
"/nonce": () => (fail ? new Response("", { status: 503 }) : new Response("abc")),
|
|
116
|
+
});
|
|
117
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main", fetch: server.fetch });
|
|
118
|
+
await expect(kc.getNonce()).rejects.toThrow(/503/);
|
|
119
|
+
fail = false;
|
|
120
|
+
await expect(kc.getNonce()).resolves.toBe("abc");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("tells nonce-reset subscribers, through both provider configs, until they unsubscribe", async () => {
|
|
124
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main" });
|
|
125
|
+
const onSign = async () => true;
|
|
126
|
+
const resets: string[] = [];
|
|
127
|
+
const offSiwe = kc.siweConfig(onSign).subscribeNonceReset!(() => resets.push("siwe"));
|
|
128
|
+
const offGpg = kc.gpgConfig(onSign).subscribeNonceReset(() => resets.push("gpg"));
|
|
129
|
+
kc.resetNonce();
|
|
130
|
+
expect(resets).toEqual(["siwe", "gpg"]);
|
|
131
|
+
offSiwe();
|
|
132
|
+
kc.resetNonce();
|
|
133
|
+
expect(resets).toEqual(["siwe", "gpg", "gpg"]);
|
|
134
|
+
offGpg();
|
|
135
|
+
kc.resetNonce();
|
|
136
|
+
expect(resets).toHaveLength(3);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("rejects an empty nonce", async () => {
|
|
140
|
+
const server = fakeKeycloak({ "/nonce": () => new Response(" ") });
|
|
141
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main", fetch: server.fetch });
|
|
142
|
+
await expect(kc.getNonce()).rejects.toThrow(/empty nonce/);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("replays the nonce session cookie where fetch exposes Set-Cookie (native, node)", async () => {
|
|
147
|
+
const server = nonceServer();
|
|
148
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main", fetch: server.fetch });
|
|
149
|
+
await kc.getNonce();
|
|
150
|
+
await kc.request(kc.tokenUrl, { method: "POST" });
|
|
151
|
+
expect(server.requests[1].headers.get("cookie")).toBe("KEYCLOAK_SESSION_SIWW=key-1");
|
|
152
|
+
kc.resetNonce();
|
|
153
|
+
await kc.getNonce();
|
|
154
|
+
await kc.request(kc.tokenUrl, { method: "POST" });
|
|
155
|
+
expect(server.requests[3].headers.get("cookie")).toBe("KEYCLOAK_SESSION_SIWW=key-2");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("builds messages with its explicit domain for SIWE and GPG", () => {
|
|
159
|
+
const kc = keycloakWallet({
|
|
160
|
+
baseUrl: "https://kc",
|
|
161
|
+
realm: "main",
|
|
162
|
+
domain: "app.example.com",
|
|
163
|
+
uri: "https://app.example.com",
|
|
164
|
+
});
|
|
165
|
+
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
|
166
|
+
const siwe = kc.siweConfig(async () => true);
|
|
167
|
+
const message = siwe.createMessage({ nonce: "n1", address, chainId: 1 }) as string;
|
|
168
|
+
const issuedAt = message.match(/Issued At: (.*)$/)?.[1];
|
|
169
|
+
expect(message).toBe(
|
|
170
|
+
buildWalletMessage("eth", {
|
|
171
|
+
nonce: "n1",
|
|
172
|
+
address,
|
|
173
|
+
domain: "app.example.com",
|
|
174
|
+
uri: "https://app.example.com",
|
|
175
|
+
issuedAt,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
const gpg = kc.gpgMessage({ nonce: "n2" });
|
|
179
|
+
expect(gpg.split("\n")[0]).toBe("app.example.com wants you to sign in with your GPG key.");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("siweConfig and gpgConfig hand the providers the shared nonce and the given onSign", async () => {
|
|
183
|
+
const server = nonceServer();
|
|
184
|
+
const kc = keycloakWallet({ baseUrl: "https://kc", realm: "main", fetch: server.fetch });
|
|
185
|
+
const onSign = async () => true;
|
|
186
|
+
const siwe = kc.siweConfig(onSign);
|
|
187
|
+
const gpg = kc.gpgConfig(onSign);
|
|
188
|
+
expect(siwe.onSign).toBe(onSign);
|
|
189
|
+
expect(gpg.onSign).toBe(onSign);
|
|
190
|
+
expect(await siwe.getNonce()).toBe(await gpg.getNonce());
|
|
191
|
+
expect(server.requests).toHaveLength(1);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("toCredentials", () => {
|
|
196
|
+
it("base64s the UTF-8 message without btoa", () => {
|
|
197
|
+
const message = "héllo ✓";
|
|
198
|
+
const creds = toCredentials({ signature: "0xsig", message });
|
|
199
|
+
expect(creds.username).toBe("0xsig");
|
|
200
|
+
expect(Buffer.from(creds.password, "base64").toString("utf8")).toBe(message);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("deriveKeycloakFromLocation", () => {
|
|
205
|
+
it("reads base and realm off a Keycloak page", () => {
|
|
206
|
+
expect(
|
|
207
|
+
deriveKeycloakFromLocation({
|
|
208
|
+
origin: "https://account.example.com",
|
|
209
|
+
pathname: "/auth/realms/main/login-actions/authenticate",
|
|
210
|
+
}),
|
|
211
|
+
).toEqual({ baseUrl: "https://account.example.com/auth", realm: "main" });
|
|
212
|
+
expect(
|
|
213
|
+
deriveKeycloakFromLocation({ origin: "https://example.com", pathname: "/dashboard" }),
|
|
214
|
+
).toBeUndefined();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { sha256, type Address } from "viem";
|
|
2
|
+
import { bytesToBase64 } from "../bridge/base64";
|
|
3
|
+
import { createWalletMessage } from "../connectkit/connectors/createWalletMessage";
|
|
4
|
+
import type { SIWEConfig } from "../connectkit/siwe/SIWEContext";
|
|
5
|
+
import { buildWalletMessage } from "../message";
|
|
6
|
+
import { browserFlow, type BrowserFlowOptions } from "./browserFlow";
|
|
7
|
+
import { createDirectGrant, type DirectGrant, type DirectGrantOptions } from "./directGrant";
|
|
8
|
+
import {
|
|
9
|
+
createWalletRegistration,
|
|
10
|
+
type RegisterWalletOptions,
|
|
11
|
+
type WalletRegistration,
|
|
12
|
+
} from "./registerWallet";
|
|
13
|
+
import { KeycloakWalletError, type KeycloakWalletResource, type OnSign } from "./types";
|
|
14
|
+
|
|
15
|
+
export interface KeycloakWalletOptions {
|
|
16
|
+
/** Keycloak base URL, e.g. `https://account.example.com` or `http://localhost:8080/auth`. */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
realm: string;
|
|
19
|
+
/** Defaults to `siww` (SHC Keycloak); mpo's own SPI mounts `wallet`. */
|
|
20
|
+
resource?: KeycloakWalletResource;
|
|
21
|
+
/**
|
|
22
|
+
* Domain written into every sign-in message. Defaults to the page host. A
|
|
23
|
+
* WebView or native caller passes the hosted page's host the realm allows.
|
|
24
|
+
*/
|
|
25
|
+
domain?: string;
|
|
26
|
+
/** URI written into every sign-in message. Defaults to the page origin or `https://<domain>`. */
|
|
27
|
+
uri?: string;
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface BrowserLoginUrlOptions {
|
|
32
|
+
/**
|
|
33
|
+
* A public client with `standardFlowEnabled` and `redirectUri` registered
|
|
34
|
+
* exactly, no wildcard. A direct-grant client such as mpo's `siwe-public`
|
|
35
|
+
* or `siws-public` (`standardFlowEnabled: false`) is refused; mpo's realm
|
|
36
|
+
* carries `wallet-onboarding` for this.
|
|
37
|
+
*/
|
|
38
|
+
clientId: string;
|
|
39
|
+
/** Where Keycloak sends the browser back: the app's deep link or page. */
|
|
40
|
+
redirectUri: string;
|
|
41
|
+
/** Default `openid`. */
|
|
42
|
+
scope?: string | string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface BrowserLogin {
|
|
46
|
+
url: string;
|
|
47
|
+
/** Keep it, and accept the redirect back only when its `state` matches. */
|
|
48
|
+
state: string;
|
|
49
|
+
/** The PKCE S256 verifier behind the URL's `code_challenge`, for the code exchange. */
|
|
50
|
+
codeVerifier: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface KeycloakWallet {
|
|
54
|
+
readonly baseUrl: string;
|
|
55
|
+
readonly realm: string;
|
|
56
|
+
readonly resource: KeycloakWalletResource;
|
|
57
|
+
readonly realmUrl: string;
|
|
58
|
+
readonly resourceUrl: string;
|
|
59
|
+
readonly tokenUrl: string;
|
|
60
|
+
/**
|
|
61
|
+
* A server nonce, shared by every caller until `resetNonce()`. Each `/nonce`
|
|
62
|
+
* call replaces the previous nonce server side (and rotates its cookie), so
|
|
63
|
+
* two flows fetching their own would leave one signing a dead nonce.
|
|
64
|
+
*/
|
|
65
|
+
getNonce(): Promise<string>;
|
|
66
|
+
/** Drop the shared nonce (the server consumed or rotated it) and tell every subscriber. */
|
|
67
|
+
resetNonce(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Call `listener` on every `resetNonce()`. SIWEProvider and GpgProvider
|
|
70
|
+
* subscribe through the configs below and refetch their cached copy, so a
|
|
71
|
+
* second sign-in never signs a nonce the server has already dropped.
|
|
72
|
+
*/
|
|
73
|
+
subscribeNonceReset(listener: () => void): () => void;
|
|
74
|
+
/** `SIWEConfig.createMessage`, with this wallet's domain and uri. */
|
|
75
|
+
createMessage(args: { nonce: string; address: string; chainId: number }): string;
|
|
76
|
+
/** `GpgConfig.buildMessage`, with this wallet's domain and uri. */
|
|
77
|
+
gpgMessage(args: { nonce: string }): string;
|
|
78
|
+
/** Props for `<SIWEProvider>` that sign through `onSign` (a browser flow, direct grant or registration). */
|
|
79
|
+
siweConfig(onSign: OnSign): SIWEConfig;
|
|
80
|
+
/** Props for `<GpgProvider>` that sign through `onSign`. */
|
|
81
|
+
gpgConfig(onSign: OnSign): {
|
|
82
|
+
getNonce: () => Promise<string>;
|
|
83
|
+
buildMessage: (args: { nonce: string }) => string;
|
|
84
|
+
onSign: OnSign;
|
|
85
|
+
subscribeNonceReset: (listener: () => void) => () => void;
|
|
86
|
+
};
|
|
87
|
+
browserFlow(opts?: BrowserFlowOptions): OnSign;
|
|
88
|
+
directGrant(opts: DirectGrantOptions): DirectGrant;
|
|
89
|
+
/**
|
|
90
|
+
* The realm's authorization-code login, with a fresh `state` and PKCE S256
|
|
91
|
+
* pair. Open `url` in the system browser or a WebView when a direct grant
|
|
92
|
+
* rejects with `ProfileIncompleteError`: Keycloak's own login runs the
|
|
93
|
+
* pending required actions (the profile form), and the next direct grant
|
|
94
|
+
* succeeds.
|
|
95
|
+
*/
|
|
96
|
+
browserLoginUrl(opts: BrowserLoginUrlOptions): BrowserLogin;
|
|
97
|
+
registerWallet(opts: RegisterWalletOptions): WalletRegistration;
|
|
98
|
+
/** `fetch` against Keycloak carrying the nonce session cookie. */
|
|
99
|
+
request(url: string, init?: RequestInit): Promise<Response>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const sessionCookie = /(KEYCLOAK_SESSION_[A-Z]+)=([^;,\s]+)/;
|
|
103
|
+
|
|
104
|
+
function readSessionCookie(res: Response): string | undefined {
|
|
105
|
+
const headers = res.headers as Headers & { getSetCookie?: () => string[] };
|
|
106
|
+
const values = headers.getSetCookie?.() ?? [headers.get("set-cookie") ?? ""];
|
|
107
|
+
for (const value of values) {
|
|
108
|
+
const match = value.match(sessionCookie);
|
|
109
|
+
if (match) return `${match[1]}=${match[2]}`;
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const base64Url = (bytes: Uint8Array) =>
|
|
115
|
+
bytesToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
116
|
+
|
|
117
|
+
function randomBase64Url(size: number): string {
|
|
118
|
+
const crypto = globalThis.crypto;
|
|
119
|
+
if (!crypto?.getRandomValues) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"browserLoginUrl needs crypto.getRandomValues (react-native-get-random-values on Hermes)",
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return base64Url(crypto.getRandomValues(new Uint8Array(size)));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** RFC 7636 S256: base64url(SHA-256(ASCII(verifier))). */
|
|
128
|
+
export function pkceChallenge(codeVerifier: string): string {
|
|
129
|
+
return base64Url(sha256(new TextEncoder().encode(codeVerifier), "bytes"));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** `username`/`password` for a signature over `message`, base64 done without btoa so it runs on Hermes. */
|
|
133
|
+
export function toCredentials(args: { signature: string; message: string }) {
|
|
134
|
+
return {
|
|
135
|
+
username: args.signature,
|
|
136
|
+
password: bytesToBase64(new TextEncoder().encode(args.message)),
|
|
137
|
+
message: args.message,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function keycloakWallet(options: KeycloakWalletOptions): KeycloakWallet {
|
|
142
|
+
const baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
143
|
+
const resource = options.resource ?? "siww";
|
|
144
|
+
const realmUrl = `${baseUrl}/realms/${encodeURIComponent(options.realm)}`;
|
|
145
|
+
const resourceUrl = `${realmUrl}/${resource}`;
|
|
146
|
+
const tokenUrl = `${realmUrl}/protocol/openid-connect/token`;
|
|
147
|
+
const fetchImpl = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
148
|
+
let cookie: string | undefined;
|
|
149
|
+
let nonce: Promise<string> | undefined;
|
|
150
|
+
const nonceListeners = new Set<() => void>();
|
|
151
|
+
|
|
152
|
+
const request = (url: string, init: RequestInit = {}) => {
|
|
153
|
+
const headers = new Headers(init.headers);
|
|
154
|
+
if (cookie && !headers.has("cookie")) headers.set("cookie", cookie);
|
|
155
|
+
return fetchImpl(url, { credentials: "include", ...init, headers });
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const fetchNonce = async () => {
|
|
159
|
+
const res = await request(`${resourceUrl}/nonce`);
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
throw new KeycloakWalletError(`Keycloak nonce request failed (${res.status})`, res.status);
|
|
162
|
+
}
|
|
163
|
+
cookie = readSessionCookie(res) ?? cookie;
|
|
164
|
+
const value = (await res.text()).trim();
|
|
165
|
+
if (!value) throw new KeycloakWalletError("Keycloak returned an empty nonce", res.status);
|
|
166
|
+
return value;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const kc: KeycloakWallet = {
|
|
170
|
+
baseUrl,
|
|
171
|
+
realm: options.realm,
|
|
172
|
+
resource,
|
|
173
|
+
realmUrl,
|
|
174
|
+
resourceUrl,
|
|
175
|
+
tokenUrl,
|
|
176
|
+
request,
|
|
177
|
+
getNonce() {
|
|
178
|
+
if (!nonce) {
|
|
179
|
+
const pending = fetchNonce();
|
|
180
|
+
nonce = pending;
|
|
181
|
+
pending.catch(() => {
|
|
182
|
+
if (nonce === pending) nonce = undefined;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return nonce;
|
|
186
|
+
},
|
|
187
|
+
resetNonce() {
|
|
188
|
+
nonce = undefined;
|
|
189
|
+
for (const listener of nonceListeners) listener();
|
|
190
|
+
},
|
|
191
|
+
subscribeNonceReset(listener) {
|
|
192
|
+
nonceListeners.add(listener);
|
|
193
|
+
return () => {
|
|
194
|
+
nonceListeners.delete(listener);
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
createMessage({ nonce: value, address, chainId }) {
|
|
198
|
+
return createWalletMessage({
|
|
199
|
+
address,
|
|
200
|
+
chainId,
|
|
201
|
+
nonce: value,
|
|
202
|
+
domain: options.domain,
|
|
203
|
+
uri: options.uri,
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
gpgMessage({ nonce: value }) {
|
|
207
|
+
return buildWalletMessage("gpg", { nonce: value, domain: options.domain, uri: options.uri });
|
|
208
|
+
},
|
|
209
|
+
siweConfig(onSign) {
|
|
210
|
+
return {
|
|
211
|
+
getNonce: () => kc.getNonce(),
|
|
212
|
+
createMessage: ({
|
|
213
|
+
nonce: value,
|
|
214
|
+
address,
|
|
215
|
+
chainId,
|
|
216
|
+
}: {
|
|
217
|
+
nonce: string;
|
|
218
|
+
address: Address;
|
|
219
|
+
chainId: number;
|
|
220
|
+
}) => kc.createMessage({ nonce: value, address, chainId }),
|
|
221
|
+
onSign,
|
|
222
|
+
subscribeNonceReset: kc.subscribeNonceReset,
|
|
223
|
+
verifyMessage: async () => true,
|
|
224
|
+
getSession: async () => null,
|
|
225
|
+
signOut: async () => true,
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
gpgConfig(onSign) {
|
|
229
|
+
return {
|
|
230
|
+
getNonce: () => kc.getNonce(),
|
|
231
|
+
buildMessage: (args) => kc.gpgMessage(args),
|
|
232
|
+
onSign,
|
|
233
|
+
subscribeNonceReset: kc.subscribeNonceReset,
|
|
234
|
+
};
|
|
235
|
+
},
|
|
236
|
+
browserFlow(opts) {
|
|
237
|
+
return browserFlow(opts);
|
|
238
|
+
},
|
|
239
|
+
directGrant(opts) {
|
|
240
|
+
return createDirectGrant(kc, opts);
|
|
241
|
+
},
|
|
242
|
+
browserLoginUrl({ clientId, redirectUri, scope }) {
|
|
243
|
+
const state = randomBase64Url(16);
|
|
244
|
+
const codeVerifier = randomBase64Url(32);
|
|
245
|
+
const params = new URLSearchParams({
|
|
246
|
+
client_id: clientId,
|
|
247
|
+
redirect_uri: redirectUri,
|
|
248
|
+
response_type: "code",
|
|
249
|
+
scope: Array.isArray(scope) ? scope.join(" ") : (scope ?? "openid"),
|
|
250
|
+
state,
|
|
251
|
+
code_challenge: pkceChallenge(codeVerifier),
|
|
252
|
+
code_challenge_method: "S256",
|
|
253
|
+
});
|
|
254
|
+
return { url: `${realmUrl}/protocol/openid-connect/auth?${params}`, state, codeVerifier };
|
|
255
|
+
},
|
|
256
|
+
registerWallet(opts) {
|
|
257
|
+
return createWalletRegistration(kc, opts);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
return kc;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Keycloak base URL and realm of a page Keycloak serves (`<base>/realms/<realm>/...`),
|
|
265
|
+
* or undefined off such a page.
|
|
266
|
+
*/
|
|
267
|
+
export function deriveKeycloakFromLocation(
|
|
268
|
+
location: { origin: string; pathname: string } | undefined = (
|
|
269
|
+
globalThis as { location?: { origin: string; pathname: string } }
|
|
270
|
+
).location,
|
|
271
|
+
): { baseUrl: string; realm: string } | undefined {
|
|
272
|
+
const match = location?.pathname.match(/^(.*)\/realms\/([^/]+)\//);
|
|
273
|
+
if (!location || !match) return undefined;
|
|
274
|
+
return { baseUrl: location.origin + match[1], realm: decodeURIComponent(match[2]) };
|
|
275
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { KeycloakWalletLoginProps } from "./KeycloakWalletLogin";
|
|
2
|
+
import type { MountedKeycloakWalletLogin } from "./mount";
|
|
3
|
+
|
|
4
|
+
export type { MountedKeycloakWalletLogin };
|
|
5
|
+
|
|
6
|
+
export function mount(
|
|
7
|
+
_target: string | Element,
|
|
8
|
+
_opts?: KeycloakWalletLoginProps,
|
|
9
|
+
): MountedKeycloakWalletLogin {
|
|
10
|
+
throw new Error("MpoKeycloakWallet.mount is web only");
|
|
11
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRoot } from "react-dom/client";
|
|
2
|
+
import type { Config } from "wagmi";
|
|
3
|
+
import {
|
|
4
|
+
KeycloakWalletLogin,
|
|
5
|
+
type KeycloakWalletLoginProps,
|
|
6
|
+
createKeycloakWalletConfig,
|
|
7
|
+
} from "./KeycloakWalletLogin";
|
|
8
|
+
|
|
9
|
+
export interface MountedKeycloakWalletLogin {
|
|
10
|
+
/** The wagmi config the login signs with. */
|
|
11
|
+
readonly config: Config;
|
|
12
|
+
unmount(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const mounted = new WeakMap<Element, MountedKeycloakWalletLogin>();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Render `<KeycloakWalletLogin>` into `target` under its own React root, so a
|
|
19
|
+
* Keycloak theme on another React (or none) can host it. Mounting an element
|
|
20
|
+
* that already holds one returns that one: a second root would fetch a second
|
|
21
|
+
* nonce and rotate the session cookie under the first. `unmount()` frees the
|
|
22
|
+
* element for a fresh mount.
|
|
23
|
+
*/
|
|
24
|
+
export function mount(
|
|
25
|
+
target: string | Element,
|
|
26
|
+
opts: KeycloakWalletLoginProps = {},
|
|
27
|
+
): MountedKeycloakWalletLogin {
|
|
28
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
29
|
+
if (!el) throw new Error(`MpoKeycloakWallet.mount: nothing matches ${String(target)}`);
|
|
30
|
+
const existing = mounted.get(el);
|
|
31
|
+
if (existing) return existing;
|
|
32
|
+
const config = opts.wagmiConfig ?? createKeycloakWalletConfig(opts);
|
|
33
|
+
const root = createRoot(el);
|
|
34
|
+
root.render(<KeycloakWalletLogin {...opts} wagmiConfig={config} />);
|
|
35
|
+
const handle: MountedKeycloakWalletLogin = {
|
|
36
|
+
config,
|
|
37
|
+
unmount() {
|
|
38
|
+
if (mounted.get(el) !== handle) return;
|
|
39
|
+
mounted.delete(el);
|
|
40
|
+
root.unmount();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
mounted.set(el, handle);
|
|
44
|
+
return handle;
|
|
45
|
+
}
|