|
| 1 | +--- |
| 2 | +title: Registry authentication |
| 3 | +description: "Specifies the Docker Registry v2 authentication" |
| 4 | +keywords: registry, images, tags, repository, distribution, Bearer authentication, advanced |
| 5 | +--- |
| 6 | + |
| 7 | +This document outlines the registry authentication scheme: |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +1. Attempt to begin a push/pull operation with the registry. |
| 12 | +2. If the registry requires authorization it will return a `401 Unauthorized` |
| 13 | + HTTP response with information on how to authenticate. |
| 14 | +3. The registry client makes a request to the authorization service for a |
| 15 | + Bearer token. |
| 16 | +4. The authorization service returns an opaque Bearer token representing the |
| 17 | + client's authorized access. |
| 18 | +5. The client retries the original request with the Bearer token embedded in |
| 19 | + the request's Authorization header. |
| 20 | +6. The Registry authorizes the client by validating the Bearer token and the |
| 21 | + claim set embedded within it and begins the push/pull session as usual. |
| 22 | + |
| 23 | +## Requirements |
| 24 | + |
| 25 | +- Registry clients which can understand and respond to token auth challenges |
| 26 | + returned by the resource server. |
| 27 | +- An authorization server capable of managing access controls to their |
| 28 | + resources hosted by any given service (such as repositories in a Docker |
| 29 | + Registry). |
| 30 | +- A Docker Registry capable of trusting the authorization server to sign tokens |
| 31 | + which clients can use for authorization and the ability to verify these |
| 32 | + tokens for single use or for use during a sufficiently short period of time. |
| 33 | + |
| 34 | +## Authorization server endpoint descriptions |
| 35 | + |
| 36 | +The described server is meant to serve as a standalone access control manager |
| 37 | +for resources hosted by other services which want to authenticate and manage |
| 38 | +authorizations using a separate access control manager. |
| 39 | + |
| 40 | +A service like this is used by the official Docker Registry to authenticate |
| 41 | +clients and verify their authorization to Docker image repositories. |
| 42 | + |
| 43 | +As of Docker 1.6, the registry client within the Docker Engine has been updated |
| 44 | +to handle such an authorization workflow. |
| 45 | + |
| 46 | +## How to authenticate |
| 47 | + |
| 48 | +Registry V1 clients first contact the index to initiate a push or pull. Under |
| 49 | +the Registry V2 workflow, clients should contact the registry first. If the |
| 50 | +registry server requires authentication it will return a `401 Unauthorized` |
| 51 | +response with a `WWW-Authenticate` header detailing how to authenticate to this |
| 52 | +registry. |
| 53 | + |
| 54 | +For example, say I (username `jlhawn`) am attempting to push an image to the |
| 55 | +repository `samalba/my-app`. For the registry to authorize this, I will need |
| 56 | +`push` access to the `samalba/my-app` repository. The registry will first |
| 57 | +return this response: |
| 58 | + |
| 59 | +```text |
| 60 | +HTTP/1.1 401 Unauthorized |
| 61 | +Content-Type: application/json; charset=utf-8 |
| 62 | +Docker-Distribution-Api-Version: registry/2.0 |
| 63 | +Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push" |
| 64 | +Date: Thu, 10 Sep 2015 19:32:31 GMT |
| 65 | +Content-Length: 235 |
| 66 | +Strict-Transport-Security: max-age=31536000 |
| 67 | +
|
| 68 | +{"errors":[{"code":"UNAUTHORIZED","message":"access to the requested resource is not authorized","detail":[{"Type":"repository","Name":"samalba/my-app","Action":"pull"},{"Type":"repository","Name":"samalba/my-app","Action":"push"}]}]} |
| 69 | +``` |
| 70 | + |
| 71 | +Note the HTTP Response Header indicating the auth challenge: |
| 72 | + |
| 73 | +```text |
| 74 | +Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:samalba/my-app:pull,push" |
| 75 | +``` |
| 76 | + |
| 77 | +This format is documented in [Section 3 of RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-3) |
| 78 | + |
| 79 | +This challenge indicates that the registry requires a token issued by the |
| 80 | +specified token server and that the request the client is attempting will |
| 81 | +need to include sufficient access entries in its claim set. To respond to this |
| 82 | +challenge, the client will need to make a `GET` request to the URL |
| 83 | +`https://auth.docker.io/token` using the `service` and `scope` values from the |
| 84 | +`WWW-Authenticate` header. |
| 85 | + |
| 86 | +## Requesting a token |
| 87 | + |
| 88 | +Defines getting a bearer and refresh token using the token endpoint. |
| 89 | + |
| 90 | +### Query parameters |
| 91 | + |
| 92 | +#### `service` |
| 93 | + |
| 94 | +The name of the service which hosts the resource. |
| 95 | + |
| 96 | +#### `offline_token` |
| 97 | + |
| 98 | +Whether to return a refresh token along with the bearer token. A refresh token |
| 99 | +is capable of getting additional bearer tokens for the same subject with |
| 100 | +different scopes. The refresh token does not have an expiration and should be |
| 101 | +considered completely opaque to the client. |
| 102 | + |
| 103 | +#### `client_id` |
| 104 | + |
| 105 | +String identifying the client. This `client_id` does not need to be registered |
| 106 | +with the authorization server but should be set to a meaningful value in order |
| 107 | +to allow auditing keys created by unregistered clients. Accepted syntax is |
| 108 | +defined in [RFC6749 Appendix |
| 109 | +A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1). |
| 110 | + |
| 111 | +#### `scope` |
| 112 | + |
| 113 | +The resource in question, formatted as one of the space-delimited entries from |
| 114 | +the `scope` parameters from the `WWW-Authenticate` header shown previously. This |
| 115 | +query parameter should be specified multiple times if there is more than one |
| 116 | +`scope` entry from the `WWW-Authenticate` header. The previous example would be |
| 117 | +specified as: `scope=repository:samalba/my-app:push`. The scope field may be |
| 118 | +empty to request a refresh token without providing any resource permissions to |
| 119 | +the returned bearer token. |
| 120 | + |
| 121 | +### Token response fields |
| 122 | + |
| 123 | +#### `token` |
| 124 | + |
| 125 | +An opaque `Bearer` token that clients should supply to subsequent |
| 126 | +requests in the `Authorization` header. |
| 127 | + |
| 128 | +#### `access_token` |
| 129 | + |
| 130 | +For compatibility with OAuth 2.0, the `token` under the name `access_token` is |
| 131 | +also accepted. At least one of these fields must be specified, but both may |
| 132 | +also appear (for compatibility with older clients). When both are specified, |
| 133 | +they should be equivalent; if they differ the client's choice is undefined. |
| 134 | + |
| 135 | +#### `expires_in` |
| 136 | + |
| 137 | +(Optional) The duration in seconds since the token was issued that it will |
| 138 | +remain valid. When omitted, this defaults to 60 seconds. For compatibility |
| 139 | +with older clients, a token should never be returned with less than 60 seconds |
| 140 | +to live. |
| 141 | + |
| 142 | +#### `issued_at` |
| 143 | + |
| 144 | +(Optional) The [RFC3339](https://www.ietf.org/rfc/rfc3339.txt)-serialized UTC |
| 145 | +standard time at which a given token was issued. If `issued_at` is omitted, the |
| 146 | +expiration is from when the token exchange completed. |
| 147 | + |
| 148 | +#### `refresh_token` |
| 149 | + |
| 150 | +(Optional) Token which can be used to get additional access tokens for |
| 151 | +the same subject with different scopes. This token should be kept secure |
| 152 | +by the client and only sent to the authorization server which issues |
| 153 | +bearer tokens. This field will only be set when `offline_token=true` is |
| 154 | +provided in the request. |
| 155 | + |
| 156 | +### Example |
| 157 | + |
| 158 | +For this example, the client makes an HTTP GET request to the following URL: |
| 159 | + |
| 160 | +```text |
| 161 | +https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push |
| 162 | +``` |
| 163 | + |
| 164 | +The token server should first attempt to authenticate the client using any |
| 165 | +authentication credentials provided with the request. From Docker 1.11 the |
| 166 | +Docker Engine supports both Basic Authentication and OAuth2 for |
| 167 | +getting tokens. Docker 1.10 and before, the registry client in the Docker Engine |
| 168 | +only supports Basic Authentication. If an attempt to authenticate to the token |
| 169 | +server fails, the token server should return a `401 Unauthorized` response |
| 170 | +indicating that the provided credentials are invalid. |
| 171 | + |
| 172 | +Whether the token server requires authentication is up to the policy of that |
| 173 | +access control provider. Some requests may require authentication to determine |
| 174 | +access (such as pushing or pulling a private repository) while others may not |
| 175 | +(such as pulling from a public repository). |
| 176 | + |
| 177 | +After authenticating the client (which may simply be an anonymous client if |
| 178 | +no attempt was made to authenticate), the token server must next query its |
| 179 | +access control list to determine whether the client has the requested scope. In |
| 180 | +this example request, if I have authenticated as user `jlhawn`, the token |
| 181 | +server will determine what access I have to the repository `samalba/my-app` |
| 182 | +hosted by the entity `registry.docker.io`. |
| 183 | + |
| 184 | +Once the token server has determined what access the client has to the |
| 185 | +resources requested in the `scope` parameter, it will take the intersection of |
| 186 | +the set of requested actions on each resource and the set of actions that the |
| 187 | +client has in fact been granted. If the client only has a subset of the |
| 188 | +requested access **it must not be considered an error** as it is not the |
| 189 | +responsibility of the token server to indicate authorization errors as part of |
| 190 | +this workflow. |
| 191 | + |
| 192 | +Continuing with the example request, the token server will find that the |
| 193 | +client's set of granted access to the repository is `[pull, push]` which when |
| 194 | +intersected with the requested access `[pull, push]` yields an equal set. If |
| 195 | +the granted access set was found only to be `[pull]` then the intersected set |
| 196 | +would only be `[pull]`. If the client has no access to the repository then the |
| 197 | +intersected set would be empty, `[]`. |
| 198 | + |
| 199 | +It is this intersected set of access which is placed in the returned token. |
| 200 | + |
| 201 | +The server then constructs an implementation-specific token with this |
| 202 | +intersected set of access, and returns it to the Docker client to use to |
| 203 | +authenticate to the audience service (within the indicated window of time): |
| 204 | + |
| 205 | +```text |
| 206 | +HTTP/1.1 200 OK |
| 207 | +Content-Type: application/json |
| 208 | +
|
| 209 | +{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlBZWU86VEVXVTpWN0pIOjI2SlY6QVFUWjpMSkMzOlNYVko6WEdIQTozNEYyOjJMQVE6WlJNSzpaN1E2In0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJqbGhhd24iLCJhdWQiOiJyZWdpc3RyeS5kb2NrZXIuY29tIiwiZXhwIjoxNDE1Mzg3MzE1LCJuYmYiOjE0MTUzODcwMTUsImlhdCI6MTQxNTM4NzAxNSwianRpIjoidFlKQ08xYzZjbnl5N2tBbjBjN3JLUGdiVjFIMWJGd3MiLCJhY2Nlc3MiOlt7InR5cGUiOiJyZXBvc2l0b3J5IiwibmFtZSI6InNhbWFsYmEvbXktYXBwIiwiYWN0aW9ucyI6WyJwdXNoIl19XX0.QhflHPfbd6eVF4lM9bwYpFZIV0PfikbyXuLx959ykRTBpe3CYnzs6YBK8FToVb5R47920PVLrh8zuLzdCr9t3w", "expires_in": 3600,"issued_at": "2009-11-10T23:00:00Z"} |
| 210 | +``` |
| 211 | + |
| 212 | +## Using the Bearer token |
| 213 | + |
| 214 | +Once the client has a token, it will try the registry request again with the |
| 215 | +token placed in the HTTP `Authorization` header like so: |
| 216 | + |
| 217 | +```text |
| 218 | +Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IkJWM0Q6MkFWWjpVQjVaOktJQVA6SU5QTDo1RU42Ok40SjQ6Nk1XTzpEUktFOkJWUUs6M0ZKTDpQT1RMIn0.eyJpc3MiOiJhdXRoLmRvY2tlci5jb20iLCJzdWIiOiJCQ0NZOk9VNlo6UUVKNTpXTjJDOjJBVkM6WTdZRDpBM0xZOjQ1VVc6NE9HRDpLQUxMOkNOSjU6NUlVTCIsImF1ZCI6InJlZ2lzdHJ5LmRvY2tlci5jb20iLCJleHAiOjE0MTUzODczMTUsIm5iZiI6MTQxNTM4NzAxNSwiaWF0IjoxNDE1Mzg3MDE1LCJqdGkiOiJ0WUpDTzFjNmNueXk3a0FuMGM3cktQZ2JWMUgxYkZ3cyIsInNjb3BlIjoiamxoYXduOnJlcG9zaXRvcnk6c2FtYWxiYS9teS1hcHA6cHVzaCxwdWxsIGpsaGF3bjpuYW1lc3BhY2U6c2FtYWxiYTpwdWxsIn0.Y3zZSwaZPqy4y9oRBVRImZyv3m_S9XDHF1tWwN7mL52C_IiA73SJkWVNsvNqpJIn5h7A2F8biv_S2ppQ1lgkbw |
| 219 | +``` |
| 220 | + |
| 221 | +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) |
0 commit comments