Appearance
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:
Field | Type | Description |
---|---|---|
relyingParty | string | Your app's website as a string. Described in SIWF spec as "Origin domain of app frontend.", see Connect section. |
redirectUrl | string (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:
Field | Type | Description |
---|---|---|
pollIntervalMs | number | The interval in milliseconds which your app will poll a status endpoint to check if the user has successfully signed in using Warpcast. |
pollAttempts | number | The 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:
Parameter | Type | Description |
---|---|---|
fid | number | The 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>