Skip to main content

Get Shop user information

After a user completes a Shop SDK authentication or lead capture flow, your app can read the information that the user authorized. The flow returns identity claims and a short-lived consent token. You exchange the consent token for a user access token, then use that token to make authenticated, user-scoped requests to the Shop Users API. In this guide, you'll turn a completed Shop SDK flow into an authenticated session that can read Shop user data.


In this guide, you'll learn how to:

  • Capture the identity claims and consent token that a completed Shop SDK flow returns.
  • Exchange the consent token for a user access token using the Shop Partners API.
  • Understand how OAuth scopes and granted Rich Authorization Request (RAR) claims control the returned fields.
  • Use the access token to read user-scoped data from the Shop Users API.

  • A Shop app with a client ID and client secret.
  • A Sign in with Shop or lead capture integration with the Shop SDK. Both features can return the consent token.
  • A trusted server where you can protect your client credentials and the user's tokens.

When a user completes the Shop SDK login or lead-capture feature, the flow can return identity claims and a signed shopConsentToken. The token is short-lived and represents the user's delegated consent to your app. The onComplete event carries this information:

FieldDescription
emailThe user's email address.
emailVerifiedWhether Shop verified the email address.
consentedScopesA space-separated list of the scopes the user consented to.
signedInWhether the user signed in, as opposed to only providing their email address.
shopConsentTokenThe consent token to exchange for a user access token.

JavaScript

function handleComplete(event) {
if (event.signedIn && event.shopConsentToken) {
// Send the consent token to your server over HTTPS.
sendToBackend({
email: event.email,
consentToken: event.shopConsentToken,
});
}
}

const login = await sdk.create('login', {
attributes: {scope: 'shop_app:oauth'},
onComplete: handleComplete,
});

Register the same onComplete handler on a lead-capture instance to exchange consent from that flow. Data requested with RAR isn't returned directly in the browser event. After the server exchanges shopConsentToken, the issued access token contains the granted RAR claims in its scope value.

A third-party identity provider integration signs users in and returns identity claims, such as sub and email, but it doesn't return a shopConsentToken. Use the Shop SDK login or lead-capture feature to exchange a consent token and call the Shop Users API.

Handle the consent token like a credential

Send the shopConsentToken only over HTTPS, exchange it from a trusted server, and don't log it. Don't construct a consent token yourself, and don't accept one from arbitrary input.


From your server, call the fetchTokensForUser mutation on the Shop Partners API, authenticating with your client ID and client secret over HTTP Basic Authentication. The first exchange uses the consent token. Every later exchange uses the publicId that the first call returns.

Endpoint: https://shop.app/api/latest/partners/graphql.json

GraphQL Mutation

mutation FetchTokensForUser($consentToken: String, $publicId: String) {
fetchTokensForUser(consentToken: $consentToken, publicId: $publicId) {
publicId
accessToken
expiresIn
tokenType
scope
user {
sub
email
emailVerified
givenName
familyName
name
phone
phoneVerified
birthdate
avatar
}
userErrors {
field
message
}
}
}

The user field returns the same user profile claims that the Shop Users API exposes, inline on the mutation response. Each field is gated by the issued token's scope: a field whose gating scope wasn't granted resolves to null, even if the response selects it. This lets you populate your user record from the mutation response without a follow-up call. The fields and their gating scopes are:

FieldGating scope or RAR claim
subAlways returned when the token authenticates a user.
email, emailVerifiedemail (also granted by email:verified or user:manage).
givenNamename or profile, or the granted given_name RAR claim.
familyNamename or profile, or the granted family_name RAR claim.
namename or profile.
phonephone (also granted by phone:verified or user:manage), or the granted phone_number RAR claim.
phoneVerifiedphone (also granted by phone:verified or user:manage).
birthdateThe granted birthdate RAR claim.
avataravatar or profile.

If the mutation fails, then user is null and userErrors is populated. A non-null user doesn't imply that any specific field resolved. Individual fields can still be null if the issued token didn't grant the required OAuth scope or RAR claim, or if the authorized value isn't on the user's profile.

For the first exchange, pass the consent token from Step 1:

GraphQL Variables

{
"consentToken": "<shopConsentToken from the Shop SDK flow>"
}

Persist the returned publicId against your own record of the user. After the first exchange, you won't need the consent token again. To mint a fresh access token later, call the same mutation with the stored publicId instead:

GraphQL Variables

{
"publicId": "<stored publicId for this user>"
}

Provide exactly one of consentToken or publicId per call.

Protect your tokens

Keep your client credentials and the returned access token server-side. Never expose them to the storefront, browser, Shopify Function, or Checkout UI extension, and never log the access token.


Anchor to Step 3: Read user data from the Shop Users APIStep 3: Read user data from the Shop Users API

With the accessToken from Step 2, call the Shop Users API. The API is scoped to the authenticated user. Authenticate with Authorization: Bearer <accessToken>, not your client credentials.

Endpoint: https://shop.app/api/latest/users/graphql.json

Anchor to Read user profile fieldsRead user profile fields

Query the user field to read the authenticated user's profile claims. The same gating that applies to the user payload on fetchTokensForUser applies here. A field whose OAuth scope or RAR claim wasn't granted on the access token resolves to null.

GraphQL Query

query CurrentUser {
user {
sub
email
emailVerified
givenName
familyName
name
phone
phoneVerified
birthdate
avatar
}
}

If your access token doesn't authenticate a user, then the user query returns an Unauthorized error.

Anchor to Read app-owned metafieldsRead app-owned metafields

In addition to the user's profile, your app can read the metafields it owns on that user. Query them with the metafields query:

GraphQL Query

query UserMetafields {
metafields(first: 10) {
nodes {
definition {
namespace
key
}
value
}
}
}

To read a single metafield by key, use the metafield query. The namespace defaults to your app's reserved namespace, so you only need the key:

GraphQL Query

query UserMembership {
metafield(identifier: {key: "my-membership"}) {
value
}
}

You can read a user's identity information from either the user payload on fetchTokensForUser (Step 2) or the user query on the Users API (above). Both surfaces return the same fields and apply the same OAuth scope and RAR claim gating.

Access tokens expire after the number of seconds in expiresIn. When a token expires, call fetchTokensForUser again with the stored publicId to get a new one.



Was this page helpful?