Please check the errata for any errors or issues reported since publication.
License: Per CC0, to the extent possible under law, the editor(s) and contributors have waived all copyright and related or neighboring rights to this work. In addition, the editor(s) and contributors have made this specification available under the Open Web Foundation Agreement Version 1.0.
IndieAuth is an identity layer on top of OAuth 2.0 [RFC6749], primarily used to obtain an OAuth 2.0 Bearer Token [RFC6750] for use by [Micropub] clients. End-Users and Clients are all represented by URLs. IndieAuth enables Clients to verify the identity of an End-User, as well as to obtain an access token that can be used to access resources under the control of the End-User.
This document was published by the IndieWeb community as a Living Standard.
Per CC0, to the extent possible under law, the editor(s) and contributors have waived all copyright and related or neighboring rights to this work. In addition, the editor(s) and contributors have made this specification available under the Open Web Foundation Agreement Version 1.0.
This section is non-normative.
The IndieAuth spec began as a way to obtain an OAuth 2.0 access token for use by Micropub clients. It can be used to both obtain an access token, as well as authenticate users signing to any application. It is built on top of the OAuth 2.0 framework, and while this document should provide enough guidance for implementers, referring to the core OAuth 2.0 spec can help answer any remaining questions. More information can be found on the IndieWeb wiki.
IndieAuth builds upon the OAuth 2.0 [RFC6749] Framework as follows
client_secret
is used)Additionally, the parameters defined by OAuth 2.0 (in particular state
, code
, and scope
) follow the same syntax requirements as defined by Appendix A of OAuth 2.0 [RFC6749].
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119].
An IndieAuth implementation can implement one or more of the roles of an IndieAuth server or client. This section describes the conformance criteria for each role.
Listed below are known types of IndieAuth implementations.
An IndieAuth Token Endpoint is responsible for generating and verifying OAuth 2.0 Bearer Tokens.
A Micropub client will attempt to obtain an OAuth 2.0 Bearer Token given an IndieAuth profile URL, and will use the token when making Micropub requests.
An IndieAuth client is a client that is attempting to authenticate a user given their profile URL, but does not need an OAuth 2.0 Bearer Token.
Users are identified by a [URL]. Profile URLs MUST have either an https
or http
scheme, MUST contain a path component (/
is a valid path), MUST NOT contain single-dot or double-dot path segments, MAY contain a query string component, MUST NOT contain a fragment component, MUST NOT contain a username or password component, and MUST NOT contain a port. Additionally, hostnames MUST be domain names and MUST NOT be ipv4 or ipv6 addresses.
Some examples of valid profile URLs are:
https://example.com/
https://example.com/username
https://example.com/users?id=100
Some examples of invalid profile URLs are:
example.com
mailto:user@example.com
https://example.com/foo/../bar
https://example.com/#me
https://user:pass@example.com/
https://example.com:8443/
https://172.28.92.51/
Clients are identified by a [URL]. Client identifier URLs MUST have either an https
or http
scheme, MUST contain a path component, MUST NOT contain single-dot or double-dot path segments, MAY contain a query string component, MUST NOT contain a fragment component, MUST NOT contain a username or password component, and MAY contain a port. Additionally, hostnames MUST be domain names or a loopback interface and MUST NOT be IPv4 or IPv6 addresses except for IPv4 127.0.0.1
or IPv6 [::1]
.
Since IndieAuth uses https/http URLs which fall under what [URL] calls "Special URLs", a string with no path component is not a valid [URL]. As such, if a URL with no path component is ever encountered, it MUST be treated as if it had the path /
. For example, if a user enters https://example.com
as their profile URL, the client MUST transform it to https://example.com/
when using it and comparing it.
Since domain names are case insensitive, the hostname component of the URL MUST be compared case insensitively. Implementations SHOULD convert the hostname to lowercase when storing and using URLs.
For ease of use, clients MAY allow users to enter just a hostname part of the URL, in which case the client MUST turn that into a valid URL before beginning the IndieAuth flow, by prepending a either an http
or https
scheme and appending the path /
. For example, if the user enters example.com
, the client transforms it into http://example.com/
before beginning discovery.
This specification uses the link rel registry as defined by [HTML] for both HTML and HTTP link relations.
Clients need to discover a few pieces of information when a user signs in. The client needs to discover the user's authorization_endpoint
, and optionally token_endpoint
if the client needs an access token. When using the Authorization flow to obtain an access token for use at a [Micropub] endpoint, the client will also discover the micropub
endpoint.
Clients MUST start by making a GET or HEAD request to [Fetch] the user's profile URL to discover the necessary values. Clients MUST follow HTTP redirects (up to a self-imposed limit). If an HTTP permament redirect (HTTP 301 or 308) is encountered, the client MUST use the resulting URL as the canonical profile URL. If an HTTP temporary redirect (HTTP 302 or 307) is encountered, the client MUST use the previous URL as the profile URL, but use the redirected-to page for discovery.
Clients MUST check for an HTTP Link
header [RFC8288] with the appropriate rel
value. If the content type of the document is HTML, then the client MUST check for an HTML <link>
element with the appropriate rel
value. If more than one of these is present, the first HTTP Link
header takes precedence, followed by the first <link>
element in document order.
The endpoints discovered MAY be relative URLs, in which case the client MUST resolve them relative to the profile URL according to [URL].
Clients MAY initially make an HTTP HEAD request [RFC7231] to follow redirects and check for the Link
header before making a GET request.
In this example, the user enters example.com
in the sign-in form, so the client initially transforms that to http://example.com/
to perform discovery. The URL http://example.com/
returns an HTTP 301 permanent redirect to https://example.com/
, so the client updates the initial profile URL to https://example.com/
, and looks at the contents of that page to find the authorization endpoint.
In this example, the user enters www.example.com
in the sign-in form, so the client initially transforms that to http://www.example.com/
to perform discovery. The URL http://www.example.com/
returns an HTTP 301 permanent redirect to https://example.com/
, so the client updates the initial profile URL to https://example.com/
, and looks at the contents of that page to find the authorization endpoint.
In this example, the user enters example.com
in the sign-in form, so the client initially transforms that to http://example.com/
to perform discovery. The URL http://example.com/
returns an HTTP 301 permanent redirect to https://example.com/
, and https://example.com/
returns an HTTP 302 temporary redirect to https://example.com/username
. The client stores the last 301 permanent redirect as the profile URL, https://example.com/
, and uses the contents of https://example.com/username
to find the authorization endpoint.
In this example, the user enters username.example
in the sign-in form, so the client initially transforms that to http://username.example/
to perform discovery. However, the user does not host any content there, and instead that page is a redirect to their profile elsewhere. The URL http://username.example/
returns an HTTP 301 permanent redirect to https://example.com/username
, so the client updates the initial profile URL to https://example.com/username
when setting the me
parameter in the initial request. At the end of the flow, the authorization endpoint will return a me
value of https://example.com/username
, which is not on the same domain as what the user entered, but the client can accept it because of the HTTP 301 redirect encountered during discovery.
In this example, the user enters username.example
in the sign-in form, so the client initially transforms that to http://username.example/
to perform discovery. However, the user does not host any content there, and instead that page is a temporary redirect to their profile elsewhere. The URL http://username.example/
returns an HTTP 302 temporary redirect to https://example.com/username
, so the client discovers the authorization endpoint at that URL. Since the redirect is temporary, the client still uses the user-entered http://username.example/
when setting the me
parameter in the initial request. At the end of the flow, the authorization endpoint will return a me
value of https://username.example/
, which is not on the same domain as the authorization endpoint, but is the same domain as the user entered. This allows users to still use a profile URL under their control while delegating the authentication or authorization flow to an external account.
When an authorization server presents its authorization interface, it will often want to display some additional information about the client beyond just the client_id
URL, in order to better inform the user about the request being made. Additionally, the authorization server needs to know the list of redirect URLs that the client is allowed to redirect to.
Since client identifiers are URLs, the authorization server SHOULD [Fetch] the URL to find more information about the client.
Clients SHOULD have a web page at their client_id
URL with basic information about the application, at least the application's name and icon. This page serves as a good landing page for human visitors, but can also serve as the place to include machine-readable information about the application. The HTML on the client_id
URL SHOULD be marked up with [h-app] Microformat to indicate the name and icon of the application. Authorization servers SHOULD support parsing the [h-app] Microformat from the client_id
, and if there is an [h-app] with a url
property matching the client_id
URL, then it should use the name and icon and display them on the authorization prompt.
<div class="h-app">
<img src="/logo.png" class="u-logo">
<a href="/" class="u-url p-name">Example App</a>
</div>
This can be parsed with a Microformats2 parser, which will result in the following JSON structure.
{
"type": [
"h-app"
],
"properties": {
"name": ["Example App"],
"logo": ["https://app.example.com/logo.png"],
"url": ["https://app.example.com/"]
}
}
If a client wishes to use a redirect URL that is on a different domain than their client_id
, or if the redirect URL uses a custom scheme (such as when the client is a native application), then the client will need to whitelist those redirect URLs so that authorization endpoints can be sure it is safe to redirect users there. The client SHOULD publish one or more <link>
tags or Link
HTTP headers with a rel
attribute of redirect_uri
at the client_id
URL.
Authorization endpoints verifying that a redirect_uri
is allowed for use by a client MUST look for an exact match of the given redirect_uri
in the request against the list of redirect_uri
s discovered after resolving any relative URLs.
GET / HTTP/1.1
Host: app.example.com
HTTP/1.1 200 Ok
Content-type: text/html; charset=utf-8
Link: <https://app.example.com/redirect>; rel="redirect_uri"
<!doctype html>
<html>
<head>
<link rel="redirect_uri" href="/redirect">
</head>
...
</html>
After obtaining the End-User's profile URL, the client fetches the URL and looks for the authorization_endpoint
and token_endpoint
rel values in the HTTP Link
headers and HTML <link>
tags as described in § 4.1 Discovery by Clients.
Link: <https://example.org/auth>; rel="authorization_endpoint" Link: <https://example.org/token>; rel="token_endpoint" <link rel="authorization_endpoint" href="https://example.org/auth"> <link rel="token_endpoint" href="https://example.org/token">
In OAuth 2.0, access tokens are opaque to clients, so clients do not need to know anything about the contents or structure of the token itself. Additionally, endpoints that clients make requests to, such as [Micropub] endpoints, may not even understand how to interpret tokens if they were issued by a standalone token endpoint. If the token endpoint is not tightly integrated with the resource server the client is interacting with, then the resource server needs a way to verify access tokens that it receives from clients. If the token endpoint and Micropub endpoint are tightly coupled, then they can of course use an internal mechanism to verify access tokens.
Token endpoints that intend to interoperate with other endpoints MUST use the mechanism described below to allow resource servers such as Micropub endpoints to verify access tokens.
If a resource server needs to verify that an access token is valid, it MUST make a GET request to the token endpoint containing an HTTP Authorization
header with the Bearer Token according to [RFC6750]. Note that the request to the endpoint will not contain any user-identifying information, so the resource server (e.g. Micropub endpoint) will need to know via out-of-band methods which token endpoint is in use.
GET https://example.org/token Authorization: Bearer xxxxxxxx Accept: application/json
The token endpoint verifies the access token using (how this verification is done is up to the implementation), and returns information about the token:
me
- The profile URL of the user corresponding to this tokenclient_id
- The client ID associated with this tokenscope
- A space-separated list of scopes associated with this tokenHTTP/1.1 200 OK Content-Type: application/json { "me": "https://user.example.net/", "client_id": https://app.example.com/", "scope": "create update delete" }
Specific implementations MAY include additional parameters as top-level JSON properties. Clients SHOULD ignore parameters they don't recognize.
If the token is not valid, the endpoint MUST return an appropriate HTTP 400, 401 or 403 response. The response body is not significant.
A client may wish to explicitly disable an access token that it has obtained, such as when the user signs out of the client. IndieAuth extends OAuth 2.0 Token Revocation [RFC7009] by defining the following:
action=revoke
.An example revocation request is below.
POST https://example.org/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded Accept: application/json action=revoke &token=xxxxxxxx
As described in [RFC7009], the revocation endpoint responds with HTTP 200 for both the case where the token was successfully revoked, or if the submitted token was invalid.
In addition to the security considerations in OAuth 2.0 Core [RFC6749] and OAuth 2.0 Threat Model and Security Considerations [RFC6819], the additional considerations apply.
Clients will initially prompt the user for their profile URL in order to discover the necessary endpoints to perform authentication or authorization. However, there may be slight differences between the URL that the user initially enters vs what the system considers the user's canonical profile URL.
For example, a user might enter user.example.net
in a login interface, and the client may assume a default scheme of http
, providing an initial profile URL of http://user.example.net
. Once the authentication or authorization flow is complete, the response in the me
parameter might be the canonical https://user.example.net/
. In some cases, user profile URLs have a full path component such as https://example.net/username
, but users may enter just example.net
in the login interface.
Clients MUST use the resulting me
value from the profile URL response or access token response rather than assume the initially-entered URL is correct, with the following condition:
This ensures that an authorization endpoint is not able to issue valid responses for arbitrary profile URLs.
Authorization servers SHOULD fetch the client_id
provided in the authentication or authorization request in order to provide users with additional information about the request, such as the application name and logo. If the server does not fetch the client information, then it SHOULD take additional measures to ensure the user is provided with as much information as possible about the request.
The authorization server SHOULD display the full client_id
on the authorization interface, in addition to displaying the fetched application information if any. Displaying the client_id
helps users know that they are authorizing the expected application.
Since all IndieAuth clients are public clients, and no client authentication is used, the only measure available to protect against some attacks described in [RFC6819] is strong verification of the client's redirect_uri
. If the redirect_uri
scheme, host or port differ from that of the client_id
, then the authorization server MUST either verify the redirect URL as described in Redirect URL, or display the redirect URL to the user so they can inspect it manually.
The link relation types below are documented to be registered by IANA per Section 6.2.1 of [RFC8288]:
This section is non-normative.
This section is non-normative.
You can find a list of articles about IndieAuth on the IndieWeb wiki.
This section is non-normative.
You can find a list of IndieAuth implementations on indieauth.net
The editor wishes to thank the IndieWeb community and other implementers for their support, encouragement and enthusiasm, including but not limited to: Amy Guy, Barnaby Walters, Benjamin Roberts, Bret Comnes, Christian Weiske, Dmitri Shuralyov, François Kooman, Jeena Paradies, Martijn van der Ven, Sebastiaan Andeweg, Sven Knebel, and Tantek Çelik.
This section is non-normative.
response_type=code
and make it required, to bring it in line with OAuth 2.0grant_type=authorization_code
when redeeming the authorization code at the authorization endpointme
parameter from the token endpoint request