Migrating users to Privy
Users
Managing users
Webhooks
Handling the user object
All of Privy’s login methods result in a unified JSON representation of your user.
To get the current user, inspect the user
object returned by the usePrivy
hook:
const { user } = usePrivy();
Unauthenticated users
For unauthenticated users, the user
object will be null
.
Authenticated users
For authenticated users, you can use the following fields:
The Privy-issued DID for the user. If you need to store additional information about a user, you can use this DID to reference them.
The datetime of when the user was created, in ISO 8601 format.
The list of accounts associated with this user. Each account contains additional metadata that may be helpful for advanced use cases.
Denotes that this is a wallet account
The server wallet ID of the wallet. Null if the wallet is not delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
The wallet address
Chain type of the wallet address
The wallet client used for this wallet during the most recent verification. If the value is ‘privy’, then this is a privy embedded wallet
The connector type used for this wallet during the most recent verification
If this is a ‘privy’ embedded wallet, stores the recovery method
Whether the wallet is imported. Only applies to embedded wallets (walletClientType === ‘privy’)
Whether the wallet is delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
HD index for the wallet. Only applies to embedded wallets (walletClientType === ‘privy’)
Denotes that this is a Twitter account
The ‘sub’ claim from the Twitter-issued JWT for this account
The username associated with the Twitter account
The name associated with the Twitter account
The profile picture URL associated with the Twitter account
Denotes that this is a Github account
The ‘sub’ claim from the Github-issued JWT for this account
The username associated with the Github account
The name associated with the Github account
The email associated with the Github account
Denotes that this is a LinkedIn account
The ‘sub’ claim from the LinkedIn-issued JWT for this account
The name associated with the LinkedIn account
The email associated with the LinkedIn account
The vanityName/profile URL associated with the LinkedIn account
Denotes that this is a Farcaster account
The Farcaster on-chain FID
The Farcaster ethereum address that owns the FID
The Farcaster protocol username
The Farcaster protocol display name
The Farcaster protocol bio
The Farcaster protocol profile picture
The Farcaster protocol profile url
The public key of the signer, if set. This is not guaranteed to be valid, as the user can revoke the key at any time
Denotes that this is a Passkey account
The passkey credential ID
Whether or not this passkey can be used for MFA
The type of authenticator holding the passkey
Metadata about the device that registered the passkey
Metadata about the OS that registered the passkey
Metadata about the browser that registered the passkey
Denotes that this is a Telegram account
The user ID that owns this Telegram account
The first name of the user
The last name of the user
The username associated with the Telegram account
The url of the user’s profile picture
Denotes that this is a cross-app account
The user’s embedded wallet address(es) from the provider app
The user’s smart wallet address(es) from the provider app
Metadata about the provider app
The subject identifier for this cross-app account
The list of MFA Methods associated with this user.
Whether or not the user has explicitly accepted the Terms and Conditions and/or Privacy Policy
Whether or not the user is a guest
The user’s email address, if they have linked one. It cannot be linked to another user.
The email address.
The user’s phone number, if they have linked one. It cannot be linked to another user.
The phone number.
The user’s first verified wallet, if they have linked at least one wallet. It cannot be linked to another user.
The user’s Twitter account, if they have linked one. It cannot be linked to another user.
The user’s Github account, if they have linked one. It cannot be linked to another user.
The user’s LinkedIn account, if they have linked one. It cannot be linked to another user.
The user’s Farcaster account, if they have linked one. It cannot be linked to another user.
The Farcaster on-chain FID
The Farcaster ethereum address that owns the FID
The Farcaster protocol username
The Farcaster protocol display name
The Farcaster protocol bio
The Farcaster protocol profile picture
The Farcaster protocol profile url
The public key of the signer, if set. This is not guaranteed to be valid, as the user can revoke the key at any time
The user’s Telegram account, if they have linked one. It cannot be linked to another user.
Custom metadata field for a given user account
You can set custom metadata for a user via Privy’s backend server SDK and/or API endpoints.
If a user has not linked an account of a given type, the corresponding field on the user
object will be undefined.
Users can have multiple passkeys linked to their account. To find all linked
passkeys, use the linkedAccounts
list and filter by passkey
account type.
Below is an example of how you might use the user
object in a minimal user profile:
import { usePrivy } from "@privy-io/react-auth";
function User() {
const { ready, authenticated, user } = usePrivy();
// Show nothing if user is not authenticated or data is still loading
if (!(ready && authenticated) || !user) {
return null;
}
return (
<div>
<p>User {user.id} has linked the following accounts:</p>
<ul>
<li>Apple: {user.apple ? user.apple.email : "None"}</li>
<li>Discord: {user.discord ? user.discord.username : "None"}</li>
<li>Email: {user.email ? user.email.address : "None"}</li>
<li>Farcaster: {user.farcaster ? user.farcaster.username : "None"}</li>
<li>GitHub: {user.github ? user.github.username : "None"}</li>
<li>Google: {user.google ? user.google.email : "None"}</li>
<li>Instagram: {user.instagram ? user.instagram.username : "None"}</li>
<li>LinkedIn: {user.linkedin ? user.linkedin.email : "None"}</li>
<li>Phone: {user.phone ? user.phone.number : "None"}</li>
<li>Spotify: {user.spotify ? user.spotify.email : "None"}</li>
<li>Telegram: {user.telegram ? user.telegram.username : "None"}</li>
<li>TikTok: {user.tiktok ? user.tiktok.username : "None"}</li>
<li>Twitter: {user.twitter ? user.twitter.username : "None"}</li>
<li>Wallet: {user.wallet ? user.wallet.address : "None"}</li>
</ul>
</div>
);
}
Refreshing the user
object
In order to update a user
object after any type of backend update, (i.e. unlinking an account or setting custom metadata) you can ensure the user object in the application is up-to-date by invoking the refreshUser
method from the useUser
hook:
import { useUser } from "@privy-io/react-auth";
const { user, refreshUser } = useUser();
const updateMetadata = async (value: string) => {
// Make API request to update custom metadata for a user from the backend
const response = await updateUserMetadata({ value });
await refreshUser();
// `user` object should be updated
console.log(user);
};
To get the current user, inspect the user
object returned by the usePrivy
hook:
const { user } = usePrivy();
Unauthenticated users
For unauthenticated users, the user
object will be null
.
Authenticated users
For authenticated users, you can use the following fields:
The Privy-issued DID for the user. If you need to store additional information about a user, you can use this DID to reference them.
The datetime of when the user was created, in ISO 8601 format.
The list of accounts associated with this user. Each account contains additional metadata that may be helpful for advanced use cases.
Denotes that this is a wallet account
The server wallet ID of the wallet. Null if the wallet is not delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
The wallet address
Chain type of the wallet address
The wallet client used for this wallet during the most recent verification. If the value is ‘privy’, then this is a privy embedded wallet
The connector type used for this wallet during the most recent verification
If this is a ‘privy’ embedded wallet, stores the recovery method
Whether the wallet is imported. Only applies to embedded wallets (walletClientType === ‘privy’)
Whether the wallet is delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
HD index for the wallet. Only applies to embedded wallets (walletClientType === ‘privy’)
Denotes that this is a Twitter account
The ‘sub’ claim from the Twitter-issued JWT for this account
The username associated with the Twitter account
The name associated with the Twitter account
The profile picture URL associated with the Twitter account
Denotes that this is a Github account
The ‘sub’ claim from the Github-issued JWT for this account
The username associated with the Github account
The name associated with the Github account
The email associated with the Github account
Denotes that this is a LinkedIn account
The ‘sub’ claim from the LinkedIn-issued JWT for this account
The name associated with the LinkedIn account
The email associated with the LinkedIn account
The vanityName/profile URL associated with the LinkedIn account
Denotes that this is a Farcaster account
The Farcaster on-chain FID
The Farcaster ethereum address that owns the FID
The Farcaster protocol username
The Farcaster protocol display name
The Farcaster protocol bio
The Farcaster protocol profile picture
The Farcaster protocol profile url
The public key of the signer, if set. This is not guaranteed to be valid, as the user can revoke the key at any time
Denotes that this is a Passkey account
The passkey credential ID
Whether or not this passkey can be used for MFA
The type of authenticator holding the passkey
Metadata about the device that registered the passkey
Metadata about the OS that registered the passkey
Metadata about the browser that registered the passkey
Denotes that this is a Telegram account
The user ID that owns this Telegram account
The first name of the user
The last name of the user
The username associated with the Telegram account
The url of the user’s profile picture
Denotes that this is a cross-app account
The user’s embedded wallet address(es) from the provider app
The user’s smart wallet address(es) from the provider app
Metadata about the provider app
The subject identifier for this cross-app account
The list of MFA Methods associated with this user.
Whether or not the user has explicitly accepted the Terms and Conditions and/or Privacy Policy
Whether or not the user is a guest
The user’s email address, if they have linked one. It cannot be linked to another user.
The email address.
The user’s phone number, if they have linked one. It cannot be linked to another user.
The phone number.
The user’s first verified wallet, if they have linked at least one wallet. It cannot be linked to another user.
The user’s Twitter account, if they have linked one. It cannot be linked to another user.
The user’s Github account, if they have linked one. It cannot be linked to another user.
The user’s LinkedIn account, if they have linked one. It cannot be linked to another user.
The user’s Farcaster account, if they have linked one. It cannot be linked to another user.
The Farcaster on-chain FID
The Farcaster ethereum address that owns the FID
The Farcaster protocol username
The Farcaster protocol display name
The Farcaster protocol bio
The Farcaster protocol profile picture
The Farcaster protocol profile url
The public key of the signer, if set. This is not guaranteed to be valid, as the user can revoke the key at any time
The user’s Telegram account, if they have linked one. It cannot be linked to another user.
Custom metadata field for a given user account
You can set custom metadata for a user via Privy’s backend server SDK and/or API endpoints.
If a user has not linked an account of a given type, the corresponding field on the user
object will be undefined.
Users can have multiple passkeys linked to their account. To find all linked
passkeys, use the linkedAccounts
list and filter by passkey
account type.
Below is an example of how you might use the user
object in a minimal user profile:
import { usePrivy } from "@privy-io/react-auth";
function User() {
const { ready, authenticated, user } = usePrivy();
// Show nothing if user is not authenticated or data is still loading
if (!(ready && authenticated) || !user) {
return null;
}
return (
<div>
<p>User {user.id} has linked the following accounts:</p>
<ul>
<li>Apple: {user.apple ? user.apple.email : "None"}</li>
<li>Discord: {user.discord ? user.discord.username : "None"}</li>
<li>Email: {user.email ? user.email.address : "None"}</li>
<li>Farcaster: {user.farcaster ? user.farcaster.username : "None"}</li>
<li>GitHub: {user.github ? user.github.username : "None"}</li>
<li>Google: {user.google ? user.google.email : "None"}</li>
<li>Instagram: {user.instagram ? user.instagram.username : "None"}</li>
<li>LinkedIn: {user.linkedin ? user.linkedin.email : "None"}</li>
<li>Phone: {user.phone ? user.phone.number : "None"}</li>
<li>Spotify: {user.spotify ? user.spotify.email : "None"}</li>
<li>Telegram: {user.telegram ? user.telegram.username : "None"}</li>
<li>TikTok: {user.tiktok ? user.tiktok.username : "None"}</li>
<li>Twitter: {user.twitter ? user.twitter.username : "None"}</li>
<li>Wallet: {user.wallet ? user.wallet.address : "None"}</li>
</ul>
</div>
);
}
Refreshing the user
object
In order to update a user
object after any type of backend update, (i.e. unlinking an account or setting custom metadata) you can ensure the user object in the application is up-to-date by invoking the refreshUser
method from the useUser
hook:
import { useUser } from "@privy-io/react-auth";
const { user, refreshUser } = useUser();
const updateMetadata = async (value: string) => {
// Make API request to update custom metadata for a user from the backend
const response = await updateUserMetadata({ value });
await refreshUser();
// `user` object should be updated
console.log(user);
};
You can get information about the current user from the user
object in the usePrivy
hook:
const {user} = usePrivy();
For unauthenticated users, the user
object will be null. For authenticated users, you can use:
user.id
to get their Privy DID, which you may use to identify your user on your backenduser.created_at
to get a UNIX timestamp of when the user was created.user.linked_accounts
to get an array of the user’s linked accounts
Parsing linked accounts
Each entry in the linked_accounts
array has different fields depending on the user data associated with that account type.
See the dropdowns below to see the specific fields associated with each account type.
Denotes that this is a wallet account
The server wallet ID of the wallet. Null if the wallet is not delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
The wallet address
Chain type of the wallet address
The wallet client used for this wallet during the most recent verification. If the value is ‘privy’, then this is a privy embedded wallet
The connector type used for this wallet during the most recent verification
If this is a ‘privy’ embedded wallet, stores the recovery method
Whether the wallet is imported. Only applies to embedded wallets (walletClientType === ‘privy’)
Whether the wallet is delegated. Only applies to embedded wallets (walletClientType === ‘privy’)
HD index for the wallet. Only applies to embedded wallets (walletClientType === ‘privy’)
Denotes that this is a Twitter account
The ‘sub’ claim from the Twitter-issued JWT for this account
The username associated with the Twitter account
The name associated with the Twitter account
The profile picture URL associated with the Twitter account
Denotes that this is a Github account
The ‘sub’ claim from the Github-issued JWT for this account
The username associated with the Github account
The name associated with the Github account
The email associated with the Github account
Denotes that this is a LinkedIn account
The ‘sub’ claim from the LinkedIn-issued JWT for this account
The name associated with the LinkedIn account
The email associated with the LinkedIn account
The vanityName/profile URL associated with the LinkedIn account
Denotes that this is a Farcaster account
The Farcaster on-chain FID
The Farcaster ethereum address that owns the FID
The Farcaster protocol username
The Farcaster protocol display name
The Farcaster protocol bio
The Farcaster protocol profile picture
The Farcaster protocol profile url
The public key of the signer, if set. This is not guaranteed to be valid, as the user can revoke the key at any time
Denotes that this is a Passkey account
The passkey credential ID
Whether or not this passkey can be used for MFA
The type of authenticator holding the passkey
Metadata about the device that registered the passkey
Metadata about the OS that registered the passkey
Metadata about the browser that registered the passkey
Denotes that this is a Telegram account
The user ID that owns this Telegram account
The first name of the user
The last name of the user
The username associated with the Telegram account
The url of the user’s profile picture
Denotes that this is a cross-app account
The user’s embedded wallet address(es) from the provider app
The user’s smart wallet address(es) from the provider app
Metadata about the provider app
The subject identifier for this cross-app account
Once a user has authenticated with Privy, you will have access to the PrivyUser
object. This will be the main entry point for all user actions.
abstract class PrivyUser {
/// Unique identifier for the user.
String get id;
/// List of linked accounts associated with the user.
List<LinkedAccounts> get linkedAccounts;
/// List of embedded Ethereum wallets associated with the user.
List<EmbeddedEthereumWallet> get embeddedEthereumWallets;
/// List of embedded Solana wallets associated with the user.
List<EmbeddedSolanaWallet> get embeddedSolanaWallets;
/// Creates an Ethereum embedded wallet for the user.
Future<Result<EmbeddedEthereumWallet>> createEthereumWallet(
{bool allowAdditional = false});
/// Creates a Solana embedded wallet for the user.
Future<Result<EmbeddedSolanaWallet>> createSolanaWallet();
}
Linked accounts
A user contains a list of LinkedAccounts, which are all account types associated with the user.
/// A sealed class representing different types of linked accounts.
sealed class LinkedAccount {}
/// Represents a phone number-based linked account.
class PhoneAccount extends LinkedAccount {}
/// Represents an email-based linked account.
class EmailAccount extends LinkedAccount {}
/// Represents a custom authentication-linked account.
class CustomAuth extends LinkedAccount {}
/// Represents an Ethereum embedded wallet linked account.
class EmbeddedEthereumWalletAccount extends LinkedAccount {}
/// Represents a Solana embedded wallet linked account.
class EmbeddedSolanaWalletAccount extends LinkedAccount {}
Once a user has authenticated with Privy, you will have access to the PrivyUser
object. This will be the main entry point for all user actions.
public data class PrivyUser(
// The user's Privy ID
val id: String,
// A list of all linked accounts - can be authentication methods or embedded wallets
val linkedAccounts: List<LinkedAccount>,
// A list of user's ethereum wallets
val ethereumWallets: List<EmbeddedEthereumWallet>,
// A list of the user's solana wallets
val solanaWallets: List<EmbeddedSolanaWallet>
// Refresh the user
public suspend fun refresh(): Result<Unit>
// Other user methods
)
public sealed interface LinkedAccount {
public data class PhoneAccount(/* Account specific data */) : LinkedAccount
public data class EmailAccount(/* Account specific data */) : LinkedAccount
public data class CustomAuth(/* Account specific data */) : LinkedAccount
public data class EmbeddedEthereumWalletAccount(/* Account specific data */) : LinkedAccount
public data class EmbeddedSolanaWalletAccount(/* Account specific data */) : LinkedAccount
// Other linked account types
}
Once a user has authenticated with Privy, you will have access to the PrivyUser
object. This will be the main entry point for all user actions.
public protocol PrivyUser {
/// The user's Privy ID
var id: String { get }
/// The user's ID token
var identityToken: String? { get }
/// The point in time at which the logged in user was created.
/// Value will only be nil if there is no user currently logged in.
var createdAt: Date? { get }
// A list of all linked accounts - can be authentication methods or embedded wallets
var linkedAccounts: [LinkedAccount] { get }
// A list of user's ethereum wallets
var embeddedEthereumWallets: [EmbeddedEthereumWallet] { get }
// A list of the user's solana wallets
var embeddedSolanaWallets: [EmbeddedSolanaWallet] { get }
// Refresh the user
func refresh() async throws
// Returns the user's access token, but will first refresh the user session if needed.
func getAccessToken() async throws -> String
// Other user methods
}
public enum LinkedAccount {
case phone(PhoneNumberAccount)
case email(EmailAccount)
case customAuth(CustomAuth)
case embeddedEthereumWallet(EmbeddedEthereumWalletAccount)
case embeddedSolanaWallet(EmbeddedSolanaWalletAccount)
// Other linked account types
}
Once your user has successfully authenticated, you can get a PrivyUser
object containing their account data via:
// User will be null if no user is authenticated
PrivyUser user = PrivyManager.Instance.User;
The PrivyUser
implements the interface below:
public interface IPrivyUser
{
// The user's ID
string Id { get; }
// List of the User's linked accounts
PrivyLinkedAccount[] LinkedAccounts { get; }
// A list of the user's embedded ethereum wallets
IEmbeddedEthereumWallet[] EmbeddedWallets { get; }
// A list of the user's embedded solana wallets
IEmbeddedSolanaWallet[] EmbeddedSolanaWallets { get; }
// Creates an embedded ethereum wallet for the user
Task<IEmbeddedEthereumWallet> CreateWallet(bool allowAdditional = false);
// Creates an embedded ethereum wallet for the user at the given HD index
Task<IEmbeddedEthereumWallet> CreateWalletAtHdIndex(int hdWalletIndex);
// Creates an embedded solana wallet for the user
Task<IEmbeddedSolanaWallet> CreateSolanaWallet(bool allowAdditional = false);
// Custom user metadata, stored as a set of key-value pairs.
// This custom metadata is set server-side.
Dictionary<string, string> CustomMetadata { get; }
}
Make sure you subscribe to the Authentication State Updates and avoid calling the IPrivyUser
object without being Authenticated, as properties will return a default value otherwise.
user.Id = ""
user.LinkedAccounts = []
user.EmbeddedWallets = []
user.CustomMetadata = new Dictionary()
You can set custom metadata for a user via Privy’s backend server SDK and/or API endpoints.