> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tybritelabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticated Session

> Reference for the AuthenticationService class in the Tybrite SDK.

The `AuthenticationService` (accessed via `client.authentication`) is the **gateway to obtaining customer session tokens**. It handles customer registration, login, passwordless flows, password management, and session refresh.

Every customer-scoped operation across the SDK — on `client.customers`, `client.cartWishlist`, `client.giftCards`, and `client.messaging` — requires the `access_token` minted here to be passed as the `xAuthToken` header.

<Note>
  **Already using your own auth provider?** If your storefront authenticates through Auth0, Clerk, Cognito, Firebase, NextAuth, SSO, or any other external identity system, you don't need this service. Use [`CustomersService`](./CustomersService) to provision Galactic Core customer records directly from your backend. See [Customers and Auth](../../customers-and-auth) for the decision matrix.
</Note>

## The Customer Session Lifecycle

```
1. Customer authenticates (login | verifyOtp | register)
   ↓
2. Backend returns session.access_token (JWT) + refresh_token
   ↓
3. Storefront stores access_token (localStorage / httpOnly cookie)
   ↓
4. Every customer-scoped API call (cart, wishlist, customers, messaging, gift-cards)
   passes the access_token as xAuthToken
   ↓
5. When token nears expiry → refreshToken → swap stored access_token
   ↓
6. Customer logs out → clear stored tokens
```

<Note>
  Access tokens are JWTs valid for **1 hour**. Refresh tokens are long-lived but single-use — each `refreshToken` call returns a brand-new refresh token that supersedes the previous one.
</Note>

***

## Core Authentication

### `register`

Create a new customer account. On success, returns **`201 Created`** along with session tokens so you can log the user in immediately.

Pass `marketing_consent: true` when the sign-up form includes a marketing opt-in box and the customer checked it. It defaults to `false` (opt-in) — a customer is never subscribed to marketing without affirmative consent. The customer can change this later via `updateCustomer`.

```typescript theme={null}
const { user, customer, session, is_multi_store_customer } = await client.authentication.register({
  requestBody: {
    email: 'jane.doe@example.com',
    password: 'securePassword123',
    name: 'Jane Doe',          // optional
    phone: '+12125550142',     // optional
    marketing_consent: true    // optional — they opted in to marketing
  }
});

if (is_multi_store_customer) {
  console.log('Linked existing Tybrite identity to this store');
} else {
  console.log('Created a brand-new login identity');
}
```

```json Response (201) theme={null}
{
  "message": "Registration successful",
  "user": {
    "id": "df648f68-39b3-4ba6-9be2-ff0a9393c02b",
    "email": "jane.doe@example.com",
    "email_confirmed": true
  },
  "customer": {
    "id": "15ecd58f-a8f8-49e4-9098-96f1fa8308e9",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "phone": "+12125550142",
    "tier": "bronze",
    "created_at": "2026-06-20T11:36:54.174303+00:00"
  },
  "session": {
    "access_token": "eyJ...<redacted>.signature",
    "refresh_token": "v1.MROK...<redacted>",
    "expires_in": 3600,
    "expires_at": 1781959013
  }
}
```

**Response shape:**

| Field                     | Type      | Description                                                                                         |
| ------------------------- | --------- | --------------------------------------------------------------------------------------------------- |
| `user`                    | `object`  | The login identity record (id, email, metadata).                                                    |
| `customer`                | `object`  | The store-scoped customer profile row.                                                              |
| `session`                 | `object`  | `{ access_token, refresh_token, expires_in, expires_at }`                                           |
| `is_multi_store_customer` | `boolean` | `true` when an existing Tybrite identity (from another store) was linked rather than newly created. |

<Warning>
  If an account already exists **for this store** with the given email, `register` returns **`409 Conflict`** — distinct from a generic `400` validation error. Surface this to your UI so you can route the user to `/login` instead.
</Warning>

***

### `login`

Authenticate an existing customer using their email and password. Returns **`200 OK`**.

```typescript theme={null}
const { message, user, customer, session } = await client.authentication.login({
  requestBody: {
    email: 'jane.doe@example.com',
    password: 'securePassword123'
  }
});

// Persist for subsequent customer-scoped calls
localStorage.setItem('tybrite_access_token', session.access_token);
localStorage.setItem('tybrite_refresh_token', session.refresh_token);
```

