Skip to main content
Version: 3.0 (beta)

Manage Account as a Customer

Introduction#

This guide describes how to use the Saleor GraphQL API to register a new account as a customer and do common account management operations.

Multiple channels and users#

Creating a new account requires specifying a channel. It is needed because you can use different communication strategies for each channel (means of contact, templates used, etc.).

User accounts are not restricted to a single channel. So, for example, a user registered in the europe channel can still log in and place an order through the usa channel.

Creating a new customer account#

Depending on the configuration of your Saleor backend instance, registering a new customer account may be a single-step operation or it may require email confirmation to activate the account (see ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL).

Previous orders#

When an account is activated, existing anonymous orders using the same email address are automatically assigned to the new account.

Registration without email confirmation#

To create a new customer account, use the accountRegister mutation. The mutation takes the following input fields:

  • email: user's email address.
  • password: user's password.
  • channel: the slug of the channel used to sign up.
mutation {  accountRegister(    input: {       email: "customer@example.com"      password: "secret"      channel: "default-channel"    }  ) {    accountErrors {      field      code    }    user {      email      isActive    }  }}

As a result, we get data of the newly created user:

{  "data": {    "accountRegister": {      "accountErrors": [],      "user": {        "email": "customer@example.com",        "isActive": true      }    }  }}

The isActive flag informs that the account is active and the user can log in to their account.

Examples above include accountErrors field, which may return any data-level errors. Here is a response that would be returned if there is already account registered for the given email:

{  "data": {    "accountRegister": {      "accountErrors": [        {          "field": "email",          "code": "UNIQUE"        }      ],      "user": null    }  }}

Registration with email confirmation#

Registering an account with email confirmation consists of two steps and it requires you to have a storefront view, where users will be redirected to confirm their email. First, you need to use the accountRegister mutation, which will create an inactive account and send an email with the confirmation link.

The mutation takes the following input fields:

  • email: user's email address.
  • password: user's password.
  • redirectUrl: path to a view where the user should be redirected to confirm their email.
  • channel: the slug of the channel used to sign up.

This example assumes that you're running the storefront locally with the default settings (running on port 3000):

mutation {  accountRegister(    input: {      email: "customer@example.com"      password: "secret"      redirectUrl: "http://localhost:3000/account-confirm/"      channel: "default-channel"    }  ) {    accountErrors {      field      code    }    user {      email      isActive    }  }}

As a result, we get data of the newly created, but yet inactive user:

{  "data": {    "accountRegister": {      "accountErrors": [],      "user": {        "email": "customer@example.com",        "isActive": false      }    }  }}

At the same time the user customer@example.com should receive an email with a confirmation link based on provided redirectUrl path, for example:

http://localhost:3000/account-confirm/?email=customer%40example.com&token=5fc-9f2116f96bdafd612cf4

The link contains two query parameters—email and token—which are required to proceed with the second mutation, confirmAccount:

mutation {  confirmAccount(    email: "customer@example.com"    token: "5fc-9f2116f96bdafd612cf4"  ) {    accountErrors {      field      code    }    user {      email      isActive    }  }}

If the token is valid, the user will be successfully activated:

{  "data": {    "confirmAccount": {      "accountErrors": [],      "user": {        "email": "customer@example.com",        "isActive": true      }    }  }}

Authentication#

Saleor API uses a JSON Web Token authentication mechanism. To avoid sending passwords with each request, you need to first create an access token, and then include it as a header with every GraphQL request.

The authorization header has the following format:

Authorization: JWT <your-access-token>

Architecture#

There are several types of JWT tokens used in the authentication process:

  • Access tokens are short-lived tokens that allow clients to authenticate as a particular user, and to conduct privileged operations. They should not be persisted as leaking an access token is like giving someone else access to your account.

  • Refresh tokens are long-lived tokens that allow you to generate additional access tokens without requiring the user to provide their email and password every time an access token expires. They should be stored in a way that makes them inaccessible to potential attackers. For web clients, the safest way to store them is an HTTP-only secure cookie. For native clients, use the platform's secure keyring where available.

  • CSRF tokens are used to protect against cross-site resource forgery attacks when the refresh token is stored in a cookie. If you use cookies to store the refresh token, persist the CSRF token in another way, for example using the browser's local storage.

Creating a JWT access token#

To create a new JWT token for a user, use the tokenCreate mutation, and provide the user's credentials as the mutation input (email and password). The mutation also generates a refresh token and returns it as a secure, HTTP-only cookie, and as a field (refreshToken) in the response. The refresh token can be used to generate new access token without the need to store the user's password, see refreshing token. As cookies can be subject to cross-site request forgery attacks, a CSRF token is also generated that needs to be stored and presented when refreshing an access token.

