Developer Platform (May 2024)

Legacy authentication

«  OAuth 2.0 authentication   ·  [   home  ·   reference  ·   community   |   search  ·   index   ·  routing table   ·  scopes table   ]   ·  Integrating with the Integrated Learning Platform UI  »

Before supporting OAuth2, the Brightspace APIs used a legacy, ID-key authentication system as a single point of entry for all client applications. This topic covers that legacy system in detail for those who need to support legacy applications.

Warning

Use of this legacy authentication system is deprecated; D2L no longer offers full support for, nor encourages the use of, this legacy authentication system.

The legacy three-legged system (user, application, service) has several key properties:

Note

The information in this topic applies only to Brightspace API client applications using the ID-Key authorization method.

Trust structure

We built the authentication system for our services that provide a Brightspace API upon a structure of proof of identity and points of controlled access.

Proof of identity

The proof of identity system employs IDs and Keys for system entities (user, LMS service, integrating application) and the trust structure uses these to identify actors and sign requests. The trust structure requires the actors to use the keys to create signatures that can offer proof of identity without the risk of session-hijacking that can arise by using tokens. Once an entity establishes its identity, an actor in the structure has trusted information upon which to control or grant access to data or services on a user’s behalf.

LMS service instances. The administrators of any deployed LMS service (including staging or test instances) can configure it with an LMS ID and Key to prove its identity to a central server. The central server can then, in turn, synchronize and distribute a list of appropriate and known Applications out to this LMS, and all other registered services.

Applications. The central Keytool service can grant a registering application an Application ID and Key associated with its name, version, description, and publisher meta-data. Applications always use this key to sign requests made of a back-end service’s Brightspace API (most importantly requests to the service when the application wants it to exchange a user name and password entered into a web form for the User ID-key pair identifying a calling user context).

Users. The application retrieves the User ID-key indirectly during the authentication process in a browser or web-control. This model follows the one used in common web services (particularly, the three-legged model of OAuth). The user must separately authenticate with the LMS service, and must confirm granting access to the third-party application.

Additionally, after authentication, the granted User ID-key pair is unique to the Application context (that is one Application ID cannot use the User ID and Key provided to another Application ID), and the requesting application can store the User ID-key pair for long periods (by default, 30 days) because the Key is never directly exchanged during operations (instead, it uses the Key to craft signatures).

Access control points

Fundamentally, the system’s strongest and most granular point of access control is the User Privilege Level, associated with a User ID-key pair. Applications can never use the Brightspace API to extend a user’s privileges beyond the scope of the User Privilege Level. For maximum usability, we recommend that administrators should consider deploying a service to rely only on this standard user access (where user access will typically be configured to grant minimum privilege and is potentially already audited).

User Identity Level. This non-optional layer is the strongest mechanism for controlling access. It is based on the user’s proof of identity; it is not possible to gain access to the service without an authenticated user context, and all access will be limited, in the end, by this identity’s privilege.

User Acceptance. This additional layer allows a user to choose whether or not to grant an application access to his or her data. Immediately after logging in, and before sending the User ID and Key to the requesting application, the service presents a page giving the user a choice to proceed or abandon the application. This gives the user privacy control and minimizes the risk of phishing attacks by third-party applications that claim to be provided by a trusted organization.

Administer by Application. In the “Manage Extensibility” administrator tool, service administrators can opt in, or opt out, of applications that are made known to the system by the service vendor. Administrators can configure their instances to make applications available as they are published; this lets administrators make applications available to their users as soon as they receive their application records synchronized to their instances. We recommend this configuration as the easiest to maintain.

Extensibility synchronization. This organization-level control allows administrators to entirely turn off discovery of new applications for environments that must be locked down or do not provide outbound connectivity. Administrators enable this mode by simply not providing (or removing) the synchronization configuration values. We recommend that administrators enable extensibility synchronization as the easiest configuration to maintain.

D2L’s Keytool service. In the event of an Application Key being compromised, D2L can revoke privileges for an Application and when this update is propagated out to instances with configured extensibility synchronization, it will deny support for applications attempting to use the associated Application ID. This limits the exposure of poorly behaved or malicious applications. It additionally protects application writers in the event that their keys become compromised. This process is not optional.

Administrator or User on a compromised account. In the event of a User Key becoming compromised (for instance, by losing a laptop or mobile device), both users and administrators can revoke tokens. If a user resets his or her password on the supporting back-end service, this will automatically revoke the privileges afforded any existing long-lived tokens generated for that user context: subsequent access by applications attempting to use that User ID-key pair will fail, and the applications will need to have their controlling users re-authenticate with the service.

By default, the LMS service will maintain a provided User ID-key pair as valid indefinitely; Administrators that want to revise this to a real time constraint (for example, 30 days) can contact their dedicated customer support contact.

Protocol architecture

The protocol architecture supports two authentication scenarios:

Common data types. Both these scenarios employ several data values expected to have a particular form:

Base string

The string signed by a key to produce a signature. When you use more than one item to form the base string, the protocol expects you to concatenate the items together with ampersands (&).

Take care when forming the base string for generating signatures: in some cases, an element of the base string must be rendered as upper case (the HTTP method) and in some cases, rendered as lower case (the API route, when generating tokens for an API call).

Timestamps

When either scenario requires the API caller to furnish a timestamp in a query parameter, the service expects the caller to use the Unix timestamp format (i.e. seconds since epoch).

IDs

IDs used to uniquely identify actors in these scenarios are strings, 22 characters in length, consisting of alphanumeric, dash (-), or underscore (_) characters.

Keys

Keys used for generating signatures in these scenarios are strings, 22 characters in length, consisting of alphanumeric, dash (-), or underscore (_) characters.

Signatures

All signatures used for authentication must be URL-friendly, and so these scenarios use this process to generate a signature:

  1. Take the key used to create the signature and produce a set of key-bytes by UTF8-encoding the key.

  2. Take the base-string for the signature and produce a set of base-string-bytes by UTF8-encoding the base-string.

  3. Produce the HMAC-SHA256 signature hash by using the key-bytes and base-string-bytes as input parameters.

  4. Take the signature hash and produce a set of signature-bytes by base64url encoding the hash (see RFC 4648; no ‘=’ padding used, ‘+’ and ‘/’ replaced with ‘-’ and ‘_’ respectively).

  5. Pass these generated signature-bytes in all the query parameters where you’re expected to provide a signature.

Using a third-party web application

In the case where a remote website hosts a third-party application that makes use of the Brightspace API, the web application will redirect calling browsers to get the user to independently authenticate with the back-end service; this way, the user never has to expose the service credentials to the web application.

Actors. The following diagram presents the authentication sequence with four different actors:

User

The person using the web application. This person has an active account with the service with sufficient permissions to make the requests the web application must make of the service.

Browser

The web-browser application the user employs to use the web application.

Webapp

The “third party” web application that actually uses the Brightspace APIs offered by the service. This application has an Application ID-key pair, conforming with the service’s need to identify the applications that use its API: the Keytool service granting Application IDs and keys has an existing mechanism for publishing new entries out to subscribed services.

Service

The Learning Service providing the Brightspace API.

Diagram terms. For clarity, we use several placeholder terms in the following diagram, instead of the opaque data they replace:

{service}

This is the Learning Service’s hosting entry point to the API. Typically it will be a hostname/port combination (for example lms.uriverside.edu:8080).

{trustedURL}

This is the URL that the web application wants the browser to visit, once the user has successfully authenticated with the back-end service. This URL’s sole purpose is to pass in app-specific token credentials created by the service that the web application can use with this user session to verify an authentic calling user context for future API calls.

Note that you must have declared this trusted URL when you registered your application, and your application can only receive token-credentials to this URL.

{sessionState}

This is session state that the web application may pass through the back-end service during the authentication process expecting it to get passed back to the trusted URL with the user token credentials. This can be used, for example, to carry a session key through to the web application, or other similar state.

{appID} and {appSig}

The public Application ID and Application Signature (generated with its Application Key) that the service can use to authenticate the calling application.

{tokenID} and {tokenKey}

Using the webapp’s Application ID and the logged-in user credentials, the service generates and provides the User ID-key pair (tokenID and tokenKey) back to the web application so that it can use them to generate a User Signature. In future calls, the web application provides the User ID and User Signature to the service so that it knows the web application is operating in a valid, authenticated calling user context.

{tokenSig}

The service constructs a Token Signature out of the calling user’s User ID and User Key (tokenID and tokenKey), providing these back to the web application so that it can cache local knowledge about the calling user context (without having to know any secret shared between the user and the service, such as the user’s password).

{userSig}

The web application can create a User Signature using the service-provided User Key; this allows the service to associate a web application’s action requests with a particular calling user context.

Protocol used. In this scenario, most of the interaction between the browser, web application, and service can safely take place using standard HTTP traffic; however, for the steps that directly involve authentication, web application implementers should expect to use HTTPS for additional data security. In the following diagram, these steps are indicated by the notes with a pink background.

sequenceDiagram autonumber actor User participant Browser participant WebApp participant Service User-->Browser: {visit webapp site at https://webapp/actionpage} Browser->>WebApp: GET https://webapp/actionpage WebApp-->>Browser: 302 Redirect https://{service}/d2l/auth/api/token?x_target={trustedURL}&x_a={appID}&x_b={appSig}&x_state={sessionState} Note right of WebApp: WebApp redirects browser <br/> with app-identifying parameters Browser->>Service: GET https://{service}/d2l/auth/api/token?x_target={trustedURL}&x_a={appID}&x_b={appSig}&x_state={sessionState} Service->>User: {service challenges user to authenticate} User-->>Service: {user auths directly with service} Service-->>Browser: 302 Redirect {trustedURL}?x_a={tokenID}&x_b={tokenKey}&x_c={tokenSig}&x_state={sessionState} Browser->>WebApp: GET {trustedURL}?x_a={tokenID}&x_b={tokenKey}&x_c={tokenSig}&x_state={sessionState} WebApp-->>Browser: 302 Redirect https://webapp/actionpage" Note right of WebApp: WebApp redirects back to original actionpage URL Browser->>WebApp: GET https://webapp/actionpage WebApp->>Service: GET http://{service}/d2l/api/{route}?x_a={appID}&x_b={userID}&x_c={appSig}&x_d={userSig}&x_t={time} Service-->Service: {validate {appSig} and {userSig}} Service-->>WebApp: 200 OK + {API return data} WebApp-->>Browser: 200 OK + {web page from API call} Browser-->User: Browser renders web page for user

Sequence notes. Some of the numbered steps in the previous diagram warrant some additional description:

1 to 4. In the first stage, the user browses to a third-party web-application that then needs to take some action through the Brightspace API to satisfy the user’s request. The web application determines that it has no long-lived token saved for the user, meaning it does not currently have any authenticated user session with the back-end Learning Framework service. Before the web application can proceed with the user’s request it must prompt the service to present the user with an authentication challenge. It does this by sending back a redirect notice to the user’s browser to the service’s authentication API route, encoding with it several query parameters:

  • x_target={trustedURL} – Landing page parameter: the registered trusted URL to which the service should redirect the user’s browser after user-authentication completes successfully. The service will only redirect to this URL if it matches the trusted URL assocated with the calling application’s record.

  • x_a={appID} – web application’s public Application ID value.

  • x_b={appSig} – Application session signature parameter: use the landing page parameter URL (as provided as x_target, but not URL-encoded) as the base-string, and the Application Key as the key.

    Note

    It’s important to note that you must use the landing page, x_target parameter URL as-is when forming the base string for generating the signature: don’t alter its case, as you would for other standard API calls.

  • x_state={sessionState} – web application’s session state to round-trip through the authentication process, maintaining state between the token request and the token callback; the back end service will pass on the value here, along with the token credentials, through to the trusted URL.

    D2L recommends that, if a web application uses this parameter, it use it for something akin to a single-use, time-limited unguessable string as a “session key”; the web application’s back-end can store state associated with this session key and use it to (for example) help mitigate the risk of cross-site request forgery attacks on the x_target callback URL.

    If the web application uses this parameter, it should realize that the user agent can see and manipulate it. Therefore, those that use this should:

    • Take care that the value is unguessable and opaque

    • Minimize the lifespan of the value to reduce the possible impact of having this value escape outside one particular authentication workflow

    • Always validate that the value passed back to the trusted URL matches an expected value

Note that we strongly recommend that the web application use HTTPS for the request of the Brightspace API to retrieve session tokens by authenticating the user.

5 to 7. In the second stage, the service presents an authentication challenge to the user (it asks the user to log in to the LMS), so that it can determine what the calling user context will be for a subsequent set of actions requested by the web application. The user authentication process is hidden from the web application, so the user never presents credentials directly to the web application. After providing credentials, the LMS requires the user to confirm (at least once) granting access to the requesting web-application.

When the user authentication completes successfully, the service creates a long-lived token to associate the user context with the web application and then redirects the user’s browser back to the web application’s x_target URL (provided to it in step 4). Along with this redirection, it passes several query parameters:

  • x_a={tokenID} – Unique ID associated with the long-lived token: the web application can provide this ID so that the service can precisely locate the web application/user context.

  • x_b={tokenKey} – Key associated with the long-lived token: the web application can use this as a key to generating session signatures.

  • x_c={tokenSig} – Token identity signature: the service joins (and delimits with an ampersand) the User ID (tokenID) and the User Key (tokenKey) to use as the base-string, and uses the Application Key as the key.

  • x_state={sessionState} – session state passed from the web application to the back-end service in its original request for user tokens in stage three.

This part of the process will use HTTPS.

8 to 10. In the third stage, the service redirects the browser back to the x_target landing page in the web application along with the long-lived token parameters, which the web application can then gather and store. The web application can use its own Application Key to verify the signed User ID and User Key (and cache these values for future use). After storing these tokens, the web application can redirect the browser back to the original action page browsed to by the user.

11 to 15. In the final stage, the web application proceeds to make the action request of the Brightspace API, now that it has all the required information to establish an authentic calling user context with the service. Along with the route for the API request, the web application provides several authentication query parameters:

  • x_a={appID} and x_c={appSig} – The web application’s public Application ID value, along with an application signature: join (and delimit with ampersands) the uppercase HTTP method (i.e. GET), lower-case version of the URI path (i.e. /d2l/api/auth), and x_t timestamp value to use as the base-string, and use the Application Key as the key.

  • x_b={userID} and x_d={userSig} – The service-provided User ID value, along with a user signature: join (and delimit with ampersand) the uppercase HTTP method, lower-case version of the URI path (i.e. /d2l/api/auth), and x_t timestamp value to use as the base-string, and use the Token Key as the key.

  • x_t={time}Unix timestamp for the action request. If this timestamp falls outside the window the service allows, it will respond with an error result code and its current time expected: the web application should just for any clock-skew and re-try the API call.

Note

It’s important to note that when you’re including the API’s URI path in the base-string for x_c and x_d token generation, you must use the lower case version of the URI path.

Using a local native application

In the case when the user employs a local, native application integrated with the Brightspace API, the native application uses a web-control or browser instance to handle the user authentication in an opaque manner; implementers of native applications should carefully consider whether they need to have any visibility into the user’s LMS authentication credentials: in most cases, we recommend that implementers act as much at arms length from the credentials as possible.

In most cases, the actors and data based back and forth are similar to the previous scenario: here we describe only the notable differences.

Actors. The actors in the middle of the sequence are slightly different:

NativeApp

The API caller is a local native application providing the primary HCI for the user.

Browser

The native application can use a locally-installed web browser to handle the user authentication, or it could incorporate a web-control as part of itself to handle the authentication.

Diagram terms. The only notable difference here is in the nature of the “landing page” to which the native application expects the service to redirect after successful authentication.

{appURI}

This should be a URI with a protocol for which the native application can register as a handler. The browser or web-control the native application uses to handle the authentication needs a way, expressible by the service, to return control back to the native application: a specialized URI is a convenient way to implement this (i.e. something like nativeAppProt://some/action/path).

sequenceDiagram autonumber actor User participant Browser participant NativeApp participant Service User-->NativeApp: {choose an activity requiring a service-API call} NativeApp-->>Browser: open URL https://{service}/d2l/auth/api/token?x_target={appURI}&x_a={appID}&x_b={appSig}&x_state={sessionState} Note right of NativeApp: Native app passes control<br/>to Browser to handle auth Browser->>Service: GET https://{service}/d2l/auth/api/token?x_target={appURI}&x_a={appID}&x_b={appSig}&x_state={sessionState} Service->>User: {Service challenges user to authenticate via browser} User-->>Service: {User authenticates to service} Note right of Service: User provides credentials in Browser<br/>User also confirms app access Service-->>Browser: 302 Redirect {appURI}?x_a={tokenID}&x_b={tokenKey}&x_c{tokenSig}&x_state={sessionState} Note right of Browser: Service redirects browser back<br/>to native-app-handled URI Browser-)NativeApp: handle {appURI}?x_a={tokenID}&x_b={tokenKey}&x_c{tokenSig}&x_state={sessionState} to the NativeApp handler Note right of NativeApp: Essentially async as browser<br/>doesn't expect a callback NativeApp->>Service: GET https://{service}/d2l/api/{route}?x_a={appID}&x_b={userID}&x_c={appSig}&x_d={userSig}&x_t={time} Service-->Service: {validate {appSig} and {userSig}} Service-->>NativeApp: 200 OK + {API return data} NativeApp-->User: {NativeApp renders results for user}

Sequence notes. The native application sequence is simplified compared to the web application scenario:

1 to 2. In the first stage, the user chooses an activity in the native application for which it will need to make an API action request of the back-end service. It determines that it has no long-lived token saved for the user, and therefore must prompt the service to present the user with an authentication challenge. It does this by prompting a locally installed web browser (or web control) to open an URL pointing at the back-end services authentication entry point, passing along parameters the service can use for context:

  • x_target={appURI} – “Landing resource” parameter: a URI to which the service can redirect the browser or web control after user-authentication completes successfully; this URI will be one for which the native application register as a handler with the browser or web control.

  • x_a={appID} and x_b={appSig} – The native application’s Application ID, and generated sessions signature, just as in the web application scenario.

  • x_state={sessionState} – native application’s session state to round-trip through the authentication process, maintaining state between the token request and the token callback; the back end service will pass on the value here, along with the token credentials, through to the app URI.

    D2L recommends that, if an application uses this parameter, it use it for something akin to a single-use, time-limited unguessable string as a “session key”; the application can store state associated with this session key and use it to (for example) help mitigate the risk of cross-site request forgery attacks on the x_target callback URL.

    If the application uses this parameter, it should realize that the user can see and manipulate it. Therefore, those that use this should:

    • Take care that the value is unguessable and opaque

    • Minimize the lifespan of the value to reduce the possible impact of having this value escape outside one particular authentication workflow

    • Always validate that the value passed back to the trusted URL matches an expected value

Note that we strongly recommend that the native application require the browser or web control use HTTPS to perform the user authentication.

3 to 7. In the second stage, the service presents an authentication challenge to the user (it asks the user to log in to the LMS), to determine the calling user context for the native application. This stage occurs largely identically to the web application scenario (including the requirement for user confirmation of granting access to the web-application). However, when the user authentication successfully completes, the x_target URI that the service redirects the user’s browser or web control to will be of a form that the browser can pass off to the native application as a registered handler.

The service passes the same session state back appended to the redirect URI as in the web application scenario (Token ID, Token Key, Token signature, Session State).

8 to 11. In the final stage, the native application proceeds to make the action request of the Brightspace API just as in the web application scenario.

«  OAuth 2.0 authentication   ·  [   home  ·   reference  ·   community   |   search  ·   index   ·  routing table   ·  scopes table   ]   ·  Integrating with the Integrated Learning Platform UI  »