**Session object:**

```json theme={null}
{
  "access_token": "eyJhbGciOi...",
  "refresh_token": "v1.MROK...",
  "expires_in": 3600,
  "expires_at": 1747700000
}
```

***

### `logout`

Invalidate the current session. Revokes both the access and refresh tokens on the server. Returns **`200 OK`**.

```typescript theme={null}
await client.authentication.logout();
// → { message: 'Logout successful' }

// Then clear local storage
localStorage.removeItem('tybrite_access_token');
localStorage.removeItem('tybrite_refresh_token');
```

***

## Passwordless Flows (Magic Links & OTP)

### `sendMagicLink`

Send a passwordless authentication link **and** a 6-digit OTP code to the customer's email. Codes are valid for **15 minutes**. Returns **`200 OK`** with a hint to check email.

```typescript theme={null}
const { message } = await client.authentication.sendMagicLink({
  requestBody: {
    email: 'jane.doe@example.com'
  }
});
// message → "Check your email for the magic link / OTP"
```

***

### `verifyOtp`

Verify the 6-digit OTP code emitted by `sendMagicLink` to complete authentication. Returns **`200 OK`** with the **same response shape as `login`** (`message`, `user`, `customer`, `session`).

```typescript theme={null}
const { user, customer, session } = await client.authentication.verifyOtp({
  requestBody: {
    email: 'jane.doe@example.com',
    token: '123456' // The 6-digit code from email
  }
});
// → same shape as login: { message, user, customer, session }
//   session.access_token is the JWT you pass as xAuthToken thereafter
```

***

## Password Management

### `resetPassword`

Trigger a password reset email for a customer who has forgotten their password. Returns **`200 OK`**.

```typescript theme={null}
await client.authentication.resetPassword({
  requestBody: {
    email: 'jane.doe@example.com',
    redirect_to: 'https://shop.example.com/reset-callback' // optional
  }
});
// → { message: 'Password reset email sent', email: 'jane.doe@example.com' }
```

***

### `updatePassword`

Update the password for the currently authenticated user. Returns **`200 OK`**.

<Warning>
  `xAuthToken` is **mandatory** here and must be the customer's **own** session JWT (the `access_token` returned from `login` / `verifyOtp` / `register`). Server keys cannot stand in — this endpoint mutates the customer's login credentials and requires per-customer authorization.
</Warning>

```typescript theme={null}
const token = localStorage.getItem('tybrite_access_token')!;

await client.authentication.updatePassword({
  xAuthToken: token,
  requestBody: {
    password: 'newSecurePassword456'
  }
});
// → {
//     message: 'Password updated successfully',
//     user: { id: 'df648f68-39b3-4ba6-9be2-ff0a9393c02b', email: 'jane.doe@example.com' }
//   }
```

***

## Session Utilities

### `refreshToken`

Exchange a refresh token for a fresh access token. Returns **`200 OK`** with a brand-new session object (including a rotated `refresh_token`).

```typescript theme={null}
const { session } = await client.authentication.refreshToken({
  requestBody: {
    refresh_token: localStorage.getItem('tybrite_refresh_token')!
  }
});
// → { message: 'Token refreshed successfully', session: {
//       access_token: 'eyJ...<redacted>.signature',
//       refresh_token: 'v1.MROK...<redacted>',  // rotated — supersedes the old one
//       expires_in: 3600,
//       expires_at: 1781966861
//   } }

localStorage.setItem('tybrite_access_token', session.access_token);
localStorage.setItem('tybrite_refresh_token', session.refresh_token);
```

<Tip>
  Schedule the refresh \~60 seconds before `expires_at` rather than waiting for a `401` on a customer-scoped call. This avoids cart/wishlist write failures during checkout.
</Tip>

***

### `getCurrentUser`

Retrieve the lightweight identity of the currently authenticated user — useful right after login to confirm "who am I?" Also routed as `GET /v1/auth/me`. Returns **`200 OK`** with `{ user, customer }`.

<Warning>
  `xAuthToken` is **mandatory** and must be the customer's **own** session JWT. This endpoint resolves "who am I?" from the JWT alone — passing the wrong token returns a different customer's record (or `401`).
