...

Text file src/github.com/docker/distribution/docs/spec/auth/jwt.md

Documentation: github.com/docker/distribution/docs/spec/auth

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

View as plain text