# `Ltix.StorageAdapter`
[🔗](https://github.com/DecoyLex/ltix/blob/main/lib/ltix/storage_adapter.ex#L1)

Defines callbacks for looking up registrations, deployments, and managing nonces.

Your application implements this behaviour to connect Ltix to your
persistence layer. See the [Storage Adapters](storage-adapters.md) guide
for a complete Ecto-backed example.

## Returning your own structs

`get_registration/2` and `get_deployment/2` can return any struct that
implements `Ltix.Registerable` or `Ltix.Deployable`. Your struct is
passed through to the `Ltix.LaunchContext` after a successful launch,
so you can access your own fields (database IDs, tenant info, etc.)
without an extra query.

## Callback summary

| Callback | Called during | Purpose |
|---|---|---|
| `c:get_registration/2` | Login initiation | Look up a platform by issuer and optional client_id |
| `c:get_deployment/2` | Launch validation | Look up a deployment by registration and deployment_id |
| `c:store_nonce/2` | Login initiation | Persist a nonce for later verification |
| `c:validate_nonce/2` | Launch validation | Verify a nonce was issued by the tool, then consume it |

# `get_deployment`

```elixir
@callback get_deployment(
  registration :: Ltix.Registration.t(),
  deployment_id :: String.t()
) ::
  {:ok, Ltix.Deployable.t()} | {:error, :not_found}
```

Look up a deployment by registration and deployment_id.

The `deployment_id` is case-sensitive and assigned by the platform.
The `registration` parameter is the resolved `Ltix.Registration`,
not the original struct returned by `c:get_registration/2`.

Return any struct that implements `Ltix.Deployable`. The library
validates it via the protocol and preserves your original struct
in the `Ltix.LaunchContext`.

# `get_registration`

```elixir
@callback get_registration(issuer :: String.t(), client_id :: String.t() | nil) ::
  {:ok, Ltix.Registerable.t()} | {:error, :not_found}
```

Look up a platform registration by issuer and client_id.

The `client_id` may be `nil` when the platform doesn't include it in
the login request. Your adapter must handle both cases.

Return any struct that implements `Ltix.Registerable`. The library
extracts the `Ltix.Registration` it needs via the protocol and
preserves your original struct in the `Ltix.LaunchContext`.

# `store_nonce`

```elixir
@callback store_nonce(nonce :: String.t(), registration :: Ltix.Registration.t()) :: :ok
```

Persist a nonce for later verification.

Called during login initiation. The nonce is generated by the library
and must be stored so that `c:validate_nonce/2` can verify it was
issued by the tool and has not been replayed.

# `validate_nonce`

```elixir
@callback validate_nonce(nonce :: String.t(), registration :: Ltix.Registration.t()) ::
  :ok | {:error, :nonce_already_used | :nonce_not_found}
```

Verify a nonce from the ID Token and consume it.

The implementation must check two things:

  1. The nonce was previously stored by `c:store_nonce/2`
  2. The nonce has not already been consumed (replay prevention)

After successful validation, mark the nonce as consumed so it cannot
be reused.

---

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