Skip to main content

Get Shop user information

After a user signs in with Shop, your app can read information about that user. The sign-in flow returns the user's 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 sign-in into an authenticated session that can read Shop user data.

Developer preview

The Shop platform is in early access. Features and APIs might change before general availability.


In this guide, you'll learn how to:

  • Capture the identity claims and consent token that a completed Sign in with Shop flow returns.
  • Exchange the consent token for a user access token using the Shop Partners API.
  • 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 integration that uses the Shop SDK login feature, which returns the consent token.
  • A trusted server where you can hold your client credentials and the user's tokens.

Anchor to Step 1: Get user information from the sign-in flowStep 1: Get user information from the sign-in flow

When a user completes Sign in with Shop through the Shop SDK login feature, the flow returns the user's 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

const login = await sdk.create('login', {
attributes: { scope: 'shop_app:oauth' },
onComplete(event) {
if (event.signedIn && event.shopConsentToken) {
// Send the consent token to your server over HTTPS.
sendToBackend({
email: event.email,
consentToken: event.shopConsentToken,
});
}
},
});

The consent token comes from the Shop SDK login feature. 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. To exchange a consent token and call the Shop Users API, use the Shop SDK login feature.

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
refreshToken
expiresIn
tokenType
scope
user {
sub
email
emailVerified
givenName
familyName
name
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
subAlways returned when the token authenticates a user.
email, emailVerifiedemail (also granted by email:verified or user:manage).
phone, phoneVerifiedphone (also granted by phone:verified or user:manage). Available but not selected in the example above — add them to the selection set when you request a phone scope.
givenName, familyName, namename or profile.
avataravatar or profile.

If the mutation fails, user is null and userErrors is populated. A non-null user does not by itself imply that any specific field resolved — individual fields can still be null if the issued token didn't grant the gating scope.

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

GraphQL Variables

{
"consentToken": "<shopConsentToken from the Sign in with Shop 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 tokens server-side. Never expose them to the storefront, browser, Shopify Function, or Checkout UI extension, and never log the access or refresh token. Store refresh tokens encrypted at rest, and scope them per user.


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 scope gating that applies to the user payload on fetchTokensForUser applies here — a field whose gating scope wasn't granted on the access token resolves to null.

GraphQL Query

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

If your access token doesn't authenticate a user, 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 per-field scope gating.

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



Was this page helpful?