> ## Documentation Index
> Fetch the complete documentation index at: https://docs.natecosmic.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth

> Connect to COSMIC via OAuth.

## Overview

COSMIC supports **OAuth 2.1 Authorization Code with [PKCE](https://www.oauth.com/oauth2-servers/pkce/)**.

Most integrations follow this flow:

```mermaid theme={null}
flowchart TB

    subgraph APP["Your App"]
        direction TB
        A1["Redirect user to COSMIC authorization endpoint"]
        A2["Exchange authorization code for tokens"]
        A3["Store access token and refresh token"]
        A4["Send API request with access token"]
    end

    subgraph COSMIC["COSMIC"]
        direction TB
        C1["User signs in and approves the request"]
        C2["Redirect user to your app with authorization code"]
        C3["Return access token and refresh token"]
        C4["Process API request with access token"]
    end

    A1 --> C1
    C1 --> C2
    C2 --> A2
    A2 --> C3
    C3 --> A3
    A3 --> A4
    A4 --> C4

    style APP fill:transparent,stroke:#1d4a50,stroke-width:1px
    style COSMIC fill:transparent,stroke:#ffc70a,stroke-width:1px
```

## Before you begin

Create an app in the{" "}
<a href="https://natecosmic.com/developers" rel="noopener noreferrer" target="_blank">developer portal</a>. You will need your app's `client_id`, at least one allowed `redirect_uri`, and the scopes your app will use.

See [Creating apps](/guides/creating-apps).

## Authorization endpoint

Start the OAuth flow by redirecting the user to:

```text theme={null}
GET https://api.natecosmic.com/auth/v1/oauth/authorize?
  response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fyour-app.com%2Foauth%2Fcallback
  &code_challenge=YOUR_CODE_CHALLENGE
  &code_challenge_method=S256
  &state=RANDOM_STATE
```

Optional: append `&scope=openid%20email%20profile` if you need broader sign-in profile data or an ID token—see **`scope`** below.

### Query parameters

<ParamField query="client_id" type="string" required>
  Your app's client identifier from the developer portal.
</ParamField>

<ParamField query="redirect_uri" type="string" required>
  The callback URI to redirect the user to after authorization. This must
  exactly match one of the allowed redirect URIs configured for your app.

  <Note>
    Redirect URIs must match exactly, including scheme, host, path, and trailing
    slash. See [Redirect URIs](/concepts/redirect-uris).
  </Note>
</ParamField>

<ParamField query="scope" type="string">
  **Optional.** Space-separated **OpenID Connect / OAuth sign-in** values for this step only (for example `openid email profile`). They do **not** select COSMIC API permissions—those come from your app in the{" "}
  <a href="https://natecosmic.com/developers" rel="noopener noreferrer" target="_blank">developer portal</a>; see [Scopes](/concepts/scopes). If you omit `scope`, COSMIC uses a default suitable for basic sign-in; include **`openid`** when you need an **ID token** in the token response.
</ParamField>

<ParamField query="response_type" type="string" required>
  Must be `code`.
</ParamField>

<ParamField query="code_challenge" type="string" required>
  The Base64URL-encoded SHA-256 hash of your app's `code_verifier`.
</ParamField>

<ParamField query="code_challenge_method" type="string" required>
  Must be `S256`.
</ParamField>

<ParamField query="state" type="string">
  A client-generated value used to prevent
  [CSRF](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF).
  Generate a unique value per request and verify it on callback.
</ParamField>

See [Scopes](/concepts/scopes) for guidance on selecting scopes.

## User consent and redirect

After the user signs in and approves the request, COSMIC redirects the browser to your registered `redirect_uri`.

Example:

```text theme={null}
https://your-app.com/oauth/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE
```

Your app should:

1. verify that `state` matches the value you originally sent
2. extract the authorization `code`
3. exchange that code at the token endpoint

See [Authorization](/concepts/authorization) for the consent flow details and [Redirect URIs](/concepts/redirect-uris) for callback registration and matching rules.

## Token endpoint

Exchange the authorization code at:

```text theme={null}
POST https://api.natecosmic.com/auth/v1/oauth/token
```

### Authorization code exchange parameters

<ParamField body="grant_type" type="string" required>
  Must be `authorization_code`.
</ParamField>

<ParamField body="code" type="string" required>
  The authorization code returned to your `redirect_uri`.
</ParamField>

<ParamField body="redirect_uri" type="string" required>
  The same `redirect_uri` used in the authorization request.
</ParamField>

<ParamField body="client_id" type="string" required>
  Your app's client identifier.
</ParamField>

<ParamField body="code_verifier" type="string" required>
  The original random secret generated by your app for this authorization
  request.
</ParamField>

<Tabs>
  <Tab title="Public client">
    Send the token request without a `client_secret`.

    ```bash theme={null}
    curl -X POST https://api.natecosmic.com/auth/v1/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=authorization_code" \
      -d "code=AUTHORIZATION_CODE" \
      -d "redirect_uri=https://your-app.com/oauth/callback" \
      -d "client_id=YOUR_CLIENT_ID" \
      -d "code_verifier=YOUR_CODE_VERIFIER"
    ```
  </Tab>

  <Tab title="Confidential client">
    Send the token request using the authentication method configured for your app in the developer portal.

    ```bash theme={null}
    curl -X POST https://api.natecosmic.com/auth/v1/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
      -d "grant_type=authorization_code" \
      -d "code=AUTHORIZATION_CODE" \
      -d "redirect_uri=https://your-app.com/oauth/callback" \
      -d "code_verifier=YOUR_CODE_VERIFIER"
    ```

    <Note>
      This example uses HTTP Basic authentication for the client credentials.
      If your app is configured differently, send the client credentials using
      that method instead.
    </Note>
  </Tab>
</Tabs>

## Token response

A successful response returns OAuth tokens, including an access token and, when applicable, a refresh token.

<ResponseField name="access_token" type="string">
  The access token used to call COSMIC APIs.
</ResponseField>

<ResponseField name="token_type" type="string">
  The token type. Typically `Bearer`.
</ResponseField>

<ResponseField name="expires_in" type="integer">
  The lifetime of the access token in seconds.
</ResponseField>

<ResponseField name="refresh_token" type="string">
  A refresh token that can be used to obtain a new access token, when
  applicable.
</ResponseField>

<ResponseField name="scope" type="string">
  Space-delimited sign-in / identity scopes for this authorization (from your authorize request, or COSMIC defaults). This is separate from COSMIC API scopes on the token; see [Scopes](/concepts/scopes) and [Access tokens](/concepts/access-token).
</ResponseField>

Use the access token as a Bearer token when calling COSMIC APIs.

## After you receive a token

Use the access token as a Bearer credential on API requests. See [Access tokens](/concepts/access-token) for **`GET /v1/me`**, scopes on the token, and how to list organizations and members.

If a call returns **`403`**, common causes include:

* the token does not include the **scope** required for that endpoint
* the app cannot access that **organization** or **member** in the current context
* an organization admin must **re-approve** the app in COSMIC after you add or widen scopes in the developer portal

See [Authorization](/concepts/authorization) and [Scopes](/concepts/scopes).

## Refresh tokens

To obtain a new access token, send a refresh token to the same token endpoint:

```text theme={null}
POST https://api.natecosmic.com/auth/v1/oauth/token
```

### Refresh token parameters

<ParamField body="grant_type" type="string" required>
  Must be `refresh_token`.
</ParamField>

<ParamField body="refresh_token" type="string" required>
  The refresh token previously issued to your app.
</ParamField>

<ParamField body="client_id" type="string">
  Your app's client identifier, if required by your app's configured token
  endpoint authentication method.
</ParamField>

<Tabs>
  <Tab title="Public client">
    Send the refresh token request without a `client_secret`.

    ```bash theme={null}
    curl -X POST https://api.natecosmic.com/auth/v1/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=refresh_token" \
      -d "refresh_token=YOUR_REFRESH_TOKEN" \
      -d "client_id=YOUR_CLIENT_ID"
    ```
  </Tab>

  <Tab title="Confidential client">
    Send the refresh token request using the authentication method configured for your app in the developer portal.

    ```bash theme={null}
    curl -X POST https://api.natecosmic.com/auth/v1/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
      -d "grant_type=refresh_token" \
      -d "refresh_token=YOUR_REFRESH_TOKEN"
    ```

    <Note>
      This example uses HTTP Basic authentication for the client credentials.
      If your app is configured differently, send the client credentials using
      that method instead.
    </Note>
  </Tab>
</Tabs>

## Common errors

### `redirect_uri` mismatch

The `redirect_uri` sent in the authorization request and token request must exactly match a URI registered for the client.

This includes:

* scheme
* host
* path
* trailing slash

See [Redirect URIs](/concepts/redirect-uris).

### `invalid_grant`

Common causes include:

* the authorization code expired
* the authorization code was already used
* the `code_verifier` does not match the original PKCE challenge
* the `redirect_uri` does not match the original authorization request

### `state` mismatch

If you send `state`, verify it on callback. Reject the response if the returned value does not match the original value stored by your app.

## Related guides

* [Creating apps](/guides/creating-apps)
* [Quickstart](/quickstart)
* [Redirect URIs](/concepts/redirect-uris)
* [Access tokens](/concepts/access-token)
* [API reference](/api-reference/api)
