# `WardrikOAuth`

OAuth sign-in for WardrikCore agents. Grants are sealed in a `WardrikVault`.

    children = [
      {WardrikVault, name: MyApp.Vault, store: store, keys: keys, grants: {WardrikOAuth, MyApp.OAuth}},
      {WardrikOAuth,
       name: MyApp.OAuth,
       vault: MyApp.Vault,
       store: store,
       page: "https://agents.example.com/oauth",
       on_granted: {MyApp.Agents, :granted, []},
       providers: [github: [issuer: "https://github.com/login/oauth", client_id: id, client_secret: secret]]}
    ]

Mount `WardrikOAuth.Plug` at `page` to finish sign-ins. See `WardrikOAuth.Provider` for provider
options.

A provider's `client_secret` may be `{:secret, name}`. It's read from the vault's deployment
secrets each time a sign-in needs it, so a new one takes effect at once. See
`WardrikVault.value/2`.

# `add_provider`

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

Adds the provider `name` at runtime, on every node.

    :ok = WardrikOAuth.add_provider(MyApp.OAuth, "linear", resource: "https://mcp.linear.app/mcp")

Takes the same options as a provider in `:providers`. The `client_secret` is sealed in the vault,
not kept in the store.

Without a `client_id`, Wardrik gets one the first time someone signs in:

  1. If the service reads Client ID Metadata Documents and `page` is `https`, the `client_id` is
     the URL of the document `WardrikOAuth.Plug` serves.
  2. Otherwise, Wardrik registers itself with the service.

Returns `{:error, reason}` for invalid options, or a name given in `:providers`.

# `authorize`

```elixir
@spec authorize(atom(), String.t(), WardrikCore.Principal.t()) ::
  {:ok, String.t()} | {:error, term()}
```

Returns the URL where `principal` signs in with the provider `name`.

Starts a sign-in, or returns the URL of the one already under way.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `client_document`

```elixir
@spec client_document(atom()) :: map()
```

Returns the Client ID Metadata Document served at `{page}/client`.

Its URL is Wardrik's `client_id` with services that read these documents.

# `finish`

```elixir
@spec finish(atom(), map()) :: {:ok, String.t()} | {:error, term()}
```

Finishes a browser sign-in from its callback's query params.

The params carry `"state"`, and `"code"` or `"error"`. Returns `{:ok, provider}` or
`{:error, reason}`, and `{:error, :no_such_flow}` if the sign-in has ended.

`WardrikOAuth.Plug` calls this. Call it yourself to handle the callback in your own route.

# `flow`

```elixir
@spec flow(atom(), String.t()) :: {:ok, map()} | :error
```

Describes the device sign-in `id`.

Returns `{:ok, map}` with `:provider`, `:principal`, `:user_code` and `:verification_uri`, or
`:error` if the sign-in has ended.

# `providers`

```elixir
@spec providers(atom()) :: [String.t()]
```

Returns the names of the providers someone can sign in with.

# `remove_provider`

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

Removes the provider `name`, added at runtime, and the client registered for it.

# `start_link`

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

Starts `WardrikOAuth`.

## Options

  * `:name` - required. The name functions reach it by.
  * `:vault` - required. The `WardrikVault` grants are sealed in.
  * `:store` - required. The `WardrikStore` for providers added at runtime and registered
    clients.
  * `:page` - required. The URL `WardrikOAuth.Plug` is mounted at, as
    `"https://agents.example.com/oauth"`.
  * `:providers` - providers to start with, as a keyword list of name and options. See
    `WardrikOAuth.Provider`. `[]` unless set.
  * `:on_granted` - `{module, function, args}` called with the provider's name and the principal,
    then `args`, after each grant.
  * `:client_name` - the name a service shows for a client Wardrik registers. `"Wardrik"`
    unless set.
  * `:req_options` - extra options for every `Req` request.
  * `:check` - a function called on each URL before a request is sent. It returns `:ok` or
    `{:error, reason}`. Use one when users add providers by URL. Without it, any `https` URL,
    and `http` on localhost, is reached.

---

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