Authentication Flow
Implement the complete user authentication lifecycle.
What you'll build
A full auth integration from scratch: register a new account, verify the phone number, log in with credentials, fetch and update the user profile, recover a forgotten password, and log out — with proper JWT token handling throughout.
Prerequisites
- A Store UUID — e.g.
e87437f2-3e35-4738-af5e-6307e368255c(staging) - cURL or any HTTP client
Registration and login do not require a token. Authenticated endpoints (profile, logout) require the JWT returned by login.
Step 1: Register a New User
Create a new account. The response includes a JWT token so the user is immediately logged in after registration.
cURL
curl -X POST https://ecom-api.staging.blaze.me/api/v1/auth/register \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"email": "jane@example.com",
"phone_number": "+15551234567",
"password": "securepassword123",
"password_confirmation": "securepassword123",
"first_name": "Jane",
"last_name": "Smith",
"date_of_birth": 631152000000,
"zip_code": "90001",
"marketing_email_opt_in": true,
"marketing_sms_opt_in": false
}
}
}'
JavaScript (fetch)
const STORE_UUID = "e87437f2-3e35-4738-af5e-6307e368255c";
const BASE_URL = "https://ecom-api.staging.blaze.me";
const headers = {
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
"X-Store": STORE_UUID,
};
const registerRes = await fetch(`${BASE_URL}/api/v1/auth/register`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: {
email: "jane@example.com",
phone_number: "+15551234567",
password: "securepassword123",
password_confirmation: "securepassword123",
first_name: "Jane",
last_name: "Smith",
date_of_birth: 631152000000,
zip_code: "90001",
marketing_email_opt_in: true,
marketing_sms_opt_in: false,
},
},
}),
});
const { data: newUser } = await registerRes.json();
const token = newUser.attributes.token;
console.log(`Registered as ${newUser.attributes.email}`);
console.log(`JWT: ${token.substring(0, 20)}...`);
Response (201 Created)
{
"data": {
"id": "d4b2e6f3-9c5a-4d7b-8e1f-2a4c6d8e0f1a",
"type": "users",
"attributes": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith"
}
}
}
Error: Email Already Exists
{
"errors": [
{
"code": "email_already_exists",
"status": "400",
"detail": "Email is already in use. Please contact Retailer Support.",
"source": { "pointer": "/data/attributes/email" }
}
]
}
Note: If the store has
phone_verification_requiredenabled, registration will return aphone_number_requires_confirmationerror — proceed to Step 2.
Step 2: Phone Verification
If phone verification is required, request a code and then submit it.
2a: Request Verification Code
curl -X POST https://ecom-api.staging.blaze.me/api/v1/auth/verification \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"phone_number": "+15551234567"
}
}
}'
const verifyRes = await fetch(`${BASE_URL}/api/v1/auth/verification`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: { phone_number: "+15551234567" },
},
}),
});
const { data: notification } = await verifyRes.json();
console.log(notification.attributes.message);
// "Verification code sent to +1***1234567"
Response
{
"data": {
"id": "notif-001",
"type": "notifications",
"attributes": {
"message": "Verification code sent to +1***1234567",
"level": "info"
}
}
}
2b: Submit Verification Code
curl -X POST https://ecom-api.staging.blaze.me/api/v1/auth/verification-check \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"phone_number": "+15551234567",
"code": "123456",
"password": "securepassword123",
"password_confirmation": "securepassword123",
"first_name": "Jane",
"last_name": "Smith"
}
}
}'
const checkRes = await fetch(`${BASE_URL}/api/v1/auth/verification-check`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: {
phone_number: "+15551234567",
code: "123456",
password: "securepassword123",
password_confirmation: "securepassword123",
first_name: "Jane",
last_name: "Smith",
},
},
}),
});
const { data: verified } = await checkRes.json();
const token = verified.attributes.token;
console.log(`Verified! JWT: ${token.substring(0, 20)}...`);
Response
{
"data": {
"id": "d4b2e6f3-9c5a-4d7b-8e1f-2a4c6d8e0f1a",
"type": "users",
"attributes": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}
Error: Invalid Code
{
"errors": [
{
"code": "invalid_verification_code",
"status": "400",
"detail": "Invalid verification code",
"source": { "pointer": "/data/attributes/code" }
}
]
}
Step 3: Login
Authenticate with email (or phone) and password to get a JWT token.
cURL
curl -X POST https://ecom-api.staging.blaze.me/api/v1/auth/login \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"email": "jane@example.com",
"password": "securepassword123"
}
}
}'
JavaScript (fetch)
const loginRes = await fetch(`${BASE_URL}/api/v1/auth/login`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: {
email: "jane@example.com",
password: "securepassword123",
},
},
}),
});
if (!loginRes.ok) {
const { errors } = await loginRes.json();
console.error("Login failed:", errors[0].detail);
// "Wrong Credentials"
} else {
const { data: user } = await loginRes.json();
const token = user.attributes.token;
// Store the token for authenticated requests
console.log(`Logged in as ${user.attributes.first_name}`);
console.log(`JWT: ${token.substring(0, 20)}...`);
}
Response (200 OK)
{
"data": {
"id": "c3a1f5d2-8b4e-4f6a-9c2d-1a3b5c7d9e0f",
"type": "users",
"attributes": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith"
}
}
}
Error: Wrong Credentials
{
"errors": [
{
"code": "bad_login",
"status": "401",
"detail": "Wrong Credentials",
"source": { "pointer": "/data/attributes/email" }
}
]
}
Step 4: Get User Profile
Fetch the authenticated user's profile. Requires the JWT token from login.
cURL
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X GET https://ecom-api.staging.blaze.me/api/v1/users/me \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-H "Authorization: Bearer $TOKEN"
JavaScript (fetch)
// Create authenticated headers
const authHeaders = {
...headers,
Authorization: `Bearer ${token}`,
};
const profileRes = await fetch(`${BASE_URL}/api/v1/users/me`, {
headers: authHeaders,
});
const { data: profile } = await profileRes.json();
console.log(`Name: ${profile.attributes.first_name} ${profile.attributes.last_name}`);
console.log(`Email: ${profile.attributes.email}`);
console.log(`Phone: ${profile.attributes.phone_number}`);
console.log(`Zip: ${profile.attributes.zip_code}`);
console.log(`Type: ${profile.attributes.customer_type}`);
console.log(`Confirmed: ${profile.attributes.is_confirmed}`);
Response
{
"data": {
"id": "c3a1f5d2-8b4e-4f6a-9c2d-1a3b5c7d9e0f",
"type": "users",
"attributes": {
"email": "jane@example.com",
"phone_number": "+15551234567",
"first_name": "Jane",
"last_name": "Smith",
"zip_code": "90001",
"address": null,
"billing_address": null,
"date_of_birth": 631152000000,
"marketing_email_opt_in": true,
"marketing_sms_opt_in": false,
"medical_id": null,
"drivers_license_id": null,
"customer_type": "recreational",
"is_confirmed": true,
"is_active": true,
"token": null
}
}
}
Step 5: Update User Profile
Update profile fields. Only provided fields are changed.
cURL
curl -X PUT https://ecom-api.staging.blaze.me/api/v1/users/me \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"data": {
"type": "users",
"attributes": {
"zip_code": "90210",
"marketing_email_opt_in": true
}
}
}'
JavaScript (fetch)
const updateRes = await fetch(`${BASE_URL}/api/v1/users/me`, {
method: "PUT",
headers: authHeaders,
body: JSON.stringify({
data: {
type: "users",
attributes: {
zip_code: "90210",
marketing_email_opt_in: true,
},
},
}),
});
const { data: updated } = await updateRes.json();
console.log(`Zip updated to: ${updated.attributes.zip_code}`); // "90210"
Response
{
"data": {
"id": "c3a1f5d2-8b4e-4f6a-9c2d-1a3b5c7d9e0f",
"type": "users",
"attributes": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"zip_code": "90210",
"marketing_email_opt_in": true,
"marketing_sms_opt_in": false,
"is_confirmed": true,
"is_active": true
}
}
}
Partial update: Use
PATCHinstead ofPUTfor the same behavior — only the fields you send are updated.
Step 6: Password Recovery
If the user forgets their password, request a reset link via email.
6a: Request Recovery Email
curl -X POST https://ecom-api.staging.blaze.me/api/v1/auth/recover_password \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"email": "jane@example.com"
}
}
}'
const recoverRes = await fetch(`${BASE_URL}/api/v1/auth/recover_password`, {
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: { email: "jane@example.com" },
},
}),
});
const { data: recovery } = await recoverRes.json();
console.log(recovery.attributes.message);
// "Password recovery instructions have been sent."
Response
{
"data": {
"id": "notif-002",
"type": "notifications",
"attributes": {
"message": "Password recovery instructions have been sent.",
"level": "info"
}
}
}
6b: Reset Password with Token
The user receives an email with a reset token. Use it to set a new password.
RESET_TOKEN="abc123def456"
curl -X POST "https://ecom-api.staging.blaze.me/api/v1/auth/reset_password/$RESET_TOKEN" \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-d '{
"data": {
"type": "users",
"attributes": {
"password": "newSecurePassword456",
"password_confirmation": "newSecurePassword456",
"email": "jane@example.com"
}
}
}'
const resetToken = "abc123def456"; // from the recovery email
const resetRes = await fetch(
`${BASE_URL}/api/v1/auth/reset_password/${resetToken}`,
{
method: "POST",
headers,
body: JSON.stringify({
data: {
type: "users",
attributes: {
password: "newSecurePassword456",
password_confirmation: "newSecurePassword456",
email: "jane@example.com",
},
},
}),
}
);
const { data: reset } = await resetRes.json();
const newToken = reset.attributes.token;
console.log(`Password reset! New JWT: ${newToken.substring(0, 20)}...`);
Response
{
"data": {
"id": "c3a1f5d2-8b4e-4f6a-9c2d-1a3b5c7d9e0f",
"type": "users",
"attributes": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}
Token expired? If the reset token has expired, you'll get a
400with codeinvalid_token. The user must request a new recovery email.
Step 7: Logout
Invalidate the current session and JWT token.
cURL
curl -X DELETE https://ecom-api.staging.blaze.me/api/v1/auth/logout \
-H "Content-Type: application/vnd.api+json" \
-H "Accept: application/vnd.api+json" \
-H "X-Store: e87437f2-3e35-4738-af5e-6307e368255c" \
-H "Authorization: Bearer $TOKEN"
JavaScript (fetch)
const logoutRes = await fetch(`${BASE_URL}/api/v1/auth/logout`, {
method: "DELETE",
headers: authHeaders,
});
const { data: logoutData } = await logoutRes.json();
console.log(logoutData.message); // "Logged out successfully"
// Clear the stored token
// token = null;
Response
{
"data": {
"message": "Logged out successfully"
}
}
JWT Token Usage Pattern
Here's a reusable auth helper for your storefront:
const STORE_UUID = "e87437f2-3e35-4738-af5e-6307e368255c";
const BASE_URL = "https://ecom-api.staging.blaze.me";
class AuthClient {
constructor(storeUuid) {
this.baseUrl = BASE_URL;
this.storeUuid = storeUuid;
this.token = null;
}
get headers() {
const h = {
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
"X-Store": this.storeUuid,
};
if (this.token) {
h["Authorization"] = `Bearer ${this.token}`;
}
return h;
}
async register(attrs) {
const res = await fetch(`${this.baseUrl}/api/v1/auth/register`, {
method: "POST",
headers: this.headers,
body: JSON.stringify({ data: { type: "users", attributes: attrs } }),
});
const { data } = await res.json();
this.token = data.attributes.token;
return data;
}
async login(email, password) {
const res = await fetch(`${this.baseUrl}/api/v1/auth/login`, {
method: "POST",
headers: this.headers,
body: JSON.stringify({
data: { type: "users", attributes: { email, password } },
}),
});
if (!res.ok) throw new Error("Login failed");
const { data } = await res.json();
this.token = data.attributes.token;
return data;
}
async getProfile() {
const res = await fetch(`${this.baseUrl}/api/v1/users/me`, {
headers: this.headers,
});
const { data } = await res.json();
return data;
}
async updateProfile(attrs) {
const res = await fetch(`${this.baseUrl}/api/v1/users/me`, {
method: "PUT",
headers: this.headers,
body: JSON.stringify({ data: { type: "users", attributes: attrs } }),
});
const { data } = await res.json();
return data;
}
async logout() {
await fetch(`${this.baseUrl}/api/v1/auth/logout`, {
method: "DELETE",
headers: this.headers,
});
this.token = null;
}
get isAuthenticated() {
return this.token !== null;
}
}
// Usage
const auth = new AuthClient(STORE_UUID);
await auth.login("jane@example.com", "securepassword123");
console.log(`Authenticated: ${auth.isAuthenticated}`); // true
const profile = await auth.getProfile();
console.log(`Hello, ${profile.attributes.first_name}!`);
await auth.updateProfile({ zip_code: "90210" });
await auth.logout();
console.log(`Authenticated: ${auth.isAuthenticated}`); // false
What's Next?
- Browse products: See the Browsing Flow example
- Create a cart: See the Cart & Checkout guide
- Authentication reference: See the Authentication guide
- User accounts: See the User Accounts guide