# client.auth

> client.auth — the auth operations.

`client.auth` — the `auth` operations.

On `AsyncKindgi` every method is the same, awaited.

## `client.auth.sign_in_options()`

```python signature
sign_in_options(
    *,
    email: str | None = None,
    timeout: float | None = None,
) -> SignInOptions
```

How a person can sign in. `GET /v1/auth/sign-in-options`

Public: nobody is signed in yet. With `email`, the ways in for that email's domain: the identity providers of the one tenant the domain is verified for (an unverified domain offers none), then any the deployment offers everyone it has added (`owner: deployment`, "Continue with Google"); without, an empty list: sign-in is email first, so nothing is offered before an email. `methods` says which ways in the deployment allows: identity providers, an API token (`POST /v1/auth/token-sign-in`), and an emailed sign-in link (`emailLink`, with a captcha site key when it needs one); `identityProviders` and `apiToken` both `false` when nobody can sign in to the console. Always mounted. The answer depends only on the domain: two people at the same domain get the same answer, whether or not either has an account. Rate-limited per client (`429 rate-limit-exceeded`, with `Retry-After`).

## `client.auth.token_sign_in()`

```python signature
token_sign_in(*, timeout: float | None = None) -> TokenSignInResult
```

Sign in to the console with an API token. `POST /v1/auth/token-sign-in`

The API token in `Authorization` is exchanged once for a browser session in the session cookie (HttpOnly; the same as a sign-in with an identity provider), so the browser never keeps the token. Only a person's full key opens a session: a service account's key, or a narrowed one (a `member` role, or one project), is refused `403 token-sign-in-not-allowed`. The session ends after its lifetime, or when the key expires if sooner. `403 token-sign-in-off` when the deployment doesn't allow it (always mounted, so a console gets that answer); `400 token-sign-in-needs-an-api-token` when the request is already signed in by a session.

## `client.auth.refresh()`

```python signature
refresh(
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> RefreshResult
```

Refresh the current session token. `POST /v1/auth/refresh`

Requires a session token (`kgi_sk_*`); bearer tokens are managed via `/v1/tokens`. The session token rotates: the new session keeps the person, scopes and expiry, and the provider is not called. OAuth 2.1 BCP refresh-token rotation: the OLD session token is invalidated (marked rotated) — reusing it after refresh returns `401 refresh-token-invalid` so compliant clients can retry with the fresh token instead of prompting a re-auth. A browser session (the session cookie) is not refreshed: `400 cookie-session-not-refreshable`, so a new token never reaches page scripts; it ends at its TTL.

## `client.auth.logout()`

```python signature
logout(
    *,
    idempotency_key: str | None = None,
    timeout: float | None = None,
) -> LogoutResult
```

Revoke the current session. `POST /v1/auth/logout`

Requires a session token (`kgi_sk_*`); bearer tokens are managed via `/v1/tokens`. Idempotent — revoking an already-revoked session returns `{ revoked: false }`. A browser session (the session cookie) also gets its cookie cleared (`Set-Cookie` with `Max-Age=0`).
