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

# Members and authentication

> Learn how Cashback Hive member sign-in works end to end: the popup flow, where the session token is stored, and how to react to auth:change events.

A **member** is an end user with a Cashback Hive account. When a member is signed in on your page, purchases they make through your tracked links earn them cashback, and you can show them their balances and activity.

## The sign-in flow

```mermaid theme={null}
sequenceDiagram
  participant Page as Your page
  participant SDK
  participant Popup as Sign-in window
  participant API as Cashback Hive

  Page->>SDK: auth.signIn()
  SDK->>Popup: window.open(…/v1/auth?key&origin&return_to)
  Popup->>API: member authenticates
  API-->>Popup: token
  Popup-->>SDK: postMessage({ type: "cbh:auth", token })
  SDK->>SDK: store token in localStorage
  SDK->>API: GET /v1/member (Bearer token)
  API-->>SDK: member
  SDK-->>Page: resolves with member, emits auth:change
```

1. Your code calls `sdk.auth.signIn()` — from a **click handler**, so the browser allows the popup.
2. The SDK opens a popup to the Cashback Hive sign-in page, passing your publishable key, the current origin, and the current URL.
3. The member signs in (or creates an account). The sign-in page sends the token back to your page with `postMessage`. The SDK only accepts messages from the Cashback Hive origin.
4. The SDK stores the token, fetches the member profile, resolves the `signIn()` promise with it, and emits `auth:change`.

### If the popup is blocked

When `window.open` returns `null`, the SDK navigates the **current page** to the sign-in URL instead. After signing in, the member is redirected back to the original URL with the token in the URL fragment (`#cbh_auth=…`). On that page load the SDK consumes the token, strips it from the URL with `history.replaceState`, and signs the member in. In this path the `signIn()` promise never resolves (the page unloads) — your UI should rely on `auth:change` after the reload rather than awaiting the promise.

### If the member closes the window

`signIn()` rejects with an `Error` whose message is `Sign-in window was closed`. Treat this as a cancel, not a failure.

### Sign-in in another tab

The SDK also listens for the `storage` event, so if the member signs in on another tab of your site, the pending `signIn()` on this tab resolves too.

## Token storage

The member token is stored in `localStorage` under the key `cbh:<publishable key>:token`. Consequences:

* Sessions **persist across visits** until the member signs out or the token becomes invalid.
* Sessions are **per origin** (standard `localStorage` scoping) and **per publishable key**.
* If `localStorage` is unavailable (private mode in some browsers, or blocked by policy), the SDK fails silently and the member will need to sign in on each visit.

The SDK never sends the token anywhere except the Cashback Hive API, as a `Bearer` token, with `credentials: "omit"` so cookies aren't involved.

## Restoring a session on load

During initialisation, if a token exists the SDK calls `member.get()` in the background. When it completes, `member.current()` becomes non-null and `auth:change` fires. If the request fails (expired or revoked token), the SDK calls `auth.signOut()` automatically.

This is why you should drive UI from `auth:change` rather than reading `auth.isSignedIn()` once on load — on a returning visit, the token is present immediately but the member profile arrives a moment later.

```js theme={null}
sdk.on("auth:change", ({ member }) => {
  if (member) showSignedIn(member);
  else showSignedOut();
});
showSignedOut(); // initial state until auth:change fires
```

## Signing out

`sdk.auth.signOut()` clears the stored token and the cached member, and emits `auth:change` with `member: null`. It's synchronous and doesn't call the API.

## Checking state synchronously

| Method | Returns | Meaning |
| - | - | - |
| `auth.isSignedIn()` | `boolean` | A token is stored. The member profile may not be loaded yet. |
| `member.current()` | `Member \| null` | The cached member profile, if it has been fetched. |
| `member.get()` | `Promise<Member \| null>` | Fetches the profile from the API. Resolves `null` immediately if no token. |

## What the token lets your page do

With a signed-in member, your page can:

* Read the member's profile and balances (`member.get()`, `member.current()`).
* Read their recent cashback activity (`member.activity()`).
* Make tracked clicks that are attributed to that member as well as to you (`openOffer()`, `trackingUrl()` — the token is sent automatically when offers are fetched).

Your page **cannot** modify the member's account, move money, or access any other member. Those operations only exist in Cashback Hive's own interfaces.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.