For more details about expiration time of the token, see token expiration time.

mutation {  tokenCreate(email: "admin@example.com", password: "admin") {    token    refreshToken    csrfToken    user {      email    }    errors {      field      message    }  }}

A successful response would look like:

{  "data": {    "tokenCreate": {      "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZXhwIjoxNTY2OTEzODc1LCJvcmlnSWF0IjoxNTY2OTEzNTc1fQ.Dw0ccxdxEXsSpM61_Zr_uCyZd-88cNZqM62k_nAjFAE",      "refreshToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1OTM5MzQyMTYsImlhdCI6MTU5MTM0MjIxNiwiZW1haWwiOiJhZG1pbkBleGFtcGxlLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam95TVE9PSIsImlzX3N0YWZmIjp0cnVlLCJpc19zdXBlcnVzZXIiOnRydWV9.CD7JEHc9Og-KFURu6MCR3VnaxISjZfJkqRhx8Y552pU",      "csrfToken": "7Jzm6rCF2r3CGPYtZls1n5Uh2Lb6sA7IlNtsWej0g7ILCHLOy2nPQzz7awDDnUZW",      "user": {        "email": "admin@example.com"      },      "errors": []    }  }}

To use the token and authorize subsequent requests, you need to include it as a HTTP Authorization request header:

Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZXhwIjoxNTY2OTEzODc1LCJvcmlnSWF0IjoxNTY2OTEzNTc1fQ.Dw0ccxdxEXsSpM61_Zr_uCyZd-88cNZqM62k_nAjFAE

Using JWT tokens in Playground#

If you are using Playground to browse the API, you can authorize your requests by providing the JWT token in the HTTP HEADERS tab. To do that, paste the following JSON structure into the tab, replacing the token with your real token:

{  "Authorization": "JWT <your-access-token>"}

Using JWT tokens with cURL#

To authenticate requests sent with cURL, use the -H or --header parameter to pass the JWT token:

curl \  -H "Authorization: JWT <your-token>" \  -H "Content-Type: application/json" \  -X POST \  -d '{"query": "{ me { email } }"}' \  http://localhost:8000/graphql/

Verifying a token#

To verify the token, use the following mutation:

mutation {  tokenVerify(token: "<your-access-token>") {    isValid    accountErrors {      field      code    }  }}
note

You can verify both access tokens and refresh tokens.

A successful response:

{  "data": {    "tokenVerify": {      "isValid": true,      "accountErrors": []    }  }}

Refreshing an access token#

The tokenRefresh mutation will generate a new access token when provided with a valid refresh token. If the refresh token is not provided as an argument, the code will try to read it from a cookie set by the tokenCreate mutation. In that case, matching CSRF token is required.

For more details about expiration time of the refreshToken, see refresh token expiration time.

Refreshing with refresh token as a browser cookie#

The mutation takes the following input fields:

  • csrfToken: the CSRF token used to protect against cross-site resource forgery attacks.
mutation {  tokenRefresh(csrfToken: "<your-csrf-token>") {    token    accountErrors {      code    }  }}

A successful response:

{  "data": {    "tokenRefresh": {      "token": "new-token",      "accountErrors": []    }  }}

Refreshing with refresh token as an input#

The mutation takes the following input fields:

  • refreshToken: the refresh token returned from the tokenCreate mutation.
mutation {  tokenRefresh(refreshToken: "<your-refresh-token>") {    token    accountErrors {      code    }  }}

A successful response:

{  "data": {    "tokenRefresh": {      "token": "new-token",      "accountErrors": []    }  }}

Deactivating all tokens of a particular user#

The tokensDeactivateAll mutation will invalidate all tokens (access and refresh, including the token used to invoke the mutation) that belong to the current user.

mutation {  tokensDeactivateAll {    accountErrors {      field      message      code    }  }}

Accessing information about the authenticated user#

The me query allows you to get information about currently logged in user.

query {  me{    id    email    defaultBillingAddress{      streetAddress1      country {        country      }    }  }}

response

{  "data": {    "me": {      "id": "VXNlcjo1OQ==",      "email": "admin@example.com",      "defaultBillingAddress": {        "streetAddress1": "835 Stacy Underpass Apt. 370",        "country": {          "country": "United States of America"        }      }    }  }}

If the request is missing the authorization header or the token is invalid, the server will respond with null.

