This tutorial shows how to create a login button on a Hydrogen storefront that lets a customer authenticate using the Customer Account API. If the login succeeds, then the site displays a welcome message with their email address.
## What you'll learn
After you've completed this tutorial, you'll be able to authenticate a Customer and make Customer Account API queries within a Hydrogen storefront.
## Requirements
- You have completed the [Getting started with the Customer Account API](/docs/storefronts/headless/building-with-the-customer-account-api/getting-started) guide.
- You have completed [Getting started with Hydrogen](/docs/storefronts/headless/hydrogen/getting-started) guide.
- You've installed the [Hydrogen](https://apps.shopify.com/hydrogen) or [Headless](https://apps.shopify.com/headless) sales channel.
## Limitations
- [Multipass](https://shopify.dev/docs/api/multipass) currently doesn't support Customer Account API. If you require single sign-on from an external website in your storefront, then you should use the code in our [multipass example](https://github.com/Shopify/hydrogen/tree/main/examples/multipass), which uses the legacy customer account flow.
- [mock.shop](https://mock.shop/), which provides example product data, doesn't support the Customer Account API. You must use a production store's credentials to work with the Customer Account API.
## Step 1: Set up a public domain for local development
Customer Account API authentication doesn't support the use of `localhost` due to [security concerns](https://datatracker.ietf.org/doc/html/rfc8252#section-8.3). For development purposes, use a tunnelling service, such as [ngrok](https://ngrok.com/).
In this step, you'll learn how to use [ngrok](https://ngrok.com/) to set up a public HTTPS domain that connects to your local Hydrogen application.
### Set up ngrok
Install and run ngrok in your development environment.
1. Set up an [ngrok](https://ngrok.com/) account.
1. In your ngrok settings, [add a static domain](https://ngrok.com/blog-post/free-static-domains-ngrok-users).
1. Install the [ngrok CLI](https://ngrok.com/download).
1. In a terminal, start ngrok using the following command:
### Add your ngrok domain to the content security policy
Modify your Hydrogen app’s [content security policy](/docs/storefronts/headless/hydrogen/content-security-policy) to allow the development domain as a `connect-src`. Your content security policy is typically located in `/app/entry.server.tsx`.
## Step 2: Set up the environment
Configure the necessary Customer Account API settings in the Shopify admin so you can send the initial authentication request to Shopify.
### Open the Customer Account API settings
1. In the Shopify admin, open the **Hydrogen** sales channel.
1. Click the storefront you're adding the customer account API functionality for.
1. Click **Storefront settings**.
1. Click **Customer Account API** to open the API settings.
### Update the application setup
For the Customer Account API to recognize your domain as a valid authentication host, edit your Customer Account API settings.
1. Under **Application setup**, click **Edit** `✎` to edit the endpoints.
1. Under **Callback URI(s)**, click **Add Callback URI**, and add your ngrok domain, with `/account/authorize` appended:
This is the URI your application will redirect to to continue the OAuth process after a successful customer login.
1. Under **JavaScript origin(s)**, click **Add origin**, and then add your ngrok domain.
1. Under **Logout URI**, click **Add Logout URI**, and then add your ngrok domain.
> Tip
> If you don't see **JavaScript origin(s)** and **Logout URI** options, you'll need to switch to a **Public** client type. You can find this option at the top of the **Customer Account API** settings.
### Set up the environment variables
There is only one environment variable needed to set up Customer Account API in your application:
**`PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID`**: A token prefixed with `shp_` that represents a client secret used in all authentication requests. You can retrieve the token by navigating to the **Customer Account API** settings page > **Customer Account API Credentials**
#### Production storefront
When deploying to Oxygen, these variables are automatically created and used in your production environment.
#### Local development
When developing Hydrogen locally, store your [environment variables](/docs/storefronts/headless/hydrogen/environments#environment-variables) in an `.env` file. You can automatically download the required variables with Shopify CLI:
1. Run [`npx shopify hydrogen link`](/docs/api/shopify-cli/hydrogen/hydrogen-link) in your Hydrogen project to link it to your Shopify store.
1. Run [`npx shopify hydrogen env pull`](/docs/api/shopify-cli/hydrogen/hydrogen-env-pull) to download your environment variables and write them to your local `.env` file.
## Step 3: Create the Customer Account API client
> Note
> The Skeleton template version 2024.7.5 and higher has a Customer Account API client by default and you can skip this step. Check `package.json` to see your Skeleton template version.
If you need to manually create a Customer Account API client, then complete the following steps:
Create a new Customer Account API client in your [`server`](/docs/storefronts/headless/hydrogen/fundamentals#project-structure) file using the [`createCustomerAccountClient`](/docs/api/hydrogen/latest/utilities/createcustomeraccountclient) utility.
Pass the new client to [`createCartHandler`](/docs/api/hydrogen/latest/utilities/createcarthandler) to ensure that the logged-in customer is persisted from your store through to checkout.
Pass the new client to the application's [`context`](https://remix.run/docs/en/main/route/loader#context) so the utility can be accessed throughout the application.
> Note
> The Customer Account API client uses the latest version of the API by default. If you need to use a specific version, then you can specify the version when you create the client.
[`createCustomerAccountClient`](docs/api/hydrogen/latest/utilities/createcustomeraccountclient) expects a [session](https://remix.run/docs/en/main/utils/sessions), implemented based on `HydrogenSession`, to persist auth tokens and the customer's logged-in state.
You can view an example of a Hydrogen session in the [Hydrogen GitHub repo](https://github.com/Shopify/hydrogen/blob/main/templates/skeleton/app/lib/session.ts).
## Step 4: Create auth routes
Your application requires three routes for customer login and logout operations.
The default routes are as follows:
- `/account/login`: A route that redirects the user to a Shopify login.
- `/account/authorize`: A route that authorizes the customer after they log in.
- `/account/logout`: A route that logs the customer out.
> Tip
> If you chose to scaffold routes when creating your app, then your app already has the required routes in place. To generate a set of standard routes, including basic account-related functionality, run `npx shopify hydrogen setup`.
### Create the login route
Follow the following steps to create a customer login route in your Hydrogen storefront:
1. In the `routes` folder, create a new file called `account_.login.[js|ts]`.
2. In the new file, add the `context.customerAccount.login()` function in the loader.
This function is responsible for redirecting users to Shopify to log in.
Note the use of underscore in `account_.login.ts`. This is to ensure that [no layout](https://remix.run/docs/en/main/file-conventions/routes#nested-urls-without-layout-nesting) is rendered when this route is accessed.
> Tip:
> If you need to override the default behavior or change the login route location, then you can implement a [`customAuthStatusHandler`](docs/api/hydrogen/latest/utilities/createcustomeraccountclient#createcustomeraccountclientoptions-propertydetail-customauthstatushandler). Review an [example implementation](/docs/api/hydrogen/latest/utilities/createcustomeraccountclient#examples).
### Create the authorization route
Follow the following steps to create an auth route in your Hydrogen storefront:
1. In the `routes` folder, create a new file called `account_.authorize.[js|ts]`.
2. In the new file, add the `context.customerAccount.authorize()` function in the loader.
After a successful login, Shopify redirects to this authorize route. It continues the OAuth process, exchanges the access token, and persists the result to your application session.
If you choose to place this route somewhere else in the application, then use the [`authUrl`](docs/api/hydrogen/latest/utilities/createcustomeraccountclient#createcustomeraccountclientoptions-propertydetail-authurl) option with a relative url, and add the full public domain auth path in the Callback URI of the Customer Account API [application setup](#update-the-application-setup)
At the end of this authorization step, the application redirects back to the page that initiated the login.
Use the [`customAuthStatusHandler`](/docs/api/hydrogen/latest/utilities/createcustomeraccountclient#createcustomeraccountclientoptions-propertydetail-customauthstatushandler) option to change this behavior.
### Create the logout route
Follow the following steps to create a logout route in your Hydrogen storefront:
1. In the `routes` folder, create a new file called `account_.logout.[js|ts]`.
2. In the new file, add the `context.customerAccount.logout()` function in the action. Avoid including this function in the loader.
The logout action should be triggered by a user event, like clicking a logout button, not when a component is being loaded that can occur by page load or prefetching. This is because logging out is a significant action that can disrupt the user's workflow, so it should only happen when the user explicitly requests it.
You can set up a redirect that takes place after the logout step using the `admin` setting in the [application setup step](#update-the-application-setup).
## Step 5: Query the Customer Account API
After you've set up your auth routes, you can start querying the Customer Account API.
In this step, you'll create a new `account` route that queries for a logged in customer's first and last name.
1. In the `routes` folder, create a new file called `account.[jsx|tsx]`.
2. Add the following code.
This code fetches the customer's first and last name from their account. If the customer isn't logged in, calling `query()` will trigger an automatic redirect to the login page, and redirect back to current page at the end of the auth process.
> Tip
> You need to commit the session at the end of a loader/action with any customer logged in check.
>
> A logged in check can trigger an access token refresh, which won't persist in your application unless session is committed to the `Set-Cookie` header.
The `query` and `mutate` functions follow GraphQL standards and return both `data` and `errors` objects. The `errors` object typically returns GraphQL errors such as a syntax error for querying for unknown field name.
Most of the time, the existence of the `errors` object means that the query isn't successful and there is nothing to show. However, during a mutation, it is possible to receive partial data while still experiencing errors.
You should always handle errors gracefully, either by showing a message to the user, or re-throwing them for your application's `ErrorBoundary` to catch.
> Warning
> Never cache Customer Account API data or store personally identifiable information (PII). Caching this type of data causes risk of unauthorized access or data breaches, compromising user privacy and security.
## Step 6: Check the customer logged in state
You can check whether a visitor is logged in without triggering an automatic login redirect. For instance, you might want to conditionally display a **Log in** or **Account details** link in a menu.
1. In the root file of your application, add `customerAccount.isLoggedIn()` in the loader.
2. Return this promise using Remix's [deferred data loading](https://remix.run/docs/en/main/discussion/pending-ui#deferred-data-loading) pattern. This allows the user interface to render before the login check is complete.
Note that you need to `commit()` the session at the end of any loader or action that checks a customer's logged-in state. This is because a logged-in check can trigger an access token refresh, which won't persist in your application unless the session is committed to the `Set-Cookie` header.
## Step 7: Associate a customer with a cart
You can associate a customer with a cart by obtaining a Storefront API [`CustomerAccessToken`](/docs/api/storefront/latest/objects/customeraccesstoken).
1. Update the Customer Account API client to use the [`unstableB2b` option](/docs/api/hydrogen/latest/utilities/createcustomeraccountclient#createcustomeraccountclientoptions-propertydetail-unstableb2b).
1. Access the [`CustomerAccessToken`](/docs/api/storefront/latest/objects/customeraccesstoken) and pass it to the `cart.updateBuyerIdentity` function.
> Note
> The `CustomerAccessToken` returned by Customer Account Client can only be used to update the buyer identity of a cart. It cannot be used with a Storefront API `customer` query.
## Next steps
- Explore the [GraphQL Customer Account API](/docs/api/customer) reference.
- Explore the [`createCustomerAccountClient`](/docs/api/hydrogen/latest/utilities/createcustomeraccountclient) reference, including additional examples.
- Explore an end-to-end Customer Account API implementation example in the [default Hydrogen template](https://github.com/Shopify/hydrogen/tree/main/templates/skeleton/app/routes/).
- Learn how to [Manage customer accounts with the Customer Account API](/docs/storefronts/headless/building-with-the-customer-account-api/customer-accounts)