# `@aurora/modules/authentication`

## Architecture: state vs. behavior split

Authentication is split into two coordinated services with deliberately
distinct responsibilities.

### `TokenStore` — state only

`shared/token-store.service.ts`. Holds the credentials, `accessToken`,
`refreshToken`, and `authenticated` signals. Owns localStorage I/O for the
session under `CREDENTIALS_KEY`. **Zero Apollo dependency.** Safe to inject
from anywhere — including the Apollo factory itself.

Inject `TokenStore` when you need to READ auth state:

- Route guards checking `tokenStore.authenticated()`.
- Components gating UI on the current session.
- The Apollo factory's `SetContextLink` reading `tokenStore.accessToken()`
  for the `Authorization` header.

### `AuthenticationService` — behavior only

`shared/authentication.service.ts` (abstract) plus concrete adapters under
`shared/adapters/`. Exposes auth ACTIONS: `signIn`, `signInUsingRefreshToken`,
`signOut`, `signUp`, `forgotPassword`, `resetPassword`, `check`.

Concrete adapters (`AuthenticationAuroraAdapterService`,
`AuthenticationDisabledService`) inject both `Apollo` (for OAuth mutations)
and `TokenStore` (for state persistence). Every state mutation goes through
the store.

Inject `AuthenticationService` when you need to CALL an auth action:

- Sign-in form's submit handler calling `signIn(...)`.
- Sign-out button calling `signOut()`.
- Route guards calling `check()` to validate / refresh on navigation.

## Why the split

Originally `AuthenticationService` held both state and behavior. The Apollo
factory needed the state (`accessToken`) to populate the `Authorization`
header; the service needed Apollo to call OAuth mutations (`signIn`,
`signInUsingRefreshToken`). Angular detected the cycle and threw
[`NG0200`](https://angular.dev/errors/NG0200).

A workaround using `Injector.get` for lazy lookup of `AuthenticationService`
inside the Apollo link callback existed for a while. It worked but was folk
wisdom — neither Apollo's docs, apollo-angular's docs, nor Angular's own
NG0200 page documented the pattern. Angular's official guidance was
"refactor the dependencies to not be reliant on one another".

This module's current shape implements that refactor. The Apollo factory
now injects `TokenStore` directly (zero cycle); `AuthenticationService`
injects `TokenStore` + `Apollo` and orchestrates the OAuth mutations.

The full rationale, alternatives considered, and migration plan are recorded
in the archived OpenSpec change
`split-token-store-from-authentication-service` under
`openspec/changes/archive/<date>-split-token-store-from-authentication-service/`.

## Consumer cheatsheet

| Need                                             | Inject                 |
| ------------------------------------------------ | ---------------------- |
| Read `accessToken` / `refreshToken` / `authenticated` | `TokenStore`          |
| Persist new credentials after an external flow  | `TokenStore.setCredentials` |
| Wipe session on sign-out from a guard           | `TokenStore.clear`     |
| Call `signIn(email, password)`                  | `AuthenticationService` |
| Call `signOut()`                                | `AuthenticationService` |
| Call `check()` from a guard                     | `AuthenticationService` |
| Both (e.g., sign-in form gating UI on auth state) | both — they are independent |

## Forbidden coupling

- `TokenStore` MUST NOT inject Apollo, HttpClient, or any transport. Keep
  it pure state. The Apollo factory depends on this contract.
- Application code MUST NOT inject `TokenStore` to write tokens directly
  except in the narrow "external auth callback" case (e.g., OAuth
  authorization-code callback guard receiving credentials from a non-Apollo
  HTTP endpoint). All OAuth mutation flows go through `AuthenticationService`.
