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

# OAuth Implementation

Implement Clever OAuth, understand what SSO tokens can access, and handle login flows from Clever Portal, Instant Login, and Log in with Clever.

> ❗️ Clever Complete Agreement Required
>
> This feature is included with a Clever Complete subscription. Sign up [here](https://www.clever.com/developer-signup) or email your Application Success Manager to learn more.

Use this page to implement Clever OAuth, understand what SSO tokens can access, and handle login flows from Clever Portal, Instant Login, and Log in with Clever.

## Overview

Clever Single Sign-On uses the OAuth 2.0 authorization code grant flow.

In this flow, a user authenticates with Clever, Clever redirects the user back to your application with an authorization code, and your application exchanges that code for an access token. If your app is configured for OIDC, Clever can also return an identity token.

## OAuth and OIDC data access

### Access tokens

Access tokens acquired through the Clever OAuth flow do **not** grant full Data API access. They can only access:

* [`/me`](https://www.postman.com/clever-pe/developer-collections/request/elnxugm/me?action=share\&source=copy-link\&creator=11463743\&ctx=documentation) — returns the token `type`, Clever user ID, and district ID
* [`/districts/{id}`](https://www.postman.com/clever-pe/developer-collections/request/qd2bpk4/districts-id?action=share\&source=copy-link\&creator=11463743\&ctx=documentation) — where `id` is the district Clever ID
* [`/users/{id}`](https://www.postman.com/clever-pe/developer-collections/request/gbicgr1/users-id?action=share\&source=copy-link\&creator=11463743\&ctx=documentation) — where `id` is the user Clever ID

If you need full roster access, use [Secure Sync](https://dev.clever.com/docs/district-getting-started).

### Identity tokens

If your application is configured for OIDC, Clever can also return an identity token.

Identity tokens include these claims:

* `user_id` — the Clever user ID for API versions before v3.0
* `user_type` — `student`, `teacher`, `staff`, or `district admin`
* `district` — the user’s Clever district ID
* `multi_role_user_id` — the Clever user ID for API v3.0 and later
* `email` — the user’s email, if available
* `email_verified` — `false` by default; contact [Clever Support](https://support.clever.com) if you need this claim to be `true`
* `given_name` — the user’s first name
* `family_name` — the user’s last name

Identity tokens also include standard metadata claims:

* `iss` — always `https://clever.com`
* `sub` — the multi-role Clever user ID used for API v3.0 and later
* `aud` — your application client ID
* `iat` — the token creation time
* `exp` — one hour after token creation
* `nonce` — used to prevent CSRF; this must match the `nonce` value from the original `/authorize` request when provided

> 🚧 `user_id`**&#x20;and&#x20;**`multi_role_user_id`**&#x20;are different**
>
> `multi_role_user_id` matches `sub`. Make sure your implementation uses the right identifier for the API version you support.

## OAuth flow

Clever expects SSO implementations to use the authorization code grant type.

1. A user initiates login.
2. Clever authenticates the user if needed.
3. Clever redirects the user to your application’s redirect URI with an authorization code.
4. Your application sends that authorization code to Clever’s token endpoint.
5. Clever returns an access token and, if configured, an identity token.
6. Your application uses the access token to identify the user and retrieve the user data it needs.

> 🚧 **Always version your&#x20;**`/me`**&#x20;request**
>
> Call `/me` with an explicit API version. This ensures you get the correct Clever user ID format for the API version your integration uses.

> ❗️ **State parameters are not included in Clever-initiated logins**
>
> Clever-initiated logins that start from the Clever Portal or other Clever-managed entry points do not include a `state` parameter by default.
>
> If your application requires `state`, ignore the incoming Clever-initiated login and restart the authentication flow from your own site so you can include `state` yourself.
>
> For more detail, see <https://dev.clever.com/docs/best-practices-edge-cases#state-parameters>.

## Initiating logins

Users can start Clever SSO in three ways:

* from the Clever Portal
* from an Instant Login link
* from a Log in with Clever button

Your integration should be able to handle all three.

Users may authenticate into Clever through different identity providers set by their district. Your application does **not** need to handle the district’s identity provider configuration directly.

<Image src="https://files.readme.io/9ff1dd2-Clever___Log_in.png" alt="1444" align="center" caption="Screenshot of a Clever login page with multiple identity providers." />

### SSO from the Clever Portal

Many districts use the Clever Portal as the main launch point for applications. Once your app is certified and connected to a district, users can launch it from their Clever Portal.

When a user clicks your app icon, Clever redirects the user to your **primary redirect URI** with an authorization code.

<Image src="https://files.readme.io/15e1da8065959a347d92c9c73c02828c74ede85a36e1c4e7a269dc11adf34771-Screenshot_2025-06-06_at_11.17.13_AM.png" alt="2874" align="center" caption="Screenshot of the Clever Portal from a teacher's perspective." />

You can also review the end-user flow in this [Demo Experience](https://clever.com/in/demo).

Typical user flow:

1. The user opens the district’s Clever login page.
2. The user selects the correct identity provider for that district.
3. The user authenticates with that identity provider.
4. The user reaches the Clever Portal.
5. The user selects your app icon to start the login flow into your application.

### SSO using Instant Login links

Some districts prefer not to use the Clever Portal. To support those districts, Clever provides [Instant Login links](https://support.clever.com/hc/en-us/articles/205546348).

Example Instant Login link:

```text
https://clever.com/oauth/instant-login?client_id=YOUR_CLIENT_ID&district_id=THE_DISTRICT_ID
```

Typical user flow:

1. The user clicks an Instant Login link, usually from a district portal, homepage, or bookmark.
2. The user is taken to the Clever login page unless already authenticated.
3. The user authenticates if needed.
4. Clever starts the login flow into your application.

### SSO using a Log in with Clever button

If users typically start from your own login page, you can add a **Log in with Clever** button to your site.

<Image src="https://files.readme.io/d995bec-Access_Read_Ahead_-_Read_Ahead__Highlight_Ideas_with_Motion.png" alt="842" align="center" width="80%" caption="Example LIWC button using a Clever button asset" />

<Image src="https://files.readme.io/81ab9eb-Login_-_Kahoot_.png" alt="884" align="center" width="80%" caption="Example LIWC button using a Clever icon asset" />

A Log in with Clever button is a link to `https://clever.com/oauth/authorize` with parameters that identify your application.

At minimum, include:

* `response_type`
* `client_id`
* `redirect_uri`

Example LIWC URL:

```text
https://clever.com/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Fexample.com&client_id=YOUR_CLIENT_ID
```

You do not need to include `district_id` for a general login link on your own site. Clever will use district context already stored in the user’s browser when available.

If district context is not available, the user sees the Clever school picker.

<Image src="https://files.readme.io/bc8ef8d-Clever___Select_your_School.png" alt="1268" align="center" caption="Screenshot of the Clever school picker" />

> 🚧 **Testing with sandbox districts**
>
> You cannot search sandbox districts and schools by name in the school picker. Use the sandbox `district_id` instead.

If your application has district-specific login pages, you can add `district_id` to skip the school picker and send users directly to the district login page.

Example:

```text
https://clever.com/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Fexample.com&client_id=YOUR_CLIENT_ID&district_id=YOUR_DISTRICT_ID
```

### LIWC button assets

You can find Log in with Clever assets [here](https://dev.clever.com/docs/clever-sso-assets).

## Authenticating users

### Acquire the authorization code

When Clever redirects the user back to your application, the redirect URI includes a `code` parameter.

Example:

`https://primary.redirect?code=code`

At this point, your application still does not know who the user is. Your next step is to exchange the code for tokens.

### Redirect URI parameters

These are the parameters Clever adds to your redirect URI:

| Parameter | Guaranteed                        | Value                                         |
| --------- | --------------------------------- | --------------------------------------------- |
| `code`    | Yes                               | Authorization code generated by Clever        |
| `scope`   | Yes                               | Scope assigned to your application            |
| `state`   | Only if provided in the LIWC link | The same state value your app originally sent |

### Exchange the code for tokens

To exchange the code, send a `POST` request to `https://clever.com/oauth/tokens`.

Use HTTP Basic Authentication with your client ID as the username and your client secret as the password.

### Build the Basic Auth header

If your HTTP client supports username/password Basic Auth directly, use your client ID as the username and your client secret as the password.

If you need to build the header yourself:

`Authorization: Basic ` + `Base64.encode(client_id + ":" + client_secret)`

### Supported POST body formats

Clever accepts these content types at `/oauth/tokens`:

* `application/x-www-form-urlencoded`
* `application/json`

> ❗️ **Unsupported content types**
>
> Clever does not accept other POST body formats such as `multipart/form-data`. If you see `invalid request / invalid content-type` errors, make sure you are using one of the supported content types.

The token request body must include:

| Parameter      | Example                          | Description                                          |
| -------------- | -------------------------------- | ---------------------------------------------------- |
| `code`         | `heRte321`                       | The authorization code received on your redirect URI |
| `grant_type`   | `authorization_code`             | Always `authorization_code` in this flow             |
| `redirect_uri` | `https://flightschool.edu/oauth` | The exact redirect URI where Clever sent the code    |

Example using `application/x-www-form-urlencoded`:

```text
POST https://clever.com/oauth/tokens
Authorization: Basic YW5WcFkyVnFkV2xqWldwMWFXTmxDZzpjY1hwWTR0cWRZbGVjNHAxYUdsMXVJ
Content-type: application/x-www-form-urlencoded
Content-length: 107

code=heRte321&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fflightschool.edu%2Foauth
```

Example using `application/json`:

```json
POST https://clever.com/oauth/tokens
Authorization: Basic YW5WcFkyVnFkV2xqWldwMWFXTmxDZzpjY1hwWTR0cWRZbGVjNHAxYUdsMXVJ
Content-type: application/json
Content-length: 113

{"code":"heRte321","grant_type":"authorization_code","redirect_uri":"https://flightschool.edu/oauth"}
```

### Token response

Clever returns tokens when:

* `client_id` and `client_secret` are valid
* `redirect_uri` exactly matches the URI where Clever sent the code
* `code` is valid

> 🚧 **Authorization codes expire quickly**
>
> Authorization codes are valid for one minute and can only be exchanged once.

Example response (`id_token` is only present when your app is configured for OIDC):

```json
{
  "access_token": "access_token_here",
  "id_token": "id_token_here"
}
```

> ❗️ **Token expiration**
>
> Tokens expire after 24 hours and cannot be refreshed. Do not use them for long-term user identification.
>
> Clever user sessions expire after 2 hours.

## Use the returned tokens

### Access tokens

After you receive an access token, call `https://api.clever.com/v3.0/me` to retrieve the user’s Clever ID, district ID, and token type.

In most implementations, you should identify the token owner immediately after exchange.

### Identity tokens

If your app receives an identity token, decode and verify it using a JWT library rather than parsing it manually.

A list of language-specific JWT libraries is available at [jwt.io/libraries](https://jwt.io/libraries).

### Bearer tokens

Use the access token as a bearer token in the `Authorization` header:

```text
GET https://api.clever.com/some_clever_resource
Authorization: Bearer jsfUI2131da2f
```

## The `authorized_by` field

Districts must authorize Clever Single Sign-On connections before users can log in with them. Districts do this by connecting to your app at the district level and configuring sharing permissions.

To learn more:

* [Connecting with Districts](https://support.clever.com/hc/s/articles/215120718?language=en_US)
* [Sharing Permissions](https://support.clever.com/hc/s/articles/203115007?language=en_US)

You can identify a district-authorized SSO request by looking at the `/me` response. In a Clever Single Sign-On flow, `authorized_by` is set to `district`.

```json
{
  "type": "user",
  "data": {
    "id": "5fc43758db087d0be186c29d",
    "district": "5d0a9e5d0958b300014e49d2",
    "type": "user",
    "authorized_by": "district"
  },
  "links": [
    {
      "rel": "self",
      "uri": "/me"
    },
    {
      "rel": "canonical",
      "uri": "/v3.0/users/5fc43758db087d0be186c29d"
    },
    {
      "rel": "district",
      "uri": "/v3.0/districts/5d0a9e5d0958b300014e49d2"
    }
  ]
}
```

<br />