github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/docs/spec/auth/jwt.md (about)

     1  <!--[metadata]>
     2  +++
     3  title = "Token Authentication Implementation"
     4  description = "Describe the reference implementation of the Docker Registry v2 authentication schema"
     5  keywords = ["registry, on-prem, images, tags, repository, distribution, JWT authentication, advanced"]
     6  [menu.main]
     7  parent="smn_registry_ref"
     8  +++
     9  <![end-metadata]-->
    10  
    11  # Docker Registry v2 Bearer token specification
    12  
    13  This specification covers the `docker/distribution` implementation of the
    14  v2 Registry's authentication schema.  Specifically, it describes the JSON
    15  Web Token schema that `docker/distribution` has adopted to implement the
    16  client-opaque Bearer token issued by an authentication service and
    17  understood by the registry.
    18  
    19  This document borrows heavily from the [JSON Web Token Draft Spec](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32)
    20  
    21  ## Getting a Bearer Token
    22  
    23  For this example, the client makes an HTTP GET request to the following URL:
    24  
    25  ```
    26  https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
    27  ```
    28  
    29  The token server should first attempt to authenticate the client using any
    30  authentication credentials provided with the request. As of Docker 1.8, the
    31  registry client in the Docker Engine only supports Basic Authentication to
    32  these token servers. If an attempt to authenticate to the token server fails,
    33  the token server should return a `401 Unauthorized` response indicating that
    34  the provided credentials are invalid.
    35  
    36  Whether the token server requires authentication is up to the policy of that
    37  access control provider. Some requests may require authentication to determine
    38  access (such as pushing or pulling a private repository) while others may not
    39  (such as pulling from a public repository).
    40  
    41  After authenticating the client (which may simply be an anonymous client if
    42  no attempt was made to authenticate), the token server must next query its
    43  access control list to determine whether the client has the requested scope. In
    44  this example request, if I have authenticated as user `jlhawn`, the token
    45  server will determine what access I have to the repository `samalba/my-app`
    46  hosted by the entity `registry.docker.io`.
    47  
    48  Once the token server has determined what access the client has to the
    49  resources requested in the `scope` parameter, it will take the intersection of
    50  the set of requested actions on each resource and the set of actions that the
    51  client has in fact been granted. If the client only has a subset of the
    52  requested access **it must not be considered an error** as it is not the
    53  responsibility of the token server to indicate authorization errors as part of
    54  this workflow.
    55  
    56  Continuing with the example request, the token server will find that the
    57  client's set of granted access to the repository is `[pull, push]` which when
    58  intersected with the requested access `[pull, push]` yields an equal set. If
    59  the granted access set was found only to be `[pull]` then the intersected set
    60  would only be `[pull]`. If the client has no access to the repository then the
    61  intersected set would be empty, `[]`.
    62  
    63  It is this intersected set of access which is placed in the returned token.
    64  
    65  The server will now construct a JSON Web Token to sign and return. A JSON Web
    66  Token has 3 main parts:
    67  
    68  1.  Headers
    69  
    70      The header of a JSON Web Token is a standard JOSE header. The "typ" field
    71      will be "JWT" and it will also contain the "alg" which identifies the
    72      signing algorithm used to produce the signature. It will also usually have
    73      a "kid" field, the ID of the key which was used to sign the token.
    74  
    75      Here is an example JOSE Header for a JSON Web Token (formatted with
    76      whitespace for readability):
    77  
    78      ```
    79      {
    80          "typ": "JWT",
    81          "alg": "ES256",
    82          "kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"
    83      }
    84      ```
    85  
    86      It specifies that this object is going to be a JSON Web token signed using
    87      the key with the given ID using the Elliptic Curve signature algorithm
    88      using a SHA256 hash.
    89  
    90  2.  Claim Set
    91  
    92      The Claim Set is a JSON struct containing these standard registered claim
    93      name fields:
    94  
    95      <dl>
    96          <dt>
    97              <code>iss</code> (Issuer)
    98          </dt>
    99          <dd>
   100              The issuer of the token, typically the fqdn of the authorization
   101              server.
   102          </dd>
   103          <dt>
   104              <code>sub</code> (Subject)
   105          </dt>
   106          <dd>
   107              The subject of the token; the name or id of the client which
   108              requested it. This should be empty (`""`) if the client did not
   109              authenticate.
   110          </dd>
   111          <dt>
   112              <code>aud</code> (Audience)
   113          </dt>
   114          <dd>
   115              The intended audience of the token; the name or id of the service
   116              which will verify the token to authorize the client/subject.
   117          </dd>
   118          <dt>
   119              <code>exp</code> (Expiration)
   120          </dt>
   121          <dd>
   122              The token should only be considered valid up to this specified date
   123              and time.
   124          </dd>
   125          <dt>
   126              <code>nbf</code> (Not Before)
   127          </dt>
   128          <dd>
   129              The token should not be considered valid before this specified date
   130              and time.
   131          </dd>
   132          <dt>
   133              <code>iat</code> (Issued At)
   134          </dt>
   135          <dd>
   136              Specifies the date and time which the Authorization server
   137              generated this token.
   138          </dd>
   139          <dt>
   140              <code>jti</code> (JWT ID)
   141          </dt>
   142          <dd>
   143              A unique identifier for this token. Can be used by the intended
   144              audience to prevent replays of the token.
   145          </dd>
   146      </dl>
   147  
   148      The Claim Set will also contain a private claim name unique to this
   149      authorization server specification:
   150  
   151      <dl>
   152          <dt>
   153              <code>access</code>
   154          </dt>
   155          <dd>
   156              An array of access entry objects with the following fields:
   157  
   158              <dl>
   159                  <dt>
   160                      <code>type</code>
   161                  </dt>
   162                  <dd>
   163                      The type of resource hosted by the service.
   164                  </dd>
   165                  <dt>
   166                      <code>name</code>
   167                  </dt>
   168                  <dd>
   169                      The name of the resource of the given type hosted by the
   170                      service.
   171                  </dd>
   172                  <dt>
   173                      <code>actions</code>
   174                  </dt>
   175                  <dd>
   176                      An array of strings which give the actions authorized on
   177                      this resource.
   178                  </dd>
   179              </dl>
   180          </dd>
   181      </dl>
   182  
   183      Here is an example of such a JWT Claim Set (formatted with whitespace for
   184      readability):
   185  
   186      ```
   187      {
   188          "iss": "auth.docker.com",
   189          "sub": "jlhawn",
   190          "aud": "registry.docker.com",
   191          "exp": 1415387315,
   192          "nbf": 1415387015,
   193          "iat": 1415387015,
   194          "jti": "tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws",
   195          "access": [
   196              {
   197                  "type": "repository",
   198                  "name": "samalba/my-app",
   199                  "actions": [
   200                      "pull",
   201                      "push"
   202                  ]
   203              }
   204          ]
   205      }
   206      ```
   207  
   208  3.  Signature
   209  
   210      The authorization server will produce a JOSE header and Claim Set with no
   211      extraneous whitespace, i.e., the JOSE Header from above would be
   212  
   213      ```
   214      {"typ":"JWT","alg":"ES256","kid":"PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6"}
   215      ```
   216  
   217      and the Claim Set from above would be
   218  
   219      ```
   220      {"iss":"auth.docker.com","sub":"jlhawn","aud":"registry.docker.com","exp":1415387315,"nbf":1415387015,"iat":1415387015,"jti":"tYJCO1c6cnyy7kAn0c7rKPgbV1H1bFws","access":[{"type":"repository","name":"samalba/my-app","actions":["push","pull"]}]}
   221      ```
   222  
   223      The utf-8 representation of this JOSE header and Claim Set are then
   224      url-safe base64 encoded (sans trailing '=' buffer), producing:
   225  
   226      ```
   227      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0
   228      ```
   229  
   230      for the JOSE Header and
   231  
   232      ```
   233      eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
   234      ```
   235  
   236      for the Claim Set. These two are concatenated using a '.' character,
   237      yielding the string:
   238  
   239      ```
   240      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0
   241      ```
   242  
   243      This is then used as the payload to a the `ES256` signature algorithm
   244      specified in the JOSE header and specified fully in [Section 3.4 of the JSON Web Algorithms (JWA)
   245      draft specification](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-38#section-3.4)
   246  
   247      This example signature will use the following ECDSA key for the server:
   248  
   249      ```
   250      {
   251          "kty": "EC",
   252          "crv": "P-256",
   253          "kid": "PYYO:TEWU:V7JH:26JV:AQTZ:LJC3:SXVJ:XGHA:34F2:2LAQ:ZRMK:Z7Q6",
   254          "d": "R7OnbfMaD5J2jl7GeE8ESo7CnHSBm_1N2k9IXYFrKJA",
   255          "x": "m7zUpx3b-zmVE5cymSs64POG9QcyEpJaYCD82-549_Q",
   256          "y": "dU3biz8sZ_8GPB-odm8Wxz3lNDr1xcAQQPQaOcr1fmc"
   257      }
   258      ```
   259  
   260      A resulting signature of the above payload using this key is:
   261  
   262      ```
   263      QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
   264      ```
   265  
   266      Concatenating all of these together with a `.` character gives the
   267      resulting JWT:
   268  
   269      ```
   270      eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w
   271      ```
   272  
   273  This can now be placed in an HTTP response and returned to the client to use to
   274  authenticate to the audience service:
   275  
   276  
   277  ```
   278  HTTP/1.1 200 OK
   279  Content-Type: application/json
   280  
   281  {"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w"}
   282  ```
   283  
   284  ## Using the signed token
   285  
   286  Once the client has a token, it will try the registry request again with the
   287  token placed in the HTTP `Authorization` header like so:
   288  
   289  ```
   290  Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw
   291  ```
   292  
   293  This is also described in [Section 2.1 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1)
   294  
   295  ## Verifying the token
   296  
   297  The registry must now verify the token presented by the user by inspecting the
   298  claim set within. The registry will:
   299  
   300  - Ensure that the issuer (`iss` claim) is an authority it trusts.
   301  - Ensure that the registry identifies as the audience (`aud` claim).
   302  - Check that the current time is between the `nbf` and `exp` claim times.
   303  - If enforcing single-use tokens, check that the JWT ID (`jti` claim) value has
   304    not been seen before.
   305    - To enforce this, the registry may keep a record of `jti`s it has seen for
   306      up to the `exp` time of the token to prevent token replays.
   307  - Check the `access` claim value and use the identified resources and the list
   308    of actions authorized to determine whether the token grants the required
   309    level of access for the operation the client is attempting to perform.
   310  - Verify that the signature of the token is valid.
   311  
   312  If any of these requirements are not met, the registry will return a
   313  `403 Forbidden` response to indicate that the token is invalid.
   314  
   315  **Note**: it is only at this point in the workflow that an authorization error
   316  may occur. The token server should *not* return errors when the user does not
   317  have the requested authorization. Instead, the returned token should indicate
   318  whatever of the requested scope the client does have (the intersection of
   319  requested and granted access). If the token does not supply proper
   320  authorization then the registry will return the appropriate error.
   321  
   322  At no point in this process should the registry need to call back to the
   323  authorization server. The registry only needs to be supplied with the trusted
   324  public keys to verify the token signatures.