Resetting the password#

Resetting the password is a two-step operation. First, you need to call a mutation to send an email with a unique link to reset the password.

The mutation takes the following input fields:

  • email: user's email address.
  • redirectUrl: path to a view where the user should be redirected to reset the password.
mutation {  requestPasswordReset(    email: "customer@example.com"    redirectUrl: "http://localhost:3000/reset-password/"  ) {    accountErrors {      field      code    }  }}

As a result, if there are no errors in the response, the system sends an email to customer@example.com with a link to provide a new password, for example:

http://localhost:3000/reset-password/?email=customer%40example.com&token=5fc-9f2116f96bdafd612cf4

The link contains two query parameters—email and token—which are required to proceed with the second mutation, setPassword.

The mutation takes the following input fields:

  • token: a unique token that was included in the link in the email.
  • email: user's email address.
  • password: the new password.
mutation {  setPassword(    token: "5fc-9f2116f96bdafd612cf4"    email: "customer@example.com"    password: "new-secret"  ) {    accountErrors {      field      code    }  }}

If there are no errors in the response, the password is successfully changed.

Changing the password#

If you wish to change your password as an authenticated customer, use the passwordChange mutation. The mutation takes the following input fields:

  • oldPassword: the current user's password.
  • newPassword: the new password.
mutation {  passwordChange(oldPassword: "secret", newPassword: "new-secret") {    accountErrors {      field      code    }  }}

If no errors were returned, the password was changed successfully.

This mutation requires an authorization header with a valid JWT token.

Changing the email address#

Changing an email address of existing user accounts is a two-step operation. First, you need to call the requestEmailChange mutation.

The mutation takes the following input fields:

  • newEmail: the new email address to set for the account.
  • password: the current user's password.
  • redirectUrl: path to a view where the user should be redirected to confirm the new email address.
mutation {  requestEmailChange(    newEmail: "new-address@example.com"    password: "secret"    redirectUrl: "http://localhost:3000/confirm-email/"  ) {    accountErrors {      field      code    }    user {      email    }  }}

If there are no errors, the mutation sends an email to the newEmail address with a link to confirm the operation. In this example, we also return the email of the current user. As you can see in the response below, the email isn't yet updated.

{  "data": {    "requestEmailChange": {      "accountErrors": [],      "user": {        "email": "admin@example.com"      }    }  }}

The confirmation links an additional query-string parameter token which is required to confirm the operation, e.g.:

http://localhost:3000/confirm-email/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODYxNzY5OTQsIm9sZF9lbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwibmV3X2VtYWlsIjoibmV3LWFkZHJlc3NAZXhhbXBsZS5jb20iLCJ1c2VyX3BrIjoyMX0.aGAo28Ss_zOn_TwAzLCXdY1xENpf_-uw2khORoodKR8

To confirm the operation, we need to use the confirmEmailChange mutation, which accepts the following input:

  • token: a unique token that was included in the link in the email.
mutation {  confirmEmailChange(    token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1ODYxNzY5OTQsIm9sZF9lbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwibmV3X2VtYWlsIjoibmV3LWFkZHJlc3NAZXhhbXBsZS5jb20iLCJ1c2VyX3BrIjoyMX0.aGAo28Ss_zOn_TwAzLCXdY1xENpf_-uw2khORoodKR8"  ) {    accountErrors {      field      code    }    user {      email    }  }}

If the token is valid the email should be now updated:

{  "data": {    "confirmEmailChange": {      "accountErrors": [],      "user": {        "email": "new-address@example.com"      }    }  }}

Both mutation require an authorization header with a valid JWT token.

Deleting the account#

If you wish to remove your own customer account, you can do so by using two mutations. First, you need to request to delete your account with the accountRequestDeletion mutation. This mutation takes the following input:

  • redirectUrl: path to a view where the user can confirm deleting the account.
mutation {  accountRequestDeletion(redirectUrl: "http://localhost:3000/confirm-delete/") {    accountErrors {      field      message      code    }  }}

As a result, if there are no errors, the user receives an email with a link to confirm deleting their account. The link includes the token query parameter that is required in the second mutation:

http://localhost:3000/confirm-delete/?token=5ff-b5818345d8b64331b068

To confirm deleting the account, use the accountDelete mutation which accepts the following input:

  • token: a unique token that was included in the link in the email.
mutation {  accountDelete(token: "5ff-b5818345d8b64331b068") {    accountErrors {      field      message      code    }  }}

As a result, the account is now deleted.

Both mutations require an authorization header with a valid JWT token.