</Warning>

```typescript theme={null}
const token = localStorage.getItem('tybrite_access_token')!;

const { user, customer } = await client.authentication.getCurrentUser({
  xAuthToken: token
});

console.log(`Logged in as: ${user.email} (customer id: ${customer.id})`);
```

```json Response (200) theme={null}
{
  "user": {
    "id": "df648f68-39b3-4ba6-9be2-ff0a9393c02b",
    "email": "jane.doe@example.com",
    "email_confirmed": true,
    "created_at": "2026-06-20T11:36:53.593963Z"
  },
  "customer": {
    "id": "15ecd58f-a8f8-49e4-9098-96f1fa8308e9",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "phone": "+12125550142",
    "tier": "bronze",
    "total_purchases": 0,
    "created_at": "2026-06-20T11:36:54.174303+00:00"
  }
}
```

<Note>
  **Looking for the full customer profile?** `getCurrentUser` returns just enough to identify the customer (id, email, basic fields). For the complete profile — addresses, store metrics, purchase history, status — call [`CustomersService.getCustomer`](./CustomersService#getcustomer). The same `xAuthToken` works there. Profile *edits* (address change, name change, etc.) also live on `CustomersService` via [`updateCustomer`](./CustomersService#updatecustomer).
</Note>

***

## Full Lifecycle Example

This walks the customer from sign-in, through a cart mutation and checkout, to logout — showing where `xAuthToken` flows.

```typescript theme={null}
import { Tybrite } from '@tybrite-labs/sdk';

const client = new Tybrite({ apiKey: process.env.TYBRITE_PUBLISHABLE_KEY });

// 1. LOGIN — mint the session
const { customer, session } = await client.authentication.login({
  requestBody: {
    email: 'jane.doe@example.com',
    password: 'securePassword123'
  }
});
// → { message: 'Login successful', user, customer, session }

const token = session.access_token;

// 2. CART — customer-scoped operations carry xAuthToken
await client.cartWishlist.addToCart({
  xAuthToken: token,
  requestBody: {
    customer_id: customer.id,
    variant_id: '9a47e047-b1b6-4c35-9617-820629e22e04', // Sony WH-1000XM4 default variant
    quantity: 2
  }
});
// → { items: [ { product_name, variant_name, quantity, unit_price, total_price, ... } ] }

// 3. CHECKOUT — order creation is sk-only + idempotent + HMAC-signed
//    (uses your server-side secret key, not the customer JWT)
const order = await serverClient.orders.createOrder({
  idempotencyKey: crypto.randomUUID(),
  xTimestamp: timestamp,
  xSignature: signature,
  requestBody: { customer_id: customer.id, items: [...] }
});
// → { data: { id, order_number, payment_status: 'paid', total, ... } }

// 4. REFRESH — if the access_token is near expiry
const { session: refreshed } = await client.authentication.refreshToken({
  requestBody: { refresh_token: session.refresh_token }
});
// → { message: 'Token refreshed successfully', session: { access_token, refresh_token, ... } }

// 5. LOGOUT — revoke tokens server-side
await client.authentication.logout();
// → { message: 'Logout successful' }
```

***

### Response Codes

| Method           | Success         | Notable Errors                                                            |
| ---------------- | --------------- | ------------------------------------------------------------------------- |
| `register`       | **201 Created** | `400` validation, **`409 Conflict`** account already exists at this store |
| `login`          | **200 OK**      | `401` invalid credentials                                                 |
| `logout`         | **200 OK**      | —                                                                         |
| `sendMagicLink`  | **200 OK**      | `400` invalid email                                                       |
| `verifyOtp`      | **200 OK**      | `400` invalid/expired code                                                |
| `resetPassword`  | **200 OK**      | `400` invalid email                                                       |
| `updatePassword` | **200 OK**      | `401` missing or invalid `xAuthToken`                                     |
| `refreshToken`   | **200 OK**      | `401` invalid/revoked refresh token                                       |
| `getCurrentUser` | **200 OK**      | `401` missing or invalid `xAuthToken`                                     |

<Note>
  **Token Lifetime:** Access tokens are valid for 1 hour. We recommend implementing an interceptor in your application to automatically call `refreshToken` before the access token expires.
</Note>
