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

# Login

> Passwordless and social login, token storage, redirect handling.

The login service handles authentication for the customer portal — passwordless email codes, social providers (Google, Facebook), and the token lifecycle that follows. It writes tokens to local storage so subsequent requests stay authenticated.

## Setup

```ts theme={null}
import {
  provideContext,
  createLoginService,
  LoginServiceContext,
} from "@juo/blocks";

provideContext(
  root,
  LoginServiceContext,
  createLoginService({
    adapter: myLoginAdapter,
    formatError: (err) => ({ message: String(err), code: undefined }),
    router: routerService,
    shop: { domain: "my-shop.myshopify.com", locale: "en" },
    onLoginSuccess: async () => {
      // hook called after a successful auth
    },
  }),
);
```

The login service depends on three other dependencies passed at construction time:

* `adapter` — connects to the auth backend (passwordless start/verify, social authenticate, refresh token).
* `router` — a minimal router with `push` / `replace` so the service can redirect post-login.
* `shop` — `{ domain, locale }`; `locale` can be a `Signal<string>` so login adopts the current theme locale.

## Shape

```ts theme={null}
type LoginService = {
  passwordless: {
    email: {
      start(email: string): Promise<Result<void>>;
      verify(email: string, code: string): Promise<Result<void>>;
    };
  };
  social: {
    start(provider: "Google" | "Facebook"): Promise<Result<void>>;
    verify(code: string): Promise<Result<void>>;
  };
  logout(): Promise<Result<void>>;
  getToken(): Promise<string | null>;
  // ...reactive signals for tokens, expires, current user email
};
```

## Example: passwordless start

```ts theme={null}
import { injectContext, LoginServiceContext } from "@juo/blocks";

async function startLogin(el: HTMLElement, email: string) {
  const login = injectContext(el, LoginServiceContext);
  const result = await login.passwordless.email.start(email);
  if (result._tag === "Failure") {
    console.error(result.error.message);
    return;
  }
  // show the code input
}

async function verifyCode(el: HTMLElement, email: string, code: string) {
  const login = injectContext(el, LoginServiceContext);
  const result = await login.passwordless.email.verify(email, code);
  if (result._tag === "Success") {
    // service redirects via the router automatically
  }
}
```

## Token management

`getToken()` returns the current access token, refreshing it transparently if it has expired (using the stored refresh token). Wire it into the fetch layer to ensure all API requests include a valid access token:

```ts theme={null}
async function authedFetch(url: string, init?: RequestInit) {
  const token = await login.getToken();
  return fetch(url, {
    ...init,
    headers: { ...init?.headers, Authorization: `Bearer ${token}` },
  });
}
```
