Login Customer in the Storefront

Log a customer in with email and password.

On this page

In this guide, you'll learn about the two ways to login a customer in your storefront.

Do not store the JWT in localStorage. After sdk.store.auth.login, write the token to the _aiminaabee_jwt httpOnly cookie, revalidate the customers cache tag, and transfer the guest cart.

ts

tsCopy
"use server"
export async function login(
storeCode: string,
_currentState: unknown,
formData: FormData
) {
const email = formData.get("email") as string
const password = formData.get("password") as string
const token = await sdk.store.auth.login(storeCode, "emailpass", {
email,
password,
})
if (typeof token !== "string") {
return "This login method requires a redirect, which is not supported."
}
await setAuthToken(token)
revalidateTag(await getCacheTag("customers"))
await transferCart(storeCode)
}

setAuthToken sets _aiminaabee_jwt (httpOnly, sameSite: "strict", 7-day maxAge, secure in production). transferCart calls sdk.store.cart.transferCart with the _aiminaabee_cart_id cookie so guest line items move to the customer.

See Build a Next.js Storefront. The rest of this page covers JWT vs session and SPA examples.

Alternative Guides: - This guide covers login using email and password. For authentication with third-party providers, refer to the Third-Party Login guide.

  • If you have email verification enabled, refer to the Verify Account guide instead.

Login Customer Methods

There are two ways to login a customer in your storefront:

  1. Using a JWT token. This JWT token is obtained from the /auth/customer/emailpass API route and is used as a bearer token in the authorization header of all requests.
  2. Using a cookie session. This method uses the /auth/session API route to set the authenticated session ID in the cookies.

The JS SDK simplifies the login approach in a single auth.login method. The upcoming sections explain the authentication approach whether you're using the JS SDK or not.

Which Authentication Method Should You Use?

The authentication method you choose depends on your use case and the type of storefront you're building.

Refer to the JS SDK Authentication guide to learn more about the differences between JWT and session authentication and which one is best for your use case.

JS SDK Authentication Configuration

Before implementing the login flow, you need to configure in the JS SDK the authentication method you're using in your storefront. This defines how the JS SDK will handle sending authenticated requests after the customer is authenticated.

For example, add the following configuration to your JS SDK initialization:

Tip: Learn how to install and configure the JS SDK in the Getting started guide.

JWT

ts

tsCopy
export const sdk = new Aiminaabee({
// ...
auth: {
type: "jwt",
},
})

Session

ts

tsCopy
export const sdk = new Aiminaabee({
// ...
auth: {
type: "session",
},
})

The JS SDK will now pass the JWT token or the session ID cookie in the authorization header of all subsequent requests based on the authentication method you've configured.

Refer to the JS SDK Authentication guide for more information about these configurations, as well as other authentication configurations.

Tip: The JS SDK's default JWT storage is localStorage. On Next.js App Router, skip that default: store the token in _aiminaabee_jwt as shown above. For React Native, use custom SDK storage as described in JS SDK Authentication.


Authentication with JS SDK

The JS SDK provides an auth.login method that handles all authentication steps based on the configured authentication method. Then, all subsequent requests will have the necessary authentication headers or cookies.

For example, to implement the login flow in your storefront with the JS SDK:

React

tsx

tsxCopy
"use client" // include with Next.js 13+
import { useState } from "react"
import { sdk } from "@/lib/sdk"
export default function Login() {
const [loading, setLoading] = useState(false)
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const handleLogin = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
e.preventDefault()
if (!email || !password) {
return
}
setLoading(true)
let token: string | { location: string }
try {
token = await sdk.store.auth.login("us", "emailpass", {
email,
password,
})
} catch (error) {
alert(`An error occurred while logging in: ${error}`)
return
}
if (typeof token !== "string") {
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
// all next requests will be authenticated
const { customer } = await sdk.store.customer.retrieve("us")
console.log(customer)
setLoading(false)
}
return (
<form>
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
name="password"
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<button
disabled={loading}
onClick={handleLogin}
>
Login
</button>
</form>
)
}

JS SDK

ts

tsCopy
const handleLogin = async () => {
let token: string | { location: string }
try {
token = await sdk.store.auth.login("us", "emailpass", {
email,
password,
})
} catch (error) {
alert(`An error occurred while logging in: ${error}`)
return
}
if (typeof token !== "string") {
alert("Authentication requires more actions, which isn't supported by this flow.")
return
}
// all next requests will be authenticated
const { customer } = await sdk.store.customer.retrieve("us")
console.log(customer)
}

In the example above, you:

  1. Create a handleLogin function that logs in a customer.
  2. In the function, you log in the customer using the sdk.auth.login method.
    • If an error occurs, show an alert and exit execution.
    • The method may return an object with a location property. This occurs when using third-party authentication providers. Learn more about implementing third-party authentication in the Third-Party Login guide.
    • Otherwise, the authentication was successful.
  3. All subsequent requests are now authenticated. As an example, you send a request to obtain the logged-in customer's details.

Authentication without JS SDK

If you're not using the JS SDK, the next sections cover the general flow for authenticating a customer in your storefront for both methods.

1. Using a JWT Token

The first authentication approach is to pass an authenticated JWT token in the authorization header of all requests. You can do that by:

  1. Retrieving a JWT token from the /auth/customer/emailpass Authenticate Customer API route:

bash

bashCopy
curl -X POST '{backend_url}/auth/customer/emailpass' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "customer@gmail.com",
"password": "supersecret"
}'
  1. Passing the token in the authorization header of all subsequent requests, as explained in the API reference:

bash

bashCopy
Authorization: Bearer {jwt_token}

Store the JWT based on your platform. On Next.js, use the _aiminaabee_jwt httpOnly cookie. SPAs may use memory or sessionStorage. Avoid localStorage for tokens when you can.

The second authentication approach is to authenticate the customer with a cookie session. You do that by:

  1. Retrieving a JWT token from the /auth/customer/emailpass Authenticate Customer API route:

bash

bashCopy
curl -X POST '{backend_url}/auth/customer/emailpass' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "customer@gmail.com",
"password": "supersecret"
}'
  1. Sending a request to the /auth/session Authentication Session API route passing in the authorization header the token as a Bearer token. This sets the authenticated session ID in the cookies:

bash

bashCopy
curl -X POST '{backend_url}/auth/session' \
-H 'Authorization: Bearer {jwt_token}'
  1. Passing the cookie session ID in all subsequent requests:

cURL

bash

bashCopy
curl '{backend_url}/store/products' \
-H 'Cookie: connect.sid={sid}'

Fetch

ts

tsCopy
fetch(`<BACKEND_URL>/store/products`, {
credentials: "include",
})