SSO using an Implicit Grant

Use the OAuth 2.0 implicit grant flow to obtain bearer tokens in client-side Javascript or on a native device without access to client secrets.

In our traditional SSO flow, Clever sends users to a redirect URI on your sever with a code parameter. After your server receives this, it initiates a request to Clever to exchange that code for a bearer token representing the user. Your application may have initiated this flow with a Log in with Clever button or Clever may have initiated thee flow using the Clever Portal or an Instant Login Link.

This token exchange flow is simplified further in Clever's implicit grant flow. Instead of receiving a code vaue that you must then exchange for a bearer token, Clever delivers the bearer token to your redirect URI as part of a URI fragment.

We still recommend implementing Clever's traditional SSO flow if you anticipate users signing in through Clever's launchpad or via other district-driven approaches. Some features districts rely on will not work with the Implicit Grant Flow exclusively. Additionally, district tokens cannot be negotiated with the implicit grant flow.

❗️

This feature is not generally available

For security reasons, the OAuth 2.0 implicit grant flow is only available to applications that cannot use the authorization grant flow outlined in Instant Login & the Identity API. If you cannot use the authorization grant flow, please reach out to [email protected].

Who is this for?

  • Web applications authored in client-side Javascript or Flash
  • Widely distributed open source applications
  • Web browser extensions and plugins
  • WordPress, Drupal, Moodle, or other platform extensions
  • Native mobile applications
  • Native desktop applications

Requirements

  • You need the client ID of the application you're developing against Clever with. Locate your client ID by signing in to your Clever developer account and navigating to your application configuration. This ID is used to identify your application to Clever.
  • You must register a redirect URI as part of your application record that will be used for the implicit grant flow. This may be an additional redirect URI or the same redirect URI you already use. You'll be handling URL fragments specially in this flow and you may want to separate concerns with a unique URL path.

The implicit grant flow, step by step

Let's examine this flow in more detail with a hypothetical example.

We have an application called Flight School. Here are some vitals on how we're configured.

FieldValue
Client IDanVpY2VqdWljZWp1aWNlCg
Redirect URIshttps://flightschool.edu/oauth, http://localhost/oauth, https://flightschool.edu/oauth/implicit, http://localhost/oauth/implicit

Your configuration will look something like this. You'll definitely be using a different Client ID, and your redirect URIs will be your own. Regardless, you can complete this implicit grant flow.

I've decided to take my own advice and use a dedicated URI path which I'll use for the remainder of this example: https://flightschool.edu/oauth/implicit.

Step 1: Present a "Log in with Clever" button

You may recall Log in with Clever buttons from Instant Login & the Identity API

In this flow, we're going to build our button with the response_type parameter set to token. This will instruct Clever to handle subsequent steps with the implicit grant flow. We'll also provide the redirect URI we decided on, properly URL escaped.

https://clever.com/oauth/authorize?response_type=token&redirect_uri=https%3A%2F%flightschool.edu%2Foauth%2F/implicit&client_id=anVpY2VqdWljZWp1aWNlCg&state=fb37f982-925b

Notice we've included the state parameter - this is important! By randomly generating a nonce value and providing it here, Clever will send it back to your application in the next step. This allows you to validate that your application initiated this flow. We recommend using "opaque" values for state parameters — a human should not be able to understand what information is described in state. See Keeping Instant Login Secure for more information.

Clever authorizes the user

This step is identical to typical "Log in with Clever" usage. If the user is not yet signed in via Clever, they'll be prompted for their district-specific credentials before the next step. If they're already signed in, Clever will instantly send the user back to your redirect_uri in the next step.

Clever sends the user to your redirect_uri along with a bearer token

After authorization, the user will be sent to your specified redirect_uri with the user's bearer token attached to the URI fragment as an access_token field.

Continuing our example, we might receive the end user at this variant of our redirect URI:

https://flightschool.edu/oauth/implicit#token_type=bearer&state=fb37f982-925b&access_token=ZWdnbWFuIGkgYW0gdGhlCg&scope=read%3Astudents%20read%3Ateachers%20read%3Auser_id

To extract the bearer token from the request and validate the state parameter, we first must evaluate the URI fragment.

What's a URI fragment?

URI fragments appear as the final element of a URI and begin with a hash character (#). They are sometimes referred to a the hash component of a URL.

Consider the following imaginary URL: https://example.com/some/path?field0=value0#field1=value1&field2=value2

This URL has a few component parts:

  • the scheme/protocol (https://)
  • the authority/host (example.com)
  • the path (/some/path)
  • the query string (?field0=value0)
  • the URI fragment (#field1=value1&field=value2)

Typically, a web server never receives a URI fragment. When you use a URL like this in a web browser, only https://example.com/some/path?field0=value0 is received by the server. The URI fragment is instead handled by the web browser. Your application programmatically gains access to the URI fragment only when the web browser is executing client-side Javascript. This is why this implicit grant flow is typically used in client-side Javascript applications.

Extracting the token from the URI fragment

The OAuth 2.0 implicit grant flow specifies that Clever present you a series of application/x-www-form-urlencoded key/value pairs within the URI fragment. Your user agent (typically a web browser in this flow) is unlikely to provide you a direct way to convert that URI fragment string into an accessible data type, such as a Javascript associative array.

You'll need to use Javascript to evaluate the URI fragment string, extract the keys and values, and yield a traversable data structure. We recommend using an open source library to perform these extractions, such as jQuery BBQ. However, the following snippet should be functional for most Clever purposes.

// First, extract the URI fragment from the user agent's location object
function extractParamsFromURIFragment() {
  var fragmentParams = {};
  var e,
      a = /\+/g,  // Regex for replacing addition symbol with a space
      r = /([^&;=]+)=?([^&;]*)/g,
      d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
      q = window.location.hash.substring(1);

  while (e = r.exec(q)) {
    fragmentParams[d(e[1])] = d(e[2]);
  }
  return fragmentParams;
}

Using a similar function, you could extract the bearer token and state with code similar to:

var params = extractParamsFromURIFragment();
var bearer_token = params['access_token'];
var state = params['state'];
var scope = params['scope'];

Server-side implications

Typically your server will still receive a simple request to your specified redirect_uri even when you're handling it client-side.

You will not receive the data in the URI fragment and you should essentially treat these requests as a "no-op." One reason to separate concerns with distinct redirect URIs is so you don't have to worry about triggering your typical server-side redirect with these requests.

Identify bearer token authenticity with tokeninfo

Now that you've gotten ahold of your user's bearer token, you'll use it in REST API requests to Clever's data API.

Before using the bearer token, it is important to verify the token's legitimacy. The implicit grant flow pushes bearer tokens in your application's direction but just because you've received a token doesn't mean it necessarily came from Clever or was intended for your specific application.

To protect against the "confused deputy problem" and determine if the token is intended for your application and was produced by Clever, you must verify its authenticity and legitimacy by issuing an additional request to Clever.

Verifying is simple. Issue a HTTP GET request to https://clever.com/oauth/tokeninfo and assert your bearer token and then validate the response.

If the bearer token received was jsfUI2131da2f, your HTTP Authorization header and request may look something like this:

GET https://clever.com/oauth/tokeninfo
Authorization: Bearer jsfUI2131da2f

Success

If the token was issued by Clever, you should receive a HTTP 200 response with a JSON body containing a client_id and the token's associated scopes. After receiving this response, compare the received client_id to the client_id associated with your application. If they are the same, proceed with storing the token and logging the user in to your application.

A typical success response will looking something like:

{
  "client_id": "9122696a58e9c5ae3ad4",
  "scopes": [
    "read:user_id",
    "read:teachers",
    "read:students"
  ]
}

If the client_id does not match, discard the token and prevent the user from signing in.

Rejection

If the token was not issued by Clever, you should receive a HTTP 401 response. No need to evaluate the response's body — just knowing that it's a HTTP 401 should be enough for you to discard the token and prevent the user from signing in.

Technical failure

If Clever can't evaluate the token for some reason — usually due to some kind of internal error — we'll respond with a HTTP 500, HTTP 502, or HTTP 503 error. These errors don't necessarily imply the token is invalid, and you may want to retry the request or ask the user to attempt the login sequence again. Please use an exponential back off strategy if you intend to retry requests.

Use the bearer token to issue Data API requests

Exactly as you would if you had obtained the token through the authorization grant flow

Error Handling

In most cases, if there's a problem authorizing the end user or if you've constructed your oauth/authorize URL incorrectly, there's no programmatic error condition to consume. Clever will not redirect the user to your specified redirect URI and you'll simply be left with an initiated login state that is never consummated.

That said, exceptional events do happen. In the event that something unusual happens and the user is authenticated through Clever and redirected to your redirect_uri in an error state, we'll provide an error parameter instead of an access_token on the URI fragment you receive.

In practice, such errors are rarely programmatically recoverable based on their disposition. If you receive one of these errors, display a simple message to your end user and allow them to try again. Consult section 4.2.2.1 of the OAuth 2.0 specification for more information.