# `Rho.Auth.Login`
[🔗](https://github.com/GenericJam/rho/blob/v0.1.0/lib/rho/auth/login.ex#L1)

Signing in to a subscription (`:anthropic`, `:openai_codex`): the OAuth
flow (`Rho.Auth.OAuthFlow`) with the provider's own localhost redirect,
answered by this BEAM.

`begin/1` starts a `:gen_tcp` listener on `127.0.0.1` at the redirect's
port (54545 for Anthropic, 1455 for OpenAI: the redirects omp registers),
opens the authorize URL in the device's browser, and when the browser is
sent back to `http://localhost:<port>/…?code=…&state=…` exchanges the code,
stores the credentials (`Rho.Auth.put/2`, which tells its
subscribers) and sends whoever called `begin/1`
`{:rho_login, provider, :ok | {:error, message}}`.

Anthropic's authorize page may show the code (`code#state`) instead of
redirecting; `paste/2` takes it (or the whole redirect URL) for the flow
in progress. One flow at a time: `begin/1` replaces an unfinished one,
and cancels an exchange still running for an older one (its result is
never used). A redirect carrying another flow's state (an old tab) gets
an error page and leaves the flow in progress alone; the flow ends on its
own redirect, a paste, or after 15 minutes without either.

The exchange runs in a worker and retries transport errors every 2 s
for up to 9 minutes: Android blocks a backgrounded app's network (~60 s
after it leaves the foreground), so it completes when the user switches
back from the browser.

Options: `:open_url` (`fn url -> :ok end`; default
`Rho.Config.open_url/1`, which a mob app points at `Mob.Device.open_url/1`),
`:redirect` (`fn provider -> uri end`, default the provider's), `:respond`
(tests: `Rho.Auth.OAuthFlow`), `:flow_timeout_ms`.

# `begin`

```elixir
@spec begin(Rho.Auth.provider()) :: {:ok, String.t()} | {:error, term()}
```

Starts a sign-in and opens the browser; returns the URL that was opened.
`{:error, :not_oauth}` for a provider that takes only an API key.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `open_in_browser`

```elixir
@spec open_in_browser(String.t()) :: :ok
```

The host default for `config :rho, :open_url`: the computer's browser
(`open` on macOS, `xdg-open` elsewhere on Unix, `start` on Windows),
started without waiting for it. When there's none it logs that and
still returns `:ok`: `begin/1` returns the URL for the host to show.

# `paste`

```elixir
@spec paste(Rho.Auth.provider(), String.t()) ::
  :ok
  | {:error,
     :no_login_started | :no_code | :state_mismatch | {:started_for, atom()}}
```

Finishes the flow in progress with what the provider's page showed: a
`code#state`, a bare code, or the redirect URL.

# `pending`

```elixir
@spec pending() :: Rho.Auth.provider() | nil
```

The provider of the sign-in in progress (nil when none), whose result
goes to the caller from now on: a page reopened during the browser flow
takes it over from the one that started it.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Started by `Rho.Supervisor` (its `:login` option holds `opts`, see the moduledoc).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
