Skip to content

Login with Farcaster

To authenticate a user via Farcaster (SIWF), use the Expo SDK's useLoginWithFarcaster hook

Initializing the login flow

To initialize login, use the loginWithFarcaster function from useLoginWithFarcaster hook to start the Farcaster login flow.

tsx
import {useLoginWithFarcaster} from '@privy-io/expo';
...
const {loginWithFarcaster, state} = useLoginWithFarcaster();

As a parameter to loginWithFarcaster, you should pass an object containing:

FieldTypeDescription
relyingPartystringYour app's website as a string. Described in SIWF spec as "Origin domain of app frontend.", see Connect section.
redirectUrlstring (optional)A URL path that Warpcast will automatically redirect to after successful authentication. This defaults to a link back to your app root, eg. '/', if not provided.

Optionally as a second parameter, you can pass in an object containing:

FieldTypeDescription
pollIntervalMsnumberThe interval in milliseconds which your app will poll a status endpoint to check if the user has successfully signed in using Warpcast.
pollAttemptsnumberThe number of polling attempts that will be made to check for successful login.

WARNING

If you pass in custom polling configuration, make sure to give the user enough time to go through the login process on Warpcast. The default values are pollIntervalMs = 1000 and pollAttempts = 10 giving the user 10 seconds to go through the login process. In our testing, this is usually enough time, but you may want to make it longer.

When this method is invoked, the user will be deeplinked to the Warpcast app on their device if they have it installed, or an installation page for the app. Within the Warpcast app, they can complete the login flow.

tsx
import {useLoginWithFarcaster, usePrivy} from '@privy-io/expo';

export function LoginScreen() {
  const {user} = usePrivy();
  const {loginWithFarcaster} = useLoginWithFarcaster();

  if (user) {
    return (
      <>
        <Text>Logged In</Text>
        <Text>{JSON.stringify(user, null, 2)}</Text>
      </>
    );
  }

  return (
    <View>
      <Button
        onPress={() => {
          loginWithFarcaster({relyingParty: 'https://example.app'});
        }}
      >
        Login
      </Button>
    </View>
  );
}

If loginWithFarcaster succeeds, it will return a PrivyUser object with details about the authenticated user.

Reasons loginWithFarcaster might fail include:

  • the network request fails
  • the login attempt is made after the user is already logged in
  • the user cancels the login flow after being linked out to Warpcast
  • the user takes too long to login and the polling time expires

To handle these failures, use the onError callback as described below.

Callbacks

Optional callbacks can be passed to useLoginWithFarcaster to handle events that occur during the authentication or linking flows.

tsx
const {state, loginWithFarcaster} = useLoginWithFarcaster({
  onSuccess: console.log,
  onError: console.error,
});

onSuccess

Pass an onSuccess function to useLoginWithFarcaster to run custom logic after a successful login. Within this callback you can access the PrivyUser returned by loginWithFarcaster, as well as an isNewUser boolean indicating if this is the user's first login to your app.

You can use this callback with both the useLoginWithFarcaster hook.

TIP

After a user has already been authenticated, you can link their Farcaster account to an existing account by following the same flow with the useLinkWithFarcaster hook instead.

tsx
import {useLoginWithFarcaster} from '@privy-io/expo';

export function LoginScreen() {
  const {loginWithFarcaster} = useLoginWithFarcaster({
    onSuccess(user, isNewUser) {
      // show a toast, send analytics event, etc...
    },
  });

  // ...
}

onError

Pass an onError function to useLoginWithFarcaster to declaratively handle errors occur during the flow.

tsx
import {useLoginWithFarcaster} from '@privy-io/expo';

export function LoginScreen() {
  const {loginWithFarcaster} = useLoginWithFarcaster({
    onError(error) {
      // show a toast, update form errors, etc...
    },
  });

  // ...
}

Tracking login flow state

The state variable returned from useLoginWithFarcaster will always be one of the following values.

ts
export type FarcasterFlowState =
  | {status: 'initial'}
  | {status: 'error'; error: Error | null}
  | {status: 'generating-uri'}
  | {status: 'awaiting-uri'}
  | {status: 'polling-status'}
  | {status: 'submitting-token'}
  | {status: 'done'};

Conditional rendering

You can use the state.status variable to conditionally render your UI based on the user's current state in the login flow.

tsx
import {View, ActivityIndicator} from 'react-native';

import {useLoginWithFarcaster} from '@privy-io/expo';

export function LoginScreen() {
  const {state, loginWithFarcaster} = useLoginWithFarcaster();

  return <View>{state.status === 'polling-status' && <ActivityIndicator />}</View>;
}

Error state

When state.status is equal to 'error', the error value is accessible as state.error which can be used to render inline hints in a login form.

tsx
import {useLoginWithFarcaster, hasError} from '@privy-io/expo';

export function LoginScreen() {
  const {state, loginWithFarcaster} = useLoginWithFarcaster();

  return (
    <View>
      {/* other ui... */}

      {state.status === 'error' && (
        <>
          <Text style={{color: 'red'}}>There was an error</Text>
          <Text style={{color: 'lightred'}}>{state.error.message}</Text>
        </>
      )}

      {hasError(state) && (
        // The `hasError` util is also provided as a convenience
        // (for typescript users, this provides the same type narrowing as above)
        <>
          <Text style={{color: 'red'}}>There was an error</Text>
          <Text style={{color: 'lightred'}}>{state.error.message}</Text>
        </>
      )}
    </View>
  );
}

Unlinking Farcaster accounts

Once a user has linked a Farcaster account to their profile, you may also want to give them the option to unlink it.

To do this, use the unlinkFarcaster method from the useUnlinkFarcaster hook. As a parameter to this method, pass an object with the following fields:

ParameterTypeDescription
fidnumberThe Farcaster ID, from the fid field of the Farcaster linked account.
tsx
import {useUnlinkFarcaster} from '@privy-io/expo';

export function LinkedFarcasterAccountScreen() {
  const unlinkFarcaster = useUnlinkFarcaster();

  return (
    <View>
      <Text>Display Name: {linkedAccount.display_name}</Text>
      <Button onPress={() => unlinkFarcaster({fid: linkedAccount.fid})}>
        Unlink Farcaster Account
      </Button>
    </View>
  );
}

Enforcing login vs. sign-up

Depending on how your app's authentication flow is set up, you may want to provide separate routes for signing up to your app (e.g. creating their account for the first time) and logging in as a returning user (e.g. logging into an existing account). You can distinguish login vs. sign-up flows by passing an optional disableSignup boolean to your login call like so:

tsx
<Button
  onPress={() =>
    loginWithFarcaster({
      relyingParty: 'https://example.app',
    })
  }
>
  <Text>Login</Text>
</Button>