Update User Profile
This guide explains how to change the universal profile of an authenticated user.
When a user creates a new account with Leather Wallet a basic profile is created and stored on the user's own storage hub. The basic profile contains only a public key. It can be extended to contain personal information like an avatar,name and description. It is always cryptographically signed by the user's key, the so-called owner key.
Hiro provides a hosting services for storage hubs. Learn about hosting a storage hub at this tutorial.
For users with BNS names, the profile can be read by any user. This extended profile can be used by any application to show a personalized user card like this:
In order to update the public profile, apps can make request to the Stacks wallet. These requests are reviewed and confirmed by the user in the wallet similar to transaction signing.
Install dependency
In order to utilize the latest profile updating with the Leather Wallet, use a version >= 7.1.0 of the @stacks/connect
NPM package.
The following dependency must be installed:
npm install @stacks/connect
This also installs the NPM package @stacks/profile
. It contains the data type PublicPersonProfile
used for the public profile.
Initiate session
Users must authenticate to an app before you request message signing. Users can install an authenticator like the Leather Wallet.
See the authentication guide before proceeding to integrate the following message signing capabilities.
Prompt to update the profile
After the user chose the content of the profile, create a PublicPersonProfile
object from that data and call the openProfileUpdateRequestPopup
function provided by the connect
package to trigger the display of the profile update prompt.
import { openProfileUpdateRequestPopup } from '@stacks/connect';
const profile = {
'@type': 'Person',
'@context': 'https://schema.org',
name: 'Friedger',
image: [
{ '@type': 'ImageObject', name: 'avatar', contentUrl: 'https://friedger.de/profile.png' },
],
};
openProfileUpdateRequestPopup({
profile,
appDetails: {
name: 'My App',
icon: 'https://example-app.com/my-app-logo.svg',
},
onFinish(data) {
console.log('Profile published', data);
},
});
Several parameters are available for calling openProfileUpdateRequestPopup
. Here is the exact interface for them:
interface ProfileUpdateRequestOptions {
profile: PublicPersonProfile;
onFinish?: (data: PublicPersonProfile) => void;
onCancel?: () => void;
appDetails: {
name: string;
icon: string;
};
authOrigin?: string;
stxAddress?: string;
userSession?: UserSession;
}
After the profile was updated, the user can share the profile with other users.
Lookup a Public Profile
The public profile for a given BNS name can be looked up using
the stacks.js
lookupProfile
method.
The functions takes an object of type ProfileLookupOptions
export interface ProfileLookupOptions {
username: string;
zoneFileLookupURL?: string;
network?: StacksNetworkName | StacksNetwork;
}
The function returns a promise with the data of the public profile if the data could be retrieved from the BNS name owner's storage and if the retrieved JSON token was sucessfully verified.
The recommended schema for the profile is as follows:
export interface PublicPersonProfile extends PublicProfileBase {
'@type': 'Person';
name?: string;
givenName?: string;
familyName?: string;
description?: string;
image?: { '@type': 'ImageObject'; name?: string; contentUrl?: string; [k: string]: unknown }[];
website?: {
'@type'?: string;
url?: string;
[k: string]: unknown;
}[];
account?: {
'@type'?: string;
service?: string;
identifier?: string;
proofType?: string;
proofUrl?: string;
proofMessage?: string;
proofSignature?: string;
[k: string]: unknown;
}[];
worksFor?: {
'@type'?: string;
'@id'?: string;
[k: string]: unknown;
}[];
knows?: {
'@type'?: string;
'@id'?: string;
[k: string]: unknown;
}[];
address?: {
'@type'?: string;
streetAddress?: string;
addressLocality?: string;
postalCode?: string;
addressCountry?: string;
[k: string]: unknown;
};
birthDate?: string;
taxID?: string;
[k: string]: unknown;
}
Usage in React Apps
Import the useConnect
helper from connect-react
package to update profiles more seamlessly with React apps.
You must install a version >= 21.0.0
npm install @stacks/connect-react
Use the function with the same parameters as outlined above. However, you don't have to specify appDetails
since they are detected automatically if useConnect
has been used already for authentication.
import { useConnect } from '@stacks/connect-react';
const MyComponent = () => {
const { doProfileUpdate } = useConnect();
const onClick = async () => {
const options = {
/** See description above */
};
await doProfileUpdate(options);
};
return <span onClick={onClick}>Update Profile</span>;
};
Profile Update Request / Response Payload
Under the hood, @stacks/connect
will serialize and deserialize data between your app and the Leather Wallet.
These payloads are tokens that conform to the JSON Web Token (JWT) standard with additional support for the secp256k1
curve used by Bitcoin and many other cryptocurrencies.
Profile Update Request Payload
When an application triggers a profile update from @stacks/connect
, the options of that profile update request are serialized into a profileUpdateRequest
payload. The profileUpdateRequest
is similar to the authRequest payload used for authentication.
The profile update request payload has the following schema, in addition to the standard JWT required fields:
interface ProfileUpdatePayload {
profile: PublicPersonProfile;
publicKey: string;
/**
* Provide the Leather Wallet with a suggested account to sign this transaction with.
* This is set by default if a `userSession` option is provided.
*/
stxAddress?: string;
appDetails?: AuthOptions['appDetails'];
network?: StacksNetwork;
}
Profile Update Response payload
After the user confirms the update, a profileUpdateResponse
payload of type PublicProfile
is sent back to your app. It contains the updated profile as confirmed by the user. Note, that this profile can be different to the requested profile by the app because the user might have modified the profile in the wallet before confirming the changes.
StacksProvider injected variable
When users have the Leather Wallet extension installed, the extension will inject a global StacksProvider
variable into the JavaScript context of your web app. This allows your JavaScript code to hook into the extension, and make authentication, transaction and signature requests. @stacks/connect
automatically detects and uses this global variable for you.
At the moment, only the Leather Wallet extension and the Xverse built-in browswer includes a StacksProvider
, however, ideally more wallets (and mobile wallets) will support this format, so that your app can be compatible with any Stacks wallet that has functionality to embed web applications.
In your web application, you can check to see if the user has a compatible wallet installed by checking for the presence of window.StacksProvider
.
Here is the interface for the StacksProvider
variable.
interface StacksProvider {
transactionRequest(payload: string): Promise<FinishedTxPayload | SponsoredFinishedTxPayload>;
authenticationRequest(payload: string): Promise<string>;
signatureRequest(payload: string): Promise<SignatureData>;
structuredDataSignatureRequest(payload: string): Promise<SignatureData>;
profileUpdateRequest(payload: string): Promise<PublicProfile>;
getProductInfo:
| undefined
| (() => {
version: string;
name: string;
meta?: {
tag?: string;
commit?: string;
[key: string]: any;
};
[key: string]: any;
});
}