diff --git a/keyserver/src/deleters/siwe-nonce-deleters.js b/keyserver/src/deleters/siwe-nonce-deleters.js index 724ad6b61..59da74b71 100644 --- a/keyserver/src/deleters/siwe-nonce-deleters.js +++ b/keyserver/src/deleters/siwe-nonce-deleters.js @@ -1,29 +1,30 @@ // @flow -import { dbQuery, SQL } from '../database/database.js'; +import { legacyKeyserverSIWENonceLifetime } from 'lib/types/siwe-types.js'; -// 30 minutes = 30min * 60sec * 1000ms -export const nonceLifetime = 30 * 60 * 1000; +import { dbQuery, SQL } from '../database/database.js'; async function deleteStaleSIWENonceEntries(): Promise { - const earliestValidCreationTime = Date.now() - nonceLifetime; + const earliestValidCreationTime = + Date.now() - legacyKeyserverSIWENonceLifetime; const query = SQL` DELETE FROM siwe_nonces WHERE creation_time < ${earliestValidCreationTime} `; await dbQuery(query); } async function checkAndInvalidateSIWENonceEntry( nonce: string, ): Promise { - const earliestValidCreationTime = Date.now() - nonceLifetime; + const earliestValidCreationTime = + Date.now() - legacyKeyserverSIWENonceLifetime; const query = SQL` DELETE FROM siwe_nonces WHERE nonce = ${nonce} AND creation_time > ${earliestValidCreationTime} `; const [result] = await dbQuery(query); return result.affectedRows && result.affectedRows > 0; } export { deleteStaleSIWENonceEntries, checkAndInvalidateSIWENonceEntry }; diff --git a/lib/types/siwe-types.js b/lib/types/siwe-types.js index 7e538325d..bc8e04edd 100644 --- a/lib/types/siwe-types.js +++ b/lib/types/siwe-types.js @@ -1,151 +1,154 @@ // @flow import type { LegacyLogInExtraInfo } from './account-types.js'; import type { SignedIdentityKeysBlob } from './crypto-types.js'; import { type DeviceTokenUpdateRequest, type PlatformDetails, } from './device-types.js'; import { type CalendarQuery } from './entry-types.js'; export type SIWENonceResponse = { +nonce: string, }; export type SIWEAuthRequest = { +message: string, +signature: string, +calendarQuery: CalendarQuery, +deviceTokenUpdateRequest?: ?DeviceTokenUpdateRequest, +platformDetails: PlatformDetails, +watchedIDs: $ReadOnlyArray, +signedIdentityKeysBlob?: ?SignedIdentityKeysBlob, +initialNotificationsEncryptedMessage?: string, +doNotRegister?: boolean, }; export type LegacySIWEAuthServerCall = { +message: string, +signature: string, +doNotRegister?: boolean, ...LegacyLogInExtraInfo, }; export type SIWESocialProof = { +siweMessage: string, +siweMessageSignature: string, }; // This is a message that the rendered webpage (landing/siwe.react.js) uses to // communicate back to the React Native WebView that is rendering it // (native/account/siwe-panel.react.js) export type SIWEWebViewMessage = | { +type: 'siwe_success', +address: string, +message: string, +signature: string, } | { +type: 'siwe_closed', } | { +type: 'walletconnect_modal_update', +state: 'open', +height: number, } | { +type: 'walletconnect_modal_update', +state: 'closed', }; export type SIWEMessage = { // RFC 4501 dns authority that is requesting the signing. +domain: string, // Ethereum address performing the signing conformant to capitalization // encoded checksum specified in EIP-55 where applicable. +address: string, // Human-readable ASCII assertion that the user will sign, and it must not // contain `\n`. +statement?: string, // RFC 3986 URI referring to the resource that is the subject of the signing // (as in the __subject__ of a claim). +uri: string, // Current version of the message. +version: string, // EIP-155 Chain ID to which the session is bound, and the network where // Contract Accounts must be resolved. +chainId: number, // Randomized token used to prevent replay attacks, at least 8 alphanumeric // characters. +nonce: string, // ISO 8601 datetime string of the current time. +issuedAt: string, // ISO 8601 datetime string that, if present, indicates when the signed // authentication message is no longer valid. +expirationTime?: string, // ISO 8601 datetime string that, if present, indicates when the signed // authentication message will become valid. +notBefore?: string, // System-specific identifier that may be used to uniquely refer to the // sign-in request. +requestId?: string, // List of information or references to information the user wishes to have // resolved as part of authentication by the relying party. They are // expressed as RFC 3986 URIs separated by `\n- `. +resources?: $ReadOnlyArray, // @deprecated // Signature of the message signed by the wallet. // // This field will be removed in future releases, an additional parameter // was added to the validate function were the signature goes to validate // the message. +signature?: string, // @deprecated // Type of sign message to be generated. // // This field will be removed in future releases and will rely on the // message version. +type?: 'Personal signature', +verify: ({ +signature: string, ... }) => Promise, +toMessage: () => string, }; export type SIWEResult = { +address: string, +message: string, +signature: string, +nonceTimestamp: number, }; export type IdentityWalletRegisterInput = { +address: string, +message: string, +signature: string, +fid?: ?string, }; export const SIWEMessageTypes = Object.freeze({ MSG_AUTH: 'msg_auth', MSG_BACKUP: 'msg_backup', }); export type SIWEMessageType = $Values; export type SIWEBackupSecrets = { +message: string, +signature: string, }; + +export const legacyKeyserverSIWENonceLifetime = 30 * 60 * 1000; // 30 minutes +export const identitySIWENonceLifetime = 2 * 60 * 1000; // 2 minutes diff --git a/native/account/registration/ethereum-utils.js b/native/account/registration/ethereum-utils.js index 616380eb8..46ab7ba6a 100644 --- a/native/account/registration/ethereum-utils.js +++ b/native/account/registration/ethereum-utils.js @@ -1,60 +1,72 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { ENSCacheContext } from 'lib/components/ens-cache-provider.react.js'; -import type { SIWEResult } from 'lib/types/siwe-types.js'; +import { + type SIWEResult, + identitySIWENonceLifetime, + legacyKeyserverSIWENonceLifetime, +} from 'lib/types/siwe-types.js'; +import { usingCommServicesAccessToken } from 'lib/utils/services-utils.js'; import { RegistrationContext } from './registration-context.js'; import { type EthereumAccountSelection, ensAvatarSelection, } from './registration-types.js'; function useGetEthereumAccountFromSIWEResult(): ( result: SIWEResult, ) => Promise { const registrationContext = React.useContext(RegistrationContext); invariant(registrationContext, 'registrationContext should be set'); const { setCachedSelections } = registrationContext; const cacheContext = React.useContext(ENSCacheContext); const { ensCache } = cacheContext; return React.useCallback( async result => { // We want to figure out if the user has an ENS avatar now // so that we can default to the ENS avatar in AvatarSelection let avatarURI: ?string = null; if (ensCache) { avatarURI = await ensCache.getAvatarURIForAddress(result.address); } const ethereumAccount = { accountType: 'ethereum', ...result, avatarURI, }; setCachedSelections(oldUserSelections => { const base = { ...oldUserSelections, ethereumAccount, }; if (base.avatarData || !avatarURI) { return base; } return { ...base, avatarData: ensAvatarSelection, }; }); return ethereumAccount; }, [ensCache, setCachedSelections], ); } -export { useGetEthereumAccountFromSIWEResult }; +function siweNonceExpired(nonceTimestamp: number): boolean { + const nonceLifetime = usingCommServicesAccessToken + ? identitySIWENonceLifetime + : legacyKeyserverSIWENonceLifetime; + return Date.now() > nonceTimestamp + nonceLifetime; +} + +export { useGetEthereumAccountFromSIWEResult, siweNonceExpired };