diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.module.scss b/embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.module.scss similarity index 70% rename from embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.module.scss rename to embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.module.scss index 410605751..fe30aaa09 100644 --- a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.module.scss +++ b/embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.module.scss @@ -1,10 +1,10 @@ -.siweRiskWarningContainer { +.riskWarningContainer { display: flex; align-items: center; justify-content: center; } -.siweRiskWarningBox { +.riskWarningBox { display: flex; padding: 6px 8px; justify-content: center; @@ -19,10 +19,10 @@ background-color: var(--bg-warning-primary); } -.siweRiskWarningCheckBoxContainer { +.riskWarningCheckBoxContainer { gap: 6px; } -.siweRiskWarningCheckBoxInputContainerClassName { +.riskWarningCheckBoxInputContainerClassName { padding-top: 0px; } diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.tsx b/embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.tsx similarity index 59% rename from embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.tsx rename to embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.tsx index 4180841a0..f5bf58ff6 100644 --- a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box.tsx +++ b/embed/oko_attached/src/components/modal_variants/common/risk_warning/risk_warning.tsx @@ -2,20 +2,21 @@ import type { FC } from "react"; import { Checkbox } from "@oko-wallet/oko-common-ui/checkbox"; import { Typography } from "@oko-wallet/oko-common-ui/typography"; -import styles from "./siwe_risk_warning_box.module.scss"; +import styles from "./risk_warning.module.scss"; -interface SiweRiskWarningCheckBoxProps { +interface RiskWarningCheckBoxProps { checked: boolean; - onChange: (isValidSiweMessage: boolean) => void; + onChange: (checked: boolean) => void; } -export const SiweRiskWarningCheckBox: FC = ({ + +export const RiskWarningCheckBox: FC = ({ checked, onChange, }) => { return ( -
+
= ({ size: "xs", weight: "medium", }} - checkboxContainerClassName={styles.siweRiskWarningCheckBoxContainer} + checkboxContainerClassName={styles.riskWarningCheckBoxContainer} checkBoxInputContainerClassName={ - styles.siweRiskWarningCheckBoxInputContainerClassName + styles.riskWarningCheckBoxInputContainerClassName } />
); }; -export const SiweRiskWarningBox = () => { +export const RiskWarningBox: FC = () => { return ( -
+
The URL requesting your signature differs from the current website. This may indicate a potential security risk. diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/ethereum_siwe_signature_content.module.scss b/embed/oko_attached/src/components/modal_variants/common/sign_in_content/sign_in_content.module.scss similarity index 100% rename from embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/ethereum_siwe_signature_content.module.scss rename to embed/oko_attached/src/components/modal_variants/common/sign_in_content/sign_in_content.module.scss diff --git a/embed/oko_attached/src/components/modal_variants/common/sign_in_message.ts b/embed/oko_attached/src/components/modal_variants/common/sign_in_message.ts new file mode 100644 index 000000000..f7936ce1e --- /dev/null +++ b/embed/oko_attached/src/components/modal_variants/common/sign_in_message.ts @@ -0,0 +1,59 @@ +/** + * Common utilities for Sign In With X (SIWE/SIWS) message verification + */ + +export const ALLOW_PROTOCOLS = ["https:"]; +export const BYPASS_HOSTS = ["localhost"]; + +/** + * Verify that the sign-in message origin matches the request origin + */ +export function verifySignInOrigin( + domain: string, + uri: string | undefined, + origin: string, +): boolean { + try { + const { + origin: originByPayload, + protocol: protocolByPayload, + host: hostByPayload, + } = new URL(origin); + + // Check domain matches + if (hostByPayload !== domain) { + return false; + } + + // Check URI if provided + if (uri) { + const { origin: originByUri, protocol: protocolByUri } = new URL(uri); + + if (originByPayload !== originByUri) { + return false; + } + + // If scheme is not HTTPS, return false (when hostname is not localhost) + if ( + !BYPASS_HOSTS.includes(hostByPayload.split(":")[0]) && + (!ALLOW_PROTOCOLS.includes(protocolByPayload) || + !ALLOW_PROTOCOLS.includes(protocolByUri)) + ) { + return false; + } + } else { + // No URI, just check protocol + if ( + !BYPASS_HOSTS.includes(hostByPayload.split(":")[0]) && + !ALLOW_PROTOCOLS.includes(protocolByPayload) + ) { + return false; + } + } + } catch (error) { + console.error(error); + return false; + } + + return true; +} diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/signer_address_or_email_for_siwe/index.tsx b/embed/oko_attached/src/components/modal_variants/common/signer_info/index.tsx similarity index 92% rename from embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/signer_address_or_email_for_siwe/index.tsx rename to embed/oko_attached/src/components/modal_variants/common/signer_info/index.tsx index 263b2cd6f..a3fad69b0 100644 --- a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/signer_address_or_email_for_siwe/index.tsx +++ b/embed/oko_attached/src/components/modal_variants/common/signer_info/index.tsx @@ -6,13 +6,13 @@ import { SignerAddressOrEmailView, } from "@oko-wallet-attached/components/modal_variants/common/metadata_content/signer_address_or_email/signer_address_or_email"; -interface SignerAddressOrEmailProps { +interface SignerInfoProps { signer: string; origin: string; initialViewType?: "View Address" | "Login Info"; } -export const SignerAddressOrEmailForSiwe: FC = ({ +export const SignerInfo: FC = ({ signer, origin, initialViewType = "View Address", diff --git a/embed/oko_attached/src/components/modal_variants/common/signer_info/styles.module.scss b/embed/oko_attached/src/components/modal_variants/common/signer_info/styles.module.scss new file mode 100644 index 000000000..a7ed28fe9 --- /dev/null +++ b/embed/oko_attached/src/components/modal_variants/common/signer_info/styles.module.scss @@ -0,0 +1,10 @@ +.wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + width: 100%; + word-break: break-all; + word-wrap: break-word; + overflow-wrap: break-word; +} diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/make_arbitrary_sig_modal.tsx b/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/make_arbitrary_sig_modal.tsx index b830071a1..eaf89c0bd 100644 --- a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/make_arbitrary_sig_modal.tsx +++ b/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/make_arbitrary_sig_modal.tsx @@ -16,7 +16,7 @@ import { verifySiweMessage, } from "@oko-wallet-attached/components/modal_variants/eth/siwe_message"; import { EthereumSiweSignatureContent } from "@oko-wallet-attached/components/modal_variants/eth/arbitrary_sig/siwe_sig/make_siwe_signature_content"; -import { SiweRiskWarningCheckBox } from "@oko-wallet-attached/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box"; +import { RiskWarningCheckBox } from "@oko-wallet-attached/components/modal_variants/common/risk_warning/risk_warning"; export const MakeArbitrarySigModal: FC = ({ getIsAborted, @@ -74,14 +74,14 @@ export const MakeArbitrarySigModal: FC = ({ )}
- + {!hasOnChainSchema && } {siweMessage && !isValidSiweMessage && ( <> - diff --git a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/make_siwe_signature_content.tsx b/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/make_siwe_signature_content.tsx index a76a4f1c0..bd86c82dd 100644 --- a/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/make_siwe_signature_content.tsx +++ b/embed/oko_attached/src/components/modal_variants/eth/arbitrary_sig/siwe_sig/make_siwe_signature_content.tsx @@ -4,16 +4,16 @@ import { Typography } from "@oko-wallet/oko-common-ui/typography"; import type { Theme } from "@oko-wallet/oko-common-ui/theme"; import type { FC } from "react"; -import styles from "./ethereum_siwe_signature_content.module.scss"; +import styles from "@oko-wallet-attached/components/modal_variants/common/sign_in_content/sign_in_content.module.scss"; import { getSiweMessage, verifySiweMessage, } from "@oko-wallet-attached/components/modal_variants/eth/siwe_message"; import { SiweSigTitleBadge } from "@oko-wallet-attached/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_sig_title_badge"; -import { SignerAddressOrEmailForSiwe } from "@oko-wallet-attached/components/modal_variants/eth/arbitrary_sig/siwe_sig/signer_address_or_email_for_siwe"; +import { SignerInfo } from "@oko-wallet-attached/components/modal_variants/common/signer_info"; import { MakeSignatureRawCodeBlockContainer } from "@oko-wallet-attached/components/modal_variants/common/make_signature/make_sig_modal_code_block_container"; import { MakeSignatureRawCodeBlock } from "@oko-wallet-attached/components/modal_variants/common/make_signature/make_sig_modal_code_block"; -import { SiweRiskWarningBox } from "@oko-wallet-attached/components/modal_variants/eth/arbitrary_sig/siwe_sig/siwe_risk_warning_box"; +import { RiskWarningBox } from "@oko-wallet-attached/components/modal_variants/common/risk_warning/risk_warning"; import { Avatar } from "@oko-wallet-attached/components/avatar/avatar"; interface EthereumSiweSignatureContentProps { @@ -37,7 +37,7 @@ export const EthereumSiweSignatureContent: FC< {isValidSiweMessage ? ( ) : ( - + )}
@@ -58,7 +58,7 @@ export const EthereumSiweSignatureContent: FC<
- boolean; @@ -22,12 +29,48 @@ export const MakeMessageSigModal: FC = ({ data, modalId, }) => { - const { onReject, onApprove, isLoading, isApproveEnabled, isDemo, theme } = - useMessageSigModal({ - getIsAborted, - data, - modalId, - }); + // Decode hex message to check for SIWS + const decodedMessage = useMemo(() => { + try { + const bytes = hexToUint8Array(data.payload.data.message); + return new TextDecoder().decode(bytes); + } catch { + return data.payload.data.message; + } + }, [data.payload.data.message]); + + const siwsMessage = getSiwsMessage(decodedMessage); + const isValidSiwsMessage = siwsMessage + ? verifySiwsMessage(siwsMessage, data.payload.origin) + : false; + const [isSiwsRiskWarningChecked, setIsSiwsRiskWarningChecked] = + useState(false); + + const { + onReject, + onApprove, + isLoading, + isApproveEnabled: isApproveEnabledOriginal, + isDemo, + theme, + } = useMessageSigModal({ + getIsAborted, + data, + modalId, + }); + + const isApproveEnabled = useMemo(() => { + if (siwsMessage && !isValidSiwsMessage) { + return isApproveEnabledOriginal && isSiwsRiskWarningChecked; + } + + return isApproveEnabledOriginal; + }, [ + isApproveEnabledOriginal, + isSiwsRiskWarningChecked, + siwsMessage, + isValidSiwsMessage, + ]); return (
@@ -37,11 +80,25 @@ export const MakeMessageSigModal: FC = ({
- + {!!siwsMessage ? ( + + ) : ( + + )}
+ {siwsMessage && !isValidSiwsMessage && ( + <> + + + + )} +