Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

OAuth2 Protocol

The mobile app uses the standard OAuth 2.0 protocol for authenticating users to the open edX LMS. The OAuth2 protocol supports authenticating the "client" and/or the "resource owner" by verifying "shared credentials" that were exchanged out-of-band with the "authorization server".  A successful authentication transaction results in a valid "access token" that the client uses for accessing resources on the "resource server".

In the mobile context:

  • the client is the mobile device
  • the resource owner is the user using the mobile device
  • the resource server is LMS or any service hosted on the edX platform that exports a public API to be called by the mobile app
  • the shared credential is either
    • the user's "password" (if the user has an edX password), or
    • the user's "social auth ID" (for 3rd party authentication)
  • the authorization server is either
    • LMS (if authenticating with the user's password), or
    • a 3rd party server (if authenticating with a 3rd party social auth)
  • the access token is used to authenticate the user in subsequent API calls

Mobile API Authentication Classes

All APIs called by the edX mobile app are authenticated using the OAuth2AuthenticationAllowInactiveUser authentication class.  It verifies the caller has a valid OAuth2 token and bypasses verification of their email address.  For a streamlined on-boarding experience, the mobile app supports ongoing usage of its features without ever requiring the user to verify their email address.

Additionally, while most edX views support session authentication, some of those APIs are decorated with the SessionAuthenticationAllowInactiveUser authentication class, which also bypasses email verification.  For example, the enrollment API doesn't require the user to be "active" (i.e., doesn't need a verified email address).

OAuth2 Access Tokens

The mobile app obtains an edX-issued access token in either of the following ways:

Example Response

The response from either of the above endpoints would provide the edX access_token as follows:

{"access_token": "5e0a0cb315e66aa96bab910faa8c70ee0ca91236", "token_type": "Bearer", "expires_in": 2591999, "scope": ""}

Authorization Bearer

Once an access token is obtained, it can be used to authenticate the user in any API call that supports the OAuth2AuthenticationAllowInactiveUser authentication class.  The access token is passed in the Bearer field of the Authorization HTTP header, as follows:

Expiration

Currently, our edX-issued access tokens expire in 30 days (technically 2591999 seconds).

OAuth2 -> Session Cookie

Additionally, the mobile app can exchange an access token for a session cookie that can be used in a WebView:

Note: It looks like the access_token endpoint (above) also returns session cookies along with the user's access_token.  So perhaps this API wasn't needed.  TBD for later investigation.

Expiration

Currently, the returned session cookie expires in 2 weeks.

OAuth2 Client Type, Client ID, and Client Secret

The OAuth2 RFC categorizes clients into the following 2 types based on their ability to confidentially store client (not user) credentials:

  1. Confidential Client Type
    Clients capable of maintaining the confidentiality of their
    credentials (e.g., client implemented on a secure server with
    restricted access to the client credentials), or capable of secure
    client authentication using other means. 

  2. Public Client Type
     Clients incapable of maintaining the confidentiality of their
     credentials (e.g., clients executing on the device used by the
     resource owner, such as an installed native application or a web
     browser-based application), and incapable of secure client
     authentication via any other means.

The RFC also explicitly calls out and defines native mobile applications, as follows:

      A native application is a public client installed and executed on
      the device used by the resource owner.  Protocol data and
      credentials are accessible to the resource owner.  It is assumed
      that any client authentication credentials included in the
      application can be extracted.  On the other hand, dynamically
      issued credentials such as access tokens or refresh tokens can
      receive an acceptable level of protection.  At a minimum, these
      credentials are protected from hostile servers with which the
      application may interact.  On some platforms, these credentials
      might be protected from other applications residing on the same
      device.

So what does this all mean for the mobile apps?

Our mobile apps are considered as a "Public" Client Type according to OAuth2. 

They are issued a Client ID, which is an authorization-server issued string identifier to identify (not authenticate) the client making the request.  On edX production servers, we use one common Client ID for all iOS clients and another common Client ID for all Android clients.  The Client ID is not intended to be a secret, but rather, a way to associate requests with a client (or a group of clients).  In django, Client IDs are managed using the OAuth2 Client page on the django admin interface (/admin/oauth2/client/).

Although a Client Secret is automatically generated for each OAuth2 Client in django, the Client Secrets for mobile apps are not to be used.  And they definitely should not be distributed, transported to, or configured or stored on the apps.  As the RFC states, we assume we cannot authenticate a mobile app since any client credential can be extracted by tampering with the local device.

Note: An authenticated client (and its Client Secret) is needed only for the Authorization Code and Client Credentials OAuth2 grant types.  The Implicit and Password grant types don't require an authenticated client.  The last (Password) grant type is what our mobile apps use.

OAuth2 Refresh Tokens

Since OAuth2 access tokens have a limited lifetime designated by their "expires_in" value, there has to be a strategy for what happens when they expire.  The OAuth2 RFC has provision for this by introducing Refresh Tokens as a means to re-authenticate with the authorization server in exchange for fresher access tokens.

Since possession of a refresh token authorizes requests for reusable access tokens, the refresh token is a proxy for the user's credentials and must be stored in a protected user-specific location.  From the OAuth2 and security standpoint, storing a refresh token is preferable over storing a user's raw credentials (passwords, etc).

 

 

 


The default django-oauth2 implementation does *not* support refresh tokens for public clients for the password grant type:
* [AccessToken.password|https://github.com/caffeinehit/django-oauth2-provider/blob/6b5bc0d3ad706d2aaa47fa476f38406cddd01236/provider/views.py#L541]
* [AccessToken.access_token_response|https://github.com/caffeinehit/django-oauth2-provider/blob/6b5bc0d3ad706d2aaa47fa476f38406cddd01236/provider/views.py#L482]

*However*, the [RFC|http://tools.ietf.org/html/rfc6749#section-10.4] *does* allow it.
Here is the relevant quote from the spec:

+10.4.  Refresh Tokens+

*Authorization servers MAY issue refresh tokens to* web application
clients and *native application clients*.
...
*When client authentication is not possible*, the authorization server
SHOULD deploy other means to detect refresh token abuse.
...
*For example, *the authorization server could employ refresh token
rotation in which a *new refresh token is issued with every access
token refresh response*.  The previous refresh token is invalidated
but retained by the authorization server.  If a refresh token is
compromised and subsequently used by both the attacker and the
legitimate client, one of them will present an invalidated refresh
token, which will inform the authorization server of the breach.
...

Determine

  1. What is the recommended expiration time for (1) refresh tokens and (2) oAuth tokens for mobile devices?
  2. What does our server-side oAuth library support for refreshing tokens? What additional code is required to support it?
  3. What is the recommended path for current app instances in the market and for future versions of the app?
  • No labels