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 5HMAC_SHA256( secret, base64urlEncoding(header) + '.' + base64urlEncoding(payload) )
The three parts are encoded separately using Base64url Encoding, and concatenated using periods to produce the JWT:
|
|
The above data and the secret of “secretkey” creates the token:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
Encoding & Decoding Tokens with HS256
|
|
Encoding & Decoding Tokens with RS256 (RSA)
|
|
Specifying Additional Headers
|
|
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
|
|
|
|
|
|
|
|
How to request an access token for the first time ?
-
Create a client entry in your database
To find out which type of
clientyou need to create, read Section 2.1.To create a new entry simply use the Django admin panel.
-
Request an access token
Your client needs to submit a
POSTrequest to/oauth2/access_tokenincluding 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
-
Request URL:
http://localhost:18381/api-auth/login/?next=/api/v1/courses/Query String Parameters:
next: /api/v1/courses/Request Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18381/login/edx-oidc/?next=/api/v1/courses/Query String Parameters:
next: /api/v1/courses/Request Method:
GETStatus Code:
302 Found -
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=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4xQuery 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:
GETStatus Code:
302 Found - redirect_uri:
-
Request URL:
http://localhost:18000/oauth2/authorize/confirmRequest Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18000/login?next=/oauth2/authorize/confirmQuery String Parameters:
/oauth2/authorize/confirmRequest Method:
GETStatus Code:
200 OK -
Request URL:
http://localhost:18000/login_ajaxForm Data:
- email:
edx@example.com - password:
edx
Request Method:
POSTStatus Code:
200 OKResponse Headers:
- Set-Cookie:
edxloggedin=true; Path=/
- email:
-
Request URL:
http://localhost:18000/oauth2/authorize/confirmRequest Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18000/oauth2/redirectRequest Method:
GETStatus Code:
302 FoundResponse Headers:
- Set-Cookie:
sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; httponly; Path=/
- Set-Cookie:
-
Request URL:
http://localhost:18381/complete/edx-oidc/?state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x&code=7ab4b799ee2a6abe7e0f81affed2c69e1da96e02Query String Parameters:
- state:
W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x - code:
7ab4b799ee2a6abe7e0f81affed2c69e1da96e02
Request Method:
GETRequest Headers:
-
Cookie:
course_discovery_sessionid=z847akheu4f11guh1nyc230f2xrr8omx; sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"
Status Code:
302 FoundResponse Headers:
- Set-Cookie:
course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg; expires=Sat, 28-Jul-2018 13:34:35 GMT; HttpOnly; Max-Age=1209600; Path=/
- state:
-
Request URL:
http://localhost:18381/api/v1/courses/Request Method:
GETRequest 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.
- JwtAuthentication is implemented in the edx-drf-extensions library.
- JwtAuthentication extends the JSONWebTokenAuthentication class implemented in the django-rest-framework-jwt library.
- JwtAuthentication is used to authenticate an API request only if it is listed in the endpoint’s authentication_classes and the request’s Authorization header specifies “JWT” instead of “Bearer”.
- Note: The Credentials service has its own implementation of JwtAuthentication and should be converted to use the common implementation in edx-drf-extensions.
auth-backends
Configuration
Adding single sign-on/out support to a service requires a few changes:
- Define settings
- Add the authentication backend
- Add the login/logout redirects
edx-rest-api-client
edx-drf-extensions.
edx oauth
-
Request URL:
http://localhost:18381/api-auth/login/?next=/api/v1/courses/Query String Parameters:
next: /api/v1/courses/Request Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18381/login/edx-oidc/?next=/api/v1/courses/Query String Parameters:
next: /api/v1/courses/Request Method:
GETStatus Code:
302 Found -
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=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4xQuery 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:
GETStatus Code:
302 Found - redirect_uri:
-
Request URL:
http://localhost:18000/oauth2/authorize/confirmRequest Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18000/login?next=/oauth2/authorize/confirmQuery String Parameters:
/oauth2/authorize/confirmRequest Method:
GETStatus Code:
200 OK -
Request URL:
http://localhost:18000/login_ajaxForm Data:
- email:
edx@example.com - password:
edx
Request Method:
POSTStatus Code:
200 OKResponse Headers:
- Set-Cookie:
edxloggedin=true; Path=/
- email:
-
Request URL:
http://localhost:18000/oauth2/authorize/confirmRequest Method:
GETStatus Code:
302 Found -
Request URL:
http://localhost:18000/oauth2/redirectRequest Method:
GETStatus Code:
302 FoundResponse Headers:
- Set-Cookie:
sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"; httponly; Path=/
- Set-Cookie:
-
Request URL:
http://localhost:18381/complete/edx-oidc/?state=W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x&code=7ab4b799ee2a6abe7e0f81affed2c69e1da96e02Query String Parameters:
- state:
W7KfXvcIA9Zg2DRRIWADlGDIY86gTt4x - code:
7ab4b799ee2a6abe7e0f81affed2c69e1da96e02
Request Method:
GETRequest Headers:
-
Cookie:
course_discovery_sessionid=z847akheu4f11guh1nyc230f2xrr8omx; sessionid="1|trhqixsrnzyoguusyir2i4frlptzngv3|SaOdjRQ7ARrj|ImJkZmQ5YTQ2YTcwYzBjNWZhZmI4MTMxZmE3MzM4MDA2MDMyM2M2NjJjNWU4YTZhY2Y5NzdkNjgxMjJmOWQwNWUi:1feKh1:OfZiGoeNnqqzbnbv0cqWgBnAvhk"
Status Code:
302 FoundResponse Headers:
- Set-Cookie:
course_discovery_sessionid=vkas33o90yjc3hn4s2ctdjp1uwfb9hrg; expires=Sat, 28-Jul-2018 13:34:35 GMT; HttpOnly; Max-Age=1209600; Path=/
- state:
-
Request URL:
http://localhost:18381/api/v1/courses/Request Method:
GETRequest 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:
|
|
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:
|
|
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