forked from TrueCloudLab/distribution
Merge pull request #1418 from dmcgowan/oauth-spec
Add specification for using OAuth with the token server
This commit is contained in:
commit
a11f6b6cfd
4 changed files with 368 additions and 8 deletions
|
@ -8,5 +8,7 @@ keywords = ["registry, on-prem, images, tags, repository, distribution, authenti
|
||||||
|
|
||||||
# Docker Registry v2 authentication
|
# Docker Registry v2 authentication
|
||||||
|
|
||||||
See the [Token Authentication Specification](token.md) and
|
See the [Token Authentication Specification](token.md),
|
||||||
[Token Authentication Implementation](jwt.md) for more information.
|
[Token Authentication Implementation](jwt.md),
|
||||||
|
[Token Scope Documentation](scope.md),
|
||||||
|
[OAuth2 Token Authentication](oauth.md) for more information.
|
||||||
|
|
190
docs/spec/auth/oauth.md
Normal file
190
docs/spec/auth/oauth.md
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Oauth2 Token Authentication"
|
||||||
|
description = "Specifies the Docker Registry v2 authentication"
|
||||||
|
keywords = ["registry, on-prem, images, tags, repository, distribution, oauth2, advanced"]
|
||||||
|
[menu.main]
|
||||||
|
parent="smn_registry_ref"
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Docker Registry v2 authentication using OAuth2
|
||||||
|
|
||||||
|
This document describes support for the OAuth2 protocol within the authorization
|
||||||
|
server. [RFC6749](https://tools.ietf.org/html/rfc6749) should be used as a
|
||||||
|
reference for the protocol and HTTP endpoints described here.
|
||||||
|
|
||||||
|
## Refresh token format
|
||||||
|
|
||||||
|
The format of the refresh token is completely opaque to the client and should be
|
||||||
|
determined by the authorization server. The authorization should ensure the
|
||||||
|
token is sufficiently long and is responsible for storing any information about
|
||||||
|
long-lived tokens which may be needed for revoking. Any information stored
|
||||||
|
inside the token will not be extracted and presented by clients.
|
||||||
|
|
||||||
|
## Getting a token
|
||||||
|
|
||||||
|
POST /token
|
||||||
|
|
||||||
|
#### Headers
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
|
||||||
|
#### Post parameters
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>
|
||||||
|
<code>grant_type</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) Type of grant used to get token. When getting a refresh token
|
||||||
|
using credentials this type should be set to "password" and have the
|
||||||
|
accompanying username and password paramters. Type "authorization_code"
|
||||||
|
is reserved for future use for authenticating to an authorization server
|
||||||
|
without having to send credentials directly from the client. When
|
||||||
|
requesting an access token with a refresh token this should be set to
|
||||||
|
"refresh_token".
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>service</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) The name of the service which hosts the resource to get
|
||||||
|
access for. Refresh tokens will only be good for getting tokens for
|
||||||
|
this service.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>client_id</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) String identifying the client. This client_id does not need
|
||||||
|
to be registered with the authorization server but should be set to a
|
||||||
|
meaningful value in order to allow auditing keys created by unregistered
|
||||||
|
clients. Accepted syntax is defined in
|
||||||
|
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1)
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>access_type</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(OPTIONAL) Access which is being requested. If "offline" is provided
|
||||||
|
then a refresh token will be returned. The default is "online" only
|
||||||
|
returning short lived access token. If the grant type is "refresh_token"
|
||||||
|
this will only return the same refresh token and not a new one.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>scope</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(OPTIONAL) The resource in question, formatted as one of the space-delimited
|
||||||
|
entries from the <code>scope</code> parameters from the <code>WWW-Authenticate</code> header
|
||||||
|
shown above. This query parameter should only be specified once but may
|
||||||
|
contain multiple scopes using the scope list format defined in the scope
|
||||||
|
grammar. If multiple <code>scope</code> is provided from
|
||||||
|
<code>WWW-Authenticate</code> header the scopes should first be
|
||||||
|
converted to a scope list before requesting the token. The above example
|
||||||
|
would be specified as: <code>scope=repository:samalba/my-app:push</code>.
|
||||||
|
When requesting a refresh token the scopes may be empty since the
|
||||||
|
refresh token will not be limited by this scope, only the provided short
|
||||||
|
lived access token will have the scope limitation.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>refresh_token</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(OPTIONAL) The refresh token to use for authentication when grant type "refresh_token" is used.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>username</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(OPTIONAL) The username to use for authentication when grant type "password" is used.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>password</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(OPTIONAL) The password to use for authentication when grant type "password" is used.
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
#### Response fields
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>
|
||||||
|
<code>access_token</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) An opaque <code>Bearer</code> token that clients should
|
||||||
|
supply to subsequent requests in the <code>Authorization</code> header.
|
||||||
|
This token should not be attempted to be parsed or understood by the
|
||||||
|
client but treated as opaque string.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>scope</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) The scope granted inside the access token. This may be the
|
||||||
|
same scope as requested or a subset. This requirement is stronger than
|
||||||
|
specified in [RFC6749 Section 4.2.2](https://tools.ietf.org/html/rfc6749#section-4.2.2)
|
||||||
|
by strictly requiring the scope in the return value.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>expires_in</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(REQUIRED) The duration in seconds since the token was issued that it
|
||||||
|
will remain valid. When omitted, this defaults to 60 seconds. For
|
||||||
|
compatibility with older clients, a token should never be returned with
|
||||||
|
less than 60 seconds to live.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>issued_at</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(Optional) The <a href="https://www.ietf.org/rfc/rfc3339.txt">RFC3339</a>-serialized UTC
|
||||||
|
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
||||||
|
expiration is from when the token exchange completed.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>refresh_token</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(Optional) Token which can be used to get additional access tokens for
|
||||||
|
the same subject with different scopes. This token should be kept secure
|
||||||
|
by the client and only sent to the authorization server which issues
|
||||||
|
bearer tokens. This field will only be set when `access_type=offline` is
|
||||||
|
provided in the request.
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
#### Example getting refresh token
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /token HTTP/1.1
|
||||||
|
Host: auth.docker.io
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
|
||||||
|
grant_type=password&username=johndoe&password=A3ddj3w&service=hub.docker.io&client_id=dockerengine&access_type=offline
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5","expires_in":"900","scope":""}
|
||||||
|
````
|
||||||
|
|
||||||
|
#### Example refreshing an Access Token
|
||||||
|
|
||||||
|
````
|
||||||
|
POST /token HTTP/1.1
|
||||||
|
Host: auth.docker.io
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
|
||||||
|
grant_type=refresh_token&refresh_token=kas9Da81Dfa8&service=registry-1.docker.io&client_id=dockerengine&scope=repository:samalba/my-app:pull,push
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{"refresh_token":"kas9Da81Dfa8","access_token":"eyJhbGciOiJFUzI1NiIsInR5":"expires_in":"900","scope":"repository:samalba/my-app:pull,repository:samalba/my-app:push"}
|
||||||
|
````
|
||||||
|
|
134
docs/spec/auth/scope.md
Normal file
134
docs/spec/auth/scope.md
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
<!--[metadata]>
|
||||||
|
+++
|
||||||
|
title = "Token Scope Documentation"
|
||||||
|
description = "Describes the scope and access fields used for registry authorization tokens"
|
||||||
|
keywords = ["registry, on-prem, images, tags, repository, distribution, advanced, access, scope"]
|
||||||
|
[menu.main]
|
||||||
|
parent="smn_registry_ref"
|
||||||
|
+++
|
||||||
|
<![end-metadata]-->
|
||||||
|
|
||||||
|
# Docker Registry Token Scope and Access
|
||||||
|
|
||||||
|
Tokens used by the registry are always restricted what resources they may
|
||||||
|
be used to access, where those resources may be accessed, and what actions
|
||||||
|
may be done on those resources. Tokens always have the context of a user which
|
||||||
|
the token was originally created for. This document describes how these
|
||||||
|
restrictions are represented and enforced by the authorization server and
|
||||||
|
resource providers.
|
||||||
|
|
||||||
|
## Scope Components
|
||||||
|
|
||||||
|
### Subject (Authenticated User)
|
||||||
|
|
||||||
|
The subject represents the user for which a token is valid. Any actions
|
||||||
|
performed using an access token should be considered on behalf of the subject.
|
||||||
|
This is included in the `sub` field of access token JWT. A refresh token should
|
||||||
|
be limited to a single subject and only be able to give out access tokens for
|
||||||
|
that subject.
|
||||||
|
|
||||||
|
### Audience (Resource Provider)
|
||||||
|
|
||||||
|
The audience represents a resource provider which is intended to be able to
|
||||||
|
perform the actions specified in the access token. Any resource provider which
|
||||||
|
does not match the audience should not use that access token. The audience is
|
||||||
|
included in the `aud` field of the access token JWT. A refresh token should be
|
||||||
|
limited to a single audience and only be able to give out access tokens for that
|
||||||
|
audience.
|
||||||
|
|
||||||
|
### Resource Type
|
||||||
|
|
||||||
|
The resource type represents the type of resource which the resource name is
|
||||||
|
intended to represent. This type may be specific to a resource provider but must
|
||||||
|
be understood by the authorization server in order to validate the subject
|
||||||
|
is authorized for a specific resource.
|
||||||
|
|
||||||
|
#### Example Resource Types
|
||||||
|
|
||||||
|
- `repository` - represents a single repository within a registry. A
|
||||||
|
repository may represent many manifest or content blobs, but the resource type
|
||||||
|
is considered the collections of those items. Actions which may be performed on
|
||||||
|
a `repository` are `pull` for accessing the collection and `push` for adding to
|
||||||
|
it.
|
||||||
|
|
||||||
|
### Resource Name
|
||||||
|
|
||||||
|
The resource name represent the name which identifies a resource for a resource
|
||||||
|
provider. A resource is identified by this name and the provided resource type.
|
||||||
|
An example of a resource name would be the name component of an image tag, such
|
||||||
|
as "samalba/myapp".
|
||||||
|
|
||||||
|
### Resource Actions
|
||||||
|
|
||||||
|
The resource actions define the actions which the access token allows to be
|
||||||
|
performed on the identified resource. These actions are type specific but will
|
||||||
|
normally have actions identifying read and write access on the resource. Example
|
||||||
|
for the `repository` type are `pull` for read access and `push` for write
|
||||||
|
access.
|
||||||
|
|
||||||
|
## Authorization Server Use
|
||||||
|
|
||||||
|
Each access token request may include a scope and an audience. The subject is
|
||||||
|
always derived from the passed in credentials or refresh token. When using
|
||||||
|
a refresh token the passed in audience must match the audience defined for
|
||||||
|
the refresh token. The audience (resource provider) is provided using the
|
||||||
|
`service` field. Multiple resource scopes may be provided using multiple `scope`
|
||||||
|
fields on the `GET` request. The `POST` request only takes in a single
|
||||||
|
`scope` field but may use a space to separate a list of multiple resource
|
||||||
|
scopes.
|
||||||
|
|
||||||
|
### Resource Scope Grammar
|
||||||
|
|
||||||
|
```
|
||||||
|
scope := resourcescope [ ' ' resourcescope ]*
|
||||||
|
resourcescope := resourcetype ":" resourcename ":" action [ ',' action ]*
|
||||||
|
resourcetype := /[a-z]*/
|
||||||
|
resourcename := component [ '/' component ]*
|
||||||
|
action := /[a-z]*/
|
||||||
|
component := alpha-numeric [ separator alpha-numeric ]*
|
||||||
|
alpha-numeric := /[a-z0-9]+/
|
||||||
|
separator := /[_.]|__|[-]*/
|
||||||
|
```
|
||||||
|
Full reference grammar is defined
|
||||||
|
(here)[https://godoc.org/github.com/docker/distribution/reference]. Currently
|
||||||
|
the scope name grammar is a subset of the reference grammar without support
|
||||||
|
for hostnames.
|
||||||
|
|
||||||
|
## Resource Provider Use
|
||||||
|
|
||||||
|
Once a resource provider has verified the authenticity of the scope through
|
||||||
|
JWT access token verification, the resource provider must ensure that scope
|
||||||
|
satisfies the request. The resource provider should match the given audience
|
||||||
|
according to name or URI the resource provider uses to identify itself. Any
|
||||||
|
denial based on subject is not defined here and is up to resource provider, the
|
||||||
|
subject is mainly provided for audit logs and any other user-specific rules
|
||||||
|
which may need to be provided but are not defined by the authorization server.
|
||||||
|
|
||||||
|
The resource provider must ensure that ANY resource being accessed as the
|
||||||
|
result of a request has the appropriate access scope. Both the resource type
|
||||||
|
and resource name must match the accessed resource and an appropriate action
|
||||||
|
scope must be included.
|
||||||
|
|
||||||
|
When appropriate authorization is not provided either due to lack of scope
|
||||||
|
or missing token, the resource provider to return a `WWW-AUTHENTICATE` HTTP
|
||||||
|
header with the `realm` as the authorization server, the `service` as the
|
||||||
|
expected audience identifying string, and a `scope` field for each required
|
||||||
|
resource scope to complete the request.
|
||||||
|
|
||||||
|
## JWT Access Tokens
|
||||||
|
|
||||||
|
Each JWT access token may only have a single subject and audience but multiple
|
||||||
|
resource scopes. The subject and audience are put into standard JWT fields
|
||||||
|
`sub` and `aud`. The resource scope is put into the `access` field. The
|
||||||
|
structure of the access field can be seen in the
|
||||||
|
[jwt documentation](jwt.md).
|
||||||
|
|
||||||
|
## Refresh Tokens
|
||||||
|
|
||||||
|
A refresh token must be defined for a single subject and audience. Further
|
||||||
|
restricting scope to specific type, name, and actions combinations should be
|
||||||
|
done by fetching an access token using the refresh token. Since the refresh
|
||||||
|
token is not scoped to specific resources for an audience, extra care should
|
||||||
|
be taken to only use the refresh token to negotiate new access tokens directly
|
||||||
|
with the authorization server, and never with a resource provider.
|
||||||
|
|
|
@ -91,6 +91,8 @@ challenge, the client will need to make a `GET` request to the URL
|
||||||
|
|
||||||
## Requesting a Token
|
## Requesting a Token
|
||||||
|
|
||||||
|
Defines getting a bearer and refresh token using the token endpoint.
|
||||||
|
|
||||||
#### Query Parameters
|
#### Query Parameters
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
|
@ -100,6 +102,25 @@ challenge, the client will need to make a `GET` request to the URL
|
||||||
<dd>
|
<dd>
|
||||||
The name of the service which hosts the resource.
|
The name of the service which hosts the resource.
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>offline_token</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
Whether to return a refresh token along with the bearer token. A refresh
|
||||||
|
token is capable of getting additional bearer tokens for the same
|
||||||
|
subject with different scopes. The refresh token does not have an
|
||||||
|
expiration and should be considered completely opaque to the client.
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>client_id</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
String identifying the client. This client_id does not need
|
||||||
|
to be registered with the authorization server but should be set to a
|
||||||
|
meaningful value in order to allow auditing keys created by unregistered
|
||||||
|
clients. Accepted syntax is defined in
|
||||||
|
[RFC6749 Appendix A.1](https://tools.ietf.org/html/rfc6749#appendix-A.1).
|
||||||
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
<code>scope</code>
|
<code>scope</code>
|
||||||
</dt>
|
</dt>
|
||||||
|
@ -109,7 +130,9 @@ challenge, the client will need to make a `GET` request to the URL
|
||||||
shown above. This query parameter should be specified multiple times if
|
shown above. This query parameter should be specified multiple times if
|
||||||
there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
|
there is more than one <code>scope</code> entry from the <code>WWW-Authenticate</code>
|
||||||
header. The above example would be specified as:
|
header. The above example would be specified as:
|
||||||
<code>scope=repository:samalba/my-app:push</code>.
|
<code>scope=repository:samalba/my-app:push</code>. The scope field may
|
||||||
|
be empty to request a refresh token without providing any resource
|
||||||
|
permissions to the returned bearer token.
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
@ -150,6 +173,16 @@ challenge, the client will need to make a `GET` request to the URL
|
||||||
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
standard time at which a given token was issued. If <code>issued_at</code> is omitted, the
|
||||||
expiration is from when the token exchange completed.
|
expiration is from when the token exchange completed.
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<code>refresh_token</code>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
(Optional) Token which can be used to get additional access tokens for
|
||||||
|
the same subject with different scopes. This token should be kept secure
|
||||||
|
by the client and only sent to the authorization server which issues
|
||||||
|
bearer tokens. This field will only be set when `offline_token=true` is
|
||||||
|
provided in the request.
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
@ -161,11 +194,12 @@ https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba
|
||||||
```
|
```
|
||||||
|
|
||||||
The token server should first attempt to authenticate the client using any
|
The token server should first attempt to authenticate the client using any
|
||||||
authentication credentials provided with the request. As of Docker 1.8, the
|
authentication credentials provided with the request. From Docker 1.11 the
|
||||||
registry client in the Docker Engine only supports Basic Authentication to
|
Docker engine supports both Basic Authentication and [OAuth2](oauth.md) for
|
||||||
these token servers. If an attempt to authenticate to the token server fails,
|
getting tokens. Docker 1.10 and before, the registry client in the Docker Engine
|
||||||
the token server should return a `401 Unauthorized` response indicating that
|
only supports Basic Authentication. If an attempt to authenticate to the token
|
||||||
the provided credentials are invalid.
|
server fails, the token server should return a `401 Unauthorized` response
|
||||||
|
indicating that the provided credentials are invalid.
|
||||||
|
|
||||||
Whether the token server requires authentication is up to the policy of that
|
Whether the token server requires authentication is up to the policy of that
|
||||||
access control provider. Some requests may require authentication to determine
|
access control provider. Some requests may require authentication to determine
|
||||||
|
|
Loading…
Reference in a new issue