Access token

JWT (JSON Web Token)

What is JSON Web Token?

JSON Web Token (JWT) is an open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

For example, a server could generate a token that has the claim “logged in as administrator” and provide that to a client. The client could then use that token to prove that it is logged in as admin. The tokens can be signed by one party’s private key (usually the server’s) so that any party can subsequently verify whether or not the token is legitimate. If the other party, by some suitable and trustworthy means, is in possession of the corresponding public key, they too are able to verify the token’s legitimacy.

When should you use JSON Web Tokens?

Here are some scenarios where JSON Web Tokens are useful:

  • Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
  • Information Exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn’t been tampered with.

Structure

  • Header

    1
    2
    3
    4
    
    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
  • Payload

    1
    2
    3
    4
    
    {
      "loggedInAs": "admin",
      "iat": 1422779638
    }
    
  • Signature

    The Base64url Encoding is similar to base64, but uses different non-alphanumeric characters and omits padding.

    1
    2
    3
    4
    5
    
    HMAC_SHA256(
      secret,
      base64urlEncoding(header) + '.' +
      base64urlEncoding(payload)
    )
    

The three parts are encoded separately using Base64url Encoding, and concatenated using periods to produce the JWT:

1
const token = base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature)

The above data and the secret of “secretkey” creates the token:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI

This resulting token can be easily passed into HTML and HTTP.

Use

In authentication, when the user successfully logs in using their credentials, a JSON Web Token will be returned and must be saved locally (typically in local or session storage (Javascript global objects sessionStorage and localStorage) , but cookies can also be used), instead of the traditional approach of creating a session in the server and returning a cookie.

For unattended processes the client may also authenticate directly by generating and signing its own JWT with a pre-shared secret and pass it to a OAuth compliant service like so:

1
2
3
4
POST /oauth2/token
Content-type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJhb...

If the client passes a valid JWT assertion the server will generate an access_token valid for making calls to the application and pass it back to the client:

1
2
3
4
5
{
  "access_token": "eyJhb...",
  "token_type": "Bearer",
  "expires_in": 3600
}

When the client wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization HTTP header using the Bearer schema. The content of the header might look like the following:

1
Authorization: Bearer eyJhbGci...<snip>...yu5CSpyHI

This is a stateless authentication mechanism as the user state is never saved in server memory. The server’s protected routes will check for a valid JWT in the Authorization header, and if it is present, the user will be allowed to access protected resources. As JWTs are self-contained, all the necessary information is there, reducing the need to query the database multiple times.

JSON Web Token implementation in Python

1
$ pip install pyjwt
Encoding & Decoding Tokens with HS256
1
2
3
4
5
6
7
8
>>> import jwt

>>> encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
>>> encoded_jwt
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'

>>> jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
{'some': 'payload'}
Encoding & Decoding Tokens with RS256 (RSA)
1
2
3
4
5
6
7
>>import jwt
>>private_key = b'-----BEGIN PRIVATE KEY-----\nMIGEAgEAMBAGByqGSM49AgEGBS...'
>>public_key = b'-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEAC...'
>>encoded = jwt.encode({'some': 'payload'}, private_key, algorithm='RS256')
'eyJhbGciOiJIU...'
>>decoded = jwt.decode(encoded, public_key, algorithms='RS256')
{'some': 'payload'}
Specifying Additional Headers
1
2
>>jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256', headers={'kid': '230498151c214b788dd97f22b85410a5'})
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIzMDQ5ODE1MWMyMTRiNzg4ZGQ5N2YyMmI4NTQxMGE1In0.eyJzb21lIjoicGF5bG9hZCJ9.DogbDGmMHgA_bU05TAB-R6geQ2nMU2BRM-LnYEtefwg'

API key

Authorization protocol

OAuth

OAuth is an authorization protocol, rather than an authentication protocol. Using OAuth on its own as an authentication method may be referred to as pseudo-authentication.

Django oauth2 provider

1
$ pip install django-oauth2-provider
1
2
3
4
5
6
7
# settings.py

INSTALLED_APPS = (
    # ...
    'provider',
    'provider.oauth2',
)
1
2
3
# urls.py

url(r'^oauth2/', include('provider.oauth2.urls', namespace = 'oauth2')),
1
2
$ python manage.py syncdb
$ python manage.py migrate
How to request an access token for the first time ?
  1. Create a client entry in your database

    To find out which type of client you need to create, read Section 2.1.

    To create a new entry simply use the Django admin panel.

  2. Request an access token

    Your client needs to submit a POST request to /oauth2/access_token including the following parameters:

    • client_id - The client ID you’ve configured in the Django admin.
    • client_secret - The client secret configured in the Django admin.
    • username - The username with which you want to log in.
    • password - The password corresponding to the user you’re logging in with.

    Request

    1
    
    $ curl -X POST -d "client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=password&username=YOUR_USERNAME&password=YOUR_PASSWORD" http://localhost:8000/oauth2/access_token/
    

    Response

    1
    
    {"access_token": "<your-access-token>", "scope": "read", "expires_in": 86399, "refresh_token": "<your-refresh-token>"}
    

    This particular way of obtaining an access token is called a Password Grant. All the other ways of acquiring an access token are outlined in Section 4.

Example between Catalog/Discovery and LMS
  1. Request URL: http://localhost:18381/api-auth/login/?next=/api/v1/courses/

    Query String Parameters: next: /api/v1/courses/

    Request Method: GET

    Status Code: 302 Found

  2. Request URL: http://localhost:18381/login/edx-oidc/?next=/api/v1/courses/

    Query String Parameters: next: /api/v1/courses/

    Request Method: GET

    Status Code: 302 Found

  3. Request URL: http://localhost:18000/oauth2/authorize/?redirect_uri=http://localhost:18381/complete/edx-oidc/&client_id=discovery-key&scope=openid+profile+email+permissions&response_type=code&state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x

    Query String Parameters:

    • redirect_uri: http://localhost:18381/complete/edx-oidc/
    • client_id: discovery-key
    • scope: openid profile email permissions
    • response_type: code
    • state: W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x

    Request Method: GET

    Status Code: 302 Found

  4. Request URL: http://localhost:18000/oauth2/authorize/confirm

    Request Method: GET

    Status Code: 302 Found

  5. Request URL: http://localhost:18000/login?next=/oauth2/authorize/confirm

    Query String Parameters: /oauth2/authorize/confirm

    Request Method: GET

    Status Code: 200 OK

  6. Request URL: http://localhost:18000/login_ajax

    Form Data:

    • email: edx@example.com
    • password: edx

    Request Method: POST

    Status Code: 200 OK

    Response Headers:

    • Set-Cookie: edxloggedin=true; Path=/
  7. Request URL: http://localhost:18000/oauth2/authorize/confirm

    Request Method: GET

    Status Code: 302 Found

  8. Request URL: http://localhost:18000/oauth2/redirect

    Request Method: GET

    Status Code: 302 Found

    Response Headers:

    • Set-Cookie: sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; httponly; Path=/
  9. Request URL: http://localhost:18381/complete/edx-oidc/?state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x&code=7ab4b799ee2a6abe7e0f81affed2c69e1da96e02

    Query String Parameters:

    • state: W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x
    • code: 7ab4b799ee2a6abe7e0f81affed2c69e1da96e02

    Request Method: GET

    Request Headers:

    • Cookie:

      course_discovery_sessionid=z847akheu4f11guh1nyc230f2xrr8omx; sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"

    Status Code: 302 Found

    Response Headers:

    • Set-Cookie: course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg; expires=Sat, 28-Jul-2018 13:34:35 GMT; HttpOnly; Max-Age=1209600; Path=/
  10. Request URL: http://localhost:18381/api/v1/courses/

    Request Method: GET

    Request Headers:

    • Cookie:

      sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; course_discovery_csrftoken=3lenIcj4HaGcbde57PLf46bgngeP42GFKaENxvopwVwux0NbvlH3Xypic6dKYXGw; course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg

    Status Code: 200 OK

OAuth Dispatch App (OAuth2 Provider Interface)
Provider code

The oauth_dispatch app provides the top-most entry points to the OAuth2 Provider views.

  • Its validator module ensures Restricted Applications only receive expired tokens.
  • Its Access Token View returns JWTs as access tokens when a JWT token_type is requested.
  • It uses an edX custom JwtBuilder implementation to create the JWT.

The JwtBuilder uses the pyjwkest library for implementation of JSON Web Signature (JWS) and other crypto to build and sign JWT tokens.

Clients & REST API Clients code

edX services, including LMS, use the

edx-rest-api-client

library to make OAuth2 client requests and REST API calls.

  • Built on top of slumber, the edx-rest-api-client provides a utility to retrieve an access token from the LMS. Its Auth classes create appropriate HTTP Authorization headers with Bearer or JWT insertions as needed.
  • It makes use of the PyJWT library for cryptographically creating JWT tokens.
  • Note: Creation of JWT tokens in our system should only be done by the OAuth Provider. This will break once we use asymmetric signing keys, for which remote services will not have the private keys.
Authentication by REST endpoints

edX REST endpoints that support JWTs as access tokens declare the custom edX

JwtAuthentication

class in its DRF

authentication_classes

scheme.

https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/oauth_dispatch/docs/decisions/0003-use-jwt-as-oauth-tokens-remove-openid-connect.rst

auth-backends
Configuration

Adding single sign-on/out support to a service requires a few changes:

  1. Define settings
  2. Add the authentication backend
  3. Add the login/logout redirects
edx-rest-api-client
edx-drf-extensions.
edx oauth
  1. Request URL: http://localhost:18381/api-auth/login/?next=/api/v1/courses/

    Query String Parameters: next: /api/v1/courses/

    Request Method: GET

    Status Code: 302 Found

  2. Request URL: http://localhost:18381/login/edx-oidc/?next=/api/v1/courses/

    Query String Parameters: next: /api/v1/courses/

    Request Method: GET

    Status Code: 302 Found

  3. Request URL: http://localhost:18000/oauth2/authorize/?redirect_uri=http://localhost:18381/complete/edx-oidc/&client_id=discovery-key&scope=openid+profile+email+permissions&response_type=code&state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x

    Query String Parameters:

    • redirect_uri: http://localhost:18381/complete/edx-oidc/
    • client_id: discovery-key
    • scope: openid profile email permissions
    • response_type: code
    • state: W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x

    Request Method: GET

    Status Code: 302 Found

  4. Request URL: http://localhost:18000/oauth2/authorize/confirm

    Request Method: GET

    Status Code: 302 Found

  5. Request URL: http://localhost:18000/login?next=/oauth2/authorize/confirm

    Query String Parameters: /oauth2/authorize/confirm

    Request Method: GET

    Status Code: 200 OK

  6. Request URL: http://localhost:18000/login_ajax

    Form Data:

    • email: edx@example.com
    • password: edx

    Request Method: POST

    Status Code: 200 OK

    Response Headers:

    • Set-Cookie: edxloggedin=true; Path=/
  7. Request URL: http://localhost:18000/oauth2/authorize/confirm

    Request Method: GET

    Status Code: 302 Found

  8. Request URL: http://localhost:18000/oauth2/redirect

    Request Method: GET

    Status Code: 302 Found

    Response Headers:

    • Set-Cookie: sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; httponly; Path=/
  9. Request URL: http://localhost:18381/complete/edx-oidc/?state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x&code=7ab4b799ee2a6abe7e0f81affed2c69e1da96e02

    Query String Parameters:

    • state: W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x
    • code: 7ab4b799ee2a6abe7e0f81affed2c69e1da96e02

    Request Method: GET

    Request Headers:

    • Cookie:

      course_discovery_sessionid=z847akheu4f11guh1nyc230f2xrr8omx; sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"

    Status Code: 302 Found

    Response Headers:

    • Set-Cookie: course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg; expires=Sat, 28-Jul-2018 13:34:35 GMT; HttpOnly; Max-Age=1209600; Path=/
  10. Request URL: http://localhost:18381/api/v1/courses/

    Request Method: GET

    Request Headers:

    • Cookie:

      sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; course_discovery_csrftoken=3lenIcj4HaGcbde57PLf46bgngeP42GFKaENxvopwVwux0NbvlH3Xypic6dKYXGw; course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg

    Status Code: 200 OK

Authorization Methods

Role-Based Access Control (RBAC)

Authentication protocols

Basic Access Authentication

In the context of an HTTP transaction, basic access authentication is a method for an HTTP user agent (e.g. a web browser) to provide a user name and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where credentials is the Base64 encoding of ID and password joined by a single colon :.

Server side

When the server wants the user agent to authenticate itself towards the server after receiving an unauthenticated request, it must send a response with a HTTP 401 Unauthorized status line and a WWW-Authenticate header field.

The WWW-Authenticate header field for basic authentication is constructed as following:

1
WWW-Authenticate: Basic realm="User Visible Realm"

Client side

For example, if the browser uses Aladdin as the username and open sesame as the password, then the field’s value is the Base64 encoding of Aladdin:open sesame, or QWxhZGRpbjpvcGVuIHNlc2FtZQ==. Then the Authorization header field will appear as:

1
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

OpenID

OIDC (OpenID Connect)

OIDC is an authentication layer built on top of OAuth 2.0 and uses an additional JSON Web Token (JWT), called an ID token, to standardize areas that OAuth 2.0 leaves up to choice.

SAML (Security Assertion Markup Language)

Authentication Methods

Social Authentication

Single-Factor/Primary Authentication (SFA)

Two-Factor Authentication (2FA)

Three-Factor Authentication (3FA)

Single Sign-on (SSO)

Multifactor Authentication (MFA)

Reference

How Authentication is Different from Authorization

Introduction to JSON Web Tokens

PyJWT

http://www.cnblogs.com/zhaojianwei/p/4667864.html

https://github.com/edx/edx-oauth2-provider