# AuthService

**Spinbox is experimental: APIs change and data may be lost.** What is built and what proves it is on [Status](https://spin-stack.github.io/website/reference/status.md); what Spinbox does not do is on [Limitations](https://spin-stack.github.io/website/overview/limitations.md).

> spin.v1.AuthService: each procedure, its request and its response.

<!-- Generated by tools/refgen from spin:internal/controlplane/apidocs/spin.v1.openapi.json. Edit the source, not this file. -->

## ApproveWorkspaceSignIn

`POST /spin.v1.AuthService/ApproveWorkspaceSignIn`

**Request**

| Field | Type | What it is |
|---|---|---|
| `request` | string | From BeginWorkspaceSignInResponse.approve_url. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `callbackUrl` | string | The workspace host's callback with the one-time code, for the browser to be sent to. |

## BeginWorkspaceSignIn

`POST /spin.v1.AuthService/BeginWorkspaceSignIn`

A sign-in on a workspace's host, done by the dashboard. The proxy begins it and sends the browser to the dashboard, which approves it for whoever is signed in there and sends the browser back with a one-time code; the proxy completes it with that code and the binding it gave the same browser. The identity provider is only ever told the dashboard's callback.

**Request**

| Field | Type | What it is |
|---|---|---|
| `host` | string | The workspace's host the browser asked for, as the proxy received it: &lt;name>.ws.&lt;domain>. The code comes back to this host and to no other. |
| `returnPath` | string | Where on that host the browser goes once it is signed in: a path, never a URL. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `approveUrl` | string | The dashboard's page that approves this sign-in, for the browser to be sent to. |
| `binding` | string | A secret for the browser that began this sign-in, kept where only the proxy reads it: CompleteWorkspaceSignIn requires it with the code, so a code opened in another browser - one somebody was sent a link to - signs nobody in. |

## CheckDeviceLogin

`POST /spin.v1.AuthService/CheckDeviceLogin`

Check device login status (CLI polls this) Returns session token once user has completed login and entered the code

**Request**

| Field | Type | What it is |
|---|---|---|
| `deviceCode` | string | The device_code from StartDeviceLogin |

**Response**

| Field | Type | What it is |
|---|---|---|
| `status` | [DeviceLoginStatus](https://spin-stack.github.io/website/reference/api/types.md#deviceloginstatus) | Status of the device login |
| `user` | [User](https://spin-stack.github.io/website/reference/api/types.md#user) | Only set when status is DEVICE_LOGIN_STATUS_COMPLETE |
| `sessionToken` | string |  |
| `expiresAt` | `Timestamp` |  |

## CompleteLogin

`POST /spin.v1.AuthService/CompleteLogin`

Complete login (callback from IdP)

**Request**

| Field | Type | What it is |
|---|---|---|
| `code` | string | OAuth/OIDC code |
| `state` | string | CSRF state token (contains provider info) |
| `binding` | string | StartLoginResponse.binding for this state |

**Response**

| Field | Type | What it is |
|---|---|---|
| `user` | [User](https://spin-stack.github.io/website/reference/api/types.md#user) |  |
| `sessionToken` | string |  |
| `expiresAt` | `Timestamp` |  |

## CompleteWorkspaceSignIn

`POST /spin.v1.AuthService/CompleteWorkspaceSignIn`

**Request**

| Field | Type | What it is |
|---|---|---|
| `code` | string |  |
| `binding` | string | BeginWorkspaceSignInResponse.binding, as the browser presented it. |
| `host` | string | The host the callback arrived on, which must be the one the sign-in began on. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `sessionToken` | string |  |
| `expiresAt` | `Timestamp` |  |
| `returnPath` | string |  |

## ConfirmDeviceLogin

`POST /spin.v1.AuthService/ConfirmDeviceLogin`

Complete device login (dashboard calls this after user enters code) Requires authentication - links the device code to the current user

**Request**

| Field | Type | What it is |
|---|---|---|
| `userCode` | string | The user code entered by the user (e.g., "ABCD-1234") |

**Response**

No fields: `{}`.

## CreateAPIKey

`POST /spin.v1.AuthService/CreateAPIKey`

Create API key

**Request**

| Field | Type | What it is |
|---|---|---|
| `name` | string |  |
| `scopes` | list of string | e.g., ["workspace:read", "workspace:write"] |
| `expiresAt` | `Timestamp` |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `apiKey` | [APIKey](https://spin-stack.github.io/website/reference/api/types.md#apikey) |  |
| `secret` | string | Only shown once |

## GetCurrentUser

`POST /spin.v1.AuthService/GetCurrentUser`

Get current user info

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `user` | [User](https://spin-stack.github.io/website/reference/api/types.md#user) |  |
| `controlPlaneVersion` | string | The release of the control plane that answered, which is the release of the dashboard asking: the bundle is served from inside that binary. It rides here rather than on an endpoint of its own because every session asks this once, and because a version an installation will be matched against known flaws by is not something to hand out unauthenticated. |

## ListAPIKeys

`POST /spin.v1.AuthService/ListAPIKeys`

List API keys

**Request**

| Field | Type | What it is |
|---|---|---|
| `page` | [PageRequest](https://spin-stack.github.io/website/reference/api/types.md#pagerequest) |  |

**Response**

| Field | Type | What it is |
|---|---|---|
| `apiKeys` | list of [APIKey](https://spin-stack.github.io/website/reference/api/types.md#apikey) |  |
| `page` | [PageResponse](https://spin-stack.github.io/website/reference/api/types.md#pageresponse) |  |

## ListIdentityProviders

`POST /spin.v1.AuthService/ListIdentityProviders`

List available identity providers (for login page) Always includes built-in GitHub provider; additional OIDC providers from database

**Request**

No fields: `{}`.

**Response**

| Field | Type | What it is |
|---|---|---|
| `providers` | list of [IdentityProvider](https://spin-stack.github.io/website/reference/api/types.md#identityprovider) |  |

## Logout

`POST /spin.v1.AuthService/Logout`

Logout (invalidate session)

**Request**

No fields: `{}`.

**Response**

No fields: `{}`.

## RevokeAPIKey

`POST /spin.v1.AuthService/RevokeAPIKey`

Revoke API key

**Request**

| Field | Type | What it is |
|---|---|---|
| `id` | string |  |

**Response**

No fields: `{}`.

## SearchUsers

`POST /spin.v1.AuthService/SearchUsers`

Search users by name or email (for team member selection, workspace sharing)

**Request**

| Field | Type | What it is |
|---|---|---|
| `query` | string | Search by name or email |
| `limit` | integer (int32) | Max results (default 10) |
| `excludeTeamId` | string | Exclude users already in this team |

**Response**

| Field | Type | What it is |
|---|---|---|
| `users` | list of [User](https://spin-stack.github.io/website/reference/api/types.md#user) |  |

## StartDeviceLogin

`POST /spin.v1.AuthService/StartDeviceLogin`

Start device login flow (for CLI when OAuth redirect is not available) Returns a user code and verification URL. User visits URL and enters code.

**Request**

| Field | Type | What it is |
|---|---|---|
| `identityProviderId` | string | Optional: use default if empty |

**Response**

| Field | Type | What it is |
|---|---|---|
| `deviceCode` | string | Secret code for CLI to poll with |
| `userCode` | string | Short code user enters (e.g., "ABCD-1234") |
| `verificationUrl` | string | URL user visits to login |
| `expiresIn` | integer (int32) | Seconds until codes expire |
| `interval` | integer (int32) | Seconds between poll attempts |

## StartLogin

`POST /spin.v1.AuthService/StartLogin`

Start login flow with a specific identity provider Use "github" for built-in GitHub auth, or IdP ID for dynamic providers

**Request**

| Field | Type | What it is |
|---|---|---|
| `identityProviderId` | string | ID of the identity provider to use |
| `redirectUri` | string | Where the identity provider sends the code: the dashboard's /auth/callback, over https. Anything else is refused. |

**Response**

| Field | Type | What it is |
|---|---|---|
| `authUrl` | string | URL to redirect user to |
| `state` | string | CSRF state token |
| `binding` | string | A secret for the client that began this sign-in, and for nobody else: CompleteLogin requires it with the state. The state and the code come back in a URL, which somebody who signed in to their own account can send to anybody - and without this, whoever opens it is signed in as them. Kept where only this client reads it, never in a URL. |
