AutoAuth (working title) is an extension to IndieAuth to allow clients to obtain tokens for other sites without the user being present to complete an interactive flow. Obtaining those tokens is controlled by the user's authorization endpoint, which allows the user to still be in control of tokens generated for the client.
[TOC]
- User: the end user.
- Authorization Endpoint: Authorization Endpoint on the user's URL.
- Resource: protected resource requiring a token connected to the user to access
- Token Endpoint: Unless otherwise specified, the Token Endpoint of the Resource. Verifies and grants requests for tokens.
- Client: Application the user is authorizing to request tokens in their name.
The possibility to access a resource through AutoAuth is discovered through the WWW-Authenticate
header field, as described in RFC6750. In addition to this, a Link header with a rel value of token_endpoint
has to be present.
If the resource contains information for non-authorized requests, it MUST return a non-error HTTP code (commonly 200
) to requests not containing a token. An example of this would be a feed of posts that also contains public posts accessible to anyone. The presence of the WWW-Authenticate
header indicates to the client that private posts with a limited audience might be available to it after authorization. If no access at all is granted without authorization, it SHOULD return code 401 Unauthorized
.
GET https://example.org/resource
HTTP/1.1 200 OK
WWW-Authenticate: Bearer realm="posts" scope="read"
Link: <https://example.org/token>; rel="token_endpoint"
…
(NOTE: both the realm and scope attributes in the example above are OPTIONAL)
If the client does not already possess an applicable token, it can use AutoAuth to have the user's authorization endpoint obtain one for it. HTTP defines the concept of "protection spaces" for which authorization credentials are valid:
A protection space is defined by the canonical root URI (the scheme and authority components of the effective request URI [...]) of the server being accessed, in combination with the realm value if present.
Following OAuth 2.0, all exchanges unless noted otherwise happen through POST requests with form-encoded payloads and JSON response types. Applications SHOULD use the appropriate Content-Type
and Accept
headers to indicate this.
The guardian of the user's identity in IndieAuth is the authorization endpoint. It is tasked here with obtaining tokens for the resource. The owner of the resource will need a confirmation that request is made at the behest of the user, and the authorization endpoint is able to provide this very similarly to how it confirms the identity in IndieAuth.
The authorization endpoint creates an unique authorization code. With this code, a POST request to the resource's token endpoint is made. This is similar to the authorization flow in in IndieAuth, except that the authorization and token endpoints here belong to different sites. This request contains the following parameters:
grant_type=authorization_code
code
- The authorization code generated by the authorization endpoint. See https://tools.ietf.org/html/rfc6749#section-4.1.2 for requirements on the authorization code.root_uri
- The root URI of the protection spacerealm
- the realm requested if anyscope
- the scope requested (note that the auth endpoint could decide here to not request a scope the client asked for)state
- a randomly chosen spoofing-protection parameter which will be included in the callback. If working with client requests as described below, the authorization endpoint MUST NOT reuse the state used by the client.callback_url
- URL owned by the authorization endpoint to send the token to after the authorization code has been verifiedme
- the users URLclient_id
- URL of the authorization endpoint
POST https://example.org/token
Content-Type: x-www-form-urlencoded
Accept: application/json
grant_type=authorization_code
&code=xxxxxxxxx
&root_uri=https://example.org
&realm=posts
&scope=read
&state=4234067
&callback_url=https://user.example/auth?x=callback
&me=https://user.example/
&client_id=https//user.example/auth
If the token endpoint agrees to proceed with the process, the token endpoint returns a HTTP 202 Accepted
response. Otherwise, it returns an OAuth 2.0 error.
The token endpoint can choose to continue the process synchronously, so the authorization code verification and even the callback can happen before this request returns.
Note that this request is not yet verified to actually be from the user's authorization endpoint, so aborting here should only happen in cases where no sensitive information is revealed. E.g. it is safe to return an error if the protection space is unknown or never granted access to, but user-specific decisions could reveal sensitive information and thus should not be made here.
The token endpoint needs to verify that the authorization code is valid and that it was issued for the matching parameters. For this, it discovers the authorization endpoint from the user's page (me
). Discovery works like in IndieAuth, except that the me
value can not change: Permanent redirects (301
, 308
) are NOT allowed. It then checks that the URL found for the authorization endpoint is equal to client_id
. If it is equal, it sends it a verification request containing the code
, me
, root_uri
, realm
(if one was requested), scope
and callback_url
parameters.
POST https://user.example/auth
Content-Type: x-www-form-urlencoded
Accept: application/json
code=xxxxxxxxx
&root_uri=https://example.org
&realm=posts
&scope=read
&callback_url=https://user.example/auth?x=callback
&me=https://user.example/
The authorization endpoint will validate that the code corresponds with the given parameters (including the value or absence(!) of realm
). If it does, a HTTP 200 response is returned, otherwise an OAuth 2.0 error response.
If the verification was successful, the token endpoint can now assume the request is genuine.
If it is willing to grant the token as requested, it generates a token and sends it to the Callback URL using a POST request. Its parameters are:
access_token
- the tokentoken_type: Bearer
state
- the state as submitted by the authorization endpoint in the token requestscope
- the scopes for which the token was granted (space-separated list). OPTIONAL if equal to the requested scopes.expires_in
- time in seconds in which the token expires. RECOMMENDED
If it is not willing to grant the token, it sends an OAuth 2.0 error message to the Callback URL instead, also adding the state
parameter.
The token can now be used to request the resource. It is included in an Authorization
header with the Bearer
HTTP authorization scheme.
The above describes how a token can be obtained, but does not prescribe how applications requiring tokens can communicate with the authorization endpoint to trigger this flow. This section adds these pieces to allow applications that are not tightly integrated with the authorization endpoint to obtain tokens through a standardized and safe mechanism. Since all tokens pass through the authorization endpoint, it can keep a record of them and the user can always use them to request revocation at the resource, without the client application cooperating.
The Client application can obtain a token authorizing it to make requests for tokens for external resources through an IndieAuth authorization flow. It requests scopes prefixed with request_external_token
, i.e. to obtain permission to request tokens with read
scope, it requires a scope of request_external_token:read
.
When the client wants to request a token for a resource, it sends a request to the Authorization Endpoint to obtain one. The client makes a POST request to the authorization endpoint with an authorization header containing its token and the following parameters:
response_type=external_token
target_url
- the URL of the resource for which to obtain the tokenstate
- a randomly chosen spoofing-protection parameter which will be included in the callbackscope
- A space-separated list of scopes the client is requesting, e.g. "read". The client must be authorized to request each of the scopes, by having a matchingrequest_external_token:
scope itself.callback_url
- the URL on which the client wants to receive the token
POST https://user.example/auth
Authorization: Bearer CLIENT_TOKEN
Content-Type: application/x-www-form-urlencoded
Accept: application/json
response_type=external_token
&target_url=https://example.org/resource
&state=1234567890
&scope=read
&callback_url=https://client.example/callbacks
If the authorization endpoint accepts the request by the client, it returns a HTTP 202 Accepted
response. Otherwise, it returns an OAuth 2.0 error.
The authorization endpoint now goes through the flow as described above and obtains a token. If it receives an error at any step, it POSTs it to the callback_url, with the state parameter added.
If the token was successfully obtained, the callback to the client contains the same information as the Access Token Callback, but with the state
parameter value submitted by the client in the External Token Request, and the realm
the token is for added.
The client can now use the token to make queries.
Alternatively, the client can poll for information. This is especially relevant if the client is a client-side application on the user's device that is not able to provide a publicly available callback URL.
The polling mechanism in this flow is closely based on the OAuth2 Device Flow.
(Identical to callback flow, except there is no callback URL and no state)
When the client wants to request a token for a resource, it sends a request to the Authorization Endpoint to obtain one. The client makes a POST request to the authorization endpoint with an authorization header containing its token and the following parameters:
response_type=external_token
target_url
- the URL of the resource for which to obtain the tokenscope
- A space-separated list of scopes the client is requesting, e.g. "read". The client must be authorized to request each of the scopes, by having a matchingrequest_token
scope itself.
POST https://user.example/auth
Authorization: Bearer CLIENT_TOKEN
Content-Type: application/x-www-form-urlencoded
Accept: application/json
response_type=external_token
&target_url=https://example.org/resource
&state=1234567890
&scope=read
If the authorization endpoint accepts the request by the client, it returns a HTTP 200 OK
response containing
-
request_id
- a randomly chosen unique ID by which the server can match later polling to the request. Note that this takes the security role ofstate
and has to be private and unguessable. -
interval
- OPTIONAL polling interval in seconds.HTTP/1.1 200 OK Content-Type: application/json { "request_id": "abcdefg", "interval": 5 }
Otherwise, it returns an OAuth 2.0 error.
The authorization endpoint now obtains the token as above. Once the process is complete, it provides the client with the details when it polls the next time.
The client regularly polls the authorization endpoint with requests including its token and the following parameters:
request_id
- the request id it got assigned in the response to the External Token Request
If the access token is available, the authorization endpoint returns the information to the client. It includes the data it received through the the Access Token Callback, excluding the state
and adding the realm
. If it is not available, the responses are the same as in the OAuth2 Device Flow: https://tools.ietf.org/html/draft-ietf-oauth-device-flow-14#section-3.5
Most notably, the new error codes are:
authorization_pending
The authorization request is still pending as the end user hasn't
yet completed the user interaction steps (Section 3.3). The
client SHOULD repeat the Access Token Request to the token
endpoint (a process known as polling). Before each new request
the client MUST wait at least the number of seconds specified by
the "interval" parameter of the Device Authorization Response (see
Section 3.2), or 5 seconds if none was provided, and respect any
increase in the polling interval required by the "slow_down"
error.
slow_down
A variant of "authorization_pending", the authorization request is
still pending and polling should continue, but the interval MUST
be increased by 5 seconds for this and all subsequent requests.
access_denied
The end user denied the authorization request.
To allow the user to restrict future access of the client, token endpoints MUST support token revocation. Authorization endpoints MUST keep records of tokens issued, and allow the user to revoke them. Systems that integrate the User's Token and Authorization endpoints SHOULD automatically revoke tokens that have been obtained with the Client's credentials if the Client's token is revoked (either by the user, or through Token Revocation)
Authorization endpoints are encouraged to allow further restrictions of the power of a granted token, e.g. to specific domains.
Fetching a file with authorization for the user clearly links the request to the user. Clients should not make such requests unexpectedly, even when they have been granted permission to do so in general. Example: a feed reader should not automatically complete the AutoAuth flow for all feeds that prompt for authorization, but initially ask the user about this.
Clients should treat all responses received from authenticated requests as private and unique to the individual user.