Skip to content

Authentication

Django oci takes a “docker style” version of OAuth 2.0. Details can be seen in this issue discussion. This generally means the following:

1. Unauthorized Response

If authentication is enabled, meaning that DISABLE_AUTHENTICATION is False in the django OCI settings, views that require some kind of retrieval, change, or creation of content will return a 401 “Unauthorized” response. Note that if you set DISABLE_AUTHENTICATION to True, this won’t happen (but it’s hugely discouraged to have a registry that doesn’t have Authentication, unless you have some specific, strong use case).

2. Www-Authenticate Header

The registry knows to return a Www-Authenticate header to the client with information about how to request a token. That might look something like:

realm="http://127.0.0.1/auth",service="http://127.0.0.1",scope="repository:vanessa/container:push,pull"

Note that realm is typically referring to the authentication server, and the service is the container registry. In the case of Django OCI they are one and the same (e.g., both on localhost) but this doesn’t have to be the case.

3. Token Request

The client then submits a request to the realm with those variables as query parameters (e.g., GET) and also provides a basic authentication header, which in the case of Django OCI, is the user’s username and token associated with the account, which is generated by the Rest Framework on creation, and can be re-generated by the user. We put them together as follows:

"username:token"

And then base64 encode that, and add it to the http Authorization header.

{"Authorization": "Basic <base64 encoded username and token>"}

That request then goes to the authorization realm, which determines if the user has permission to access resource requested, and for the scope needed.

4. Token Generation

Given that the user account is valid, meaning that we check that the username exists, the token is correct, and the user has permission for the scopes requested for the repository, we generate a jwt token that looks like the following:

{
  "iss": "auth.docker.com",
  "sub": "jlhawn",
  "exp": 1415387315,
  "nbf": 1415387015,
  "iat": 1415387015,
  "jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
  "access": [
    {
      "type": "repository",
      "name": "samalba/my-app",
      "actions": [
        "push"
      ]
    }
  ]
}

The “exp” field is the timestamp for when the token expires. The nbf says “This can’t be used before this timestamp,” and iat refers to the issued at timestamp. You can read more about jwt here. We basically use a python jwt library to encode this into a long token using a secret on the server, and return this token to the calling client.

{"token": "1sdjkjf....xxsdfser", "issued_at": "<issued timestamp>", "expires_in": 600}

5. Request retry

The client then retries the same request, but added the token to the Authorization header, this time with Bearer.

{"Authorization": "Bearer <token>"}

And then hooray! The request should be successful, along with subsequent requests using the token until it expires. For more detail about Authorization, we recommend that you reference the reggie Authentication tutorial.