# `ADK.Auth.CredentialManager`
[🔗](https://github.com/zeroasterisk/adk-elixir/blob/main/lib/adk/auth/credential_manager.ex#L1)

Stateless orchestrator for the full OAuth2 / credential lifecycle.

Mirrors Python ADK's `CredentialManager` but as pure functions.
The lifecycle is: **load → exchange → refresh → save**.

## Usage

    alias ADK.Auth.{Credential, CredentialManager, InMemoryStore}

    # Create a store process
    {:ok, store} = InMemoryStore.start_link()

    # Initial credential (has auth_code, awaiting exchange)
    cred = Credential.oauth2_with_code(
      "client-id",
      "client-secret",
      "auth-code-from-callback",
      token_endpoint: "https://oauth.example.com/token"
    )

    # The manager will exchange, save, and return the ready credential
    {:ok, ready} = CredentialManager.get_credential("github", cred, server: store)
    # => %Credential{type: :oauth2, access_token: "gho_...", refresh_token: "ghr_..."}

    # On next call: loads from store, refreshes if near-expired
    {:ok, ready} = CredentialManager.get_credential("github", cred, server: store)

## Options

- `:server` — the credential store PID or name (passed to `InMemoryStore`)
- `:store_mod` — module implementing `ADK.Auth.CredentialStore` (default: `ADK.Auth.InMemoryStore`)
- `:redirect_uri` — used during auth code exchange
- `:refresh_buffer` — seconds before expiry to proactively refresh (default: 300)
- `:http_opts` — extra options forwarded to `Req.post/2`

# `delete_credential`

```elixir
@spec delete_credential(
  String.t(),
  keyword()
) :: :ok | {:error, term()}
```

Delete a credential from the store.

# `get_credential`

```elixir
@spec get_credential(String.t(), ADK.Auth.Credential.t(), keyword()) ::
  {:ok, ADK.Auth.Credential.t()} | :needs_auth | {:error, term()}
```

Get a ready credential by name, running exchange/refresh as needed.

Steps:
1. If credential is a simple type (api_key, http_bearer) → return immediately
2. Try loading from the credential store
3. If loaded → check expiry → refresh if needed → save if refreshed → return
4. If not loaded and credential needs exchange → exchange → save → return
5. If not loaded and credential is client_credentials-capable → exchange → save → return
6. Otherwise → `:needs_auth` (user must re-authenticate)

## Returns

- `{:ok, credential}` — ready-to-use credential
- `:needs_auth` — no stored credential and no auth code; user must authenticate
- `{:error, reason}` — exchange/refresh/store failure

# `save_credential`

```elixir
@spec save_credential(String.t(), ADK.Auth.Credential.t(), keyword()) ::
  :ok | {:error, term()}
```

Store a credential under a name. Convenience wrapper around the store module.

---

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