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.

Author's Note

This specification was contributed to the W3C from the IndieWeb community. More history and evolution of IndieAuth can be found on the IndieWeb wiki.

Introduction

Background

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.

OAuth 2.0 Extension

IndieAuth builds upon the OAuth 2.0 [[!RFC6749]] Framework as follows

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]].

Conformance

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]].

Conformance Classes

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.

Authorization Endpoint

An IndieAuth Authorization Endpoint is responsible for obtaining authentication or authorization consent from the end user and generating and verifying authorization codes.

Token Endpoint

An IndieAuth Token Endpoint is responsible for generating and verifying OAuth 2.0 Bearer Tokens.

Micropub Client

A Micropub client will attempt to obtain an OAuth 2.0 Bearer Token given a URL that allows the discovery of an Authorization Endpoint, and will use the token when making Micropub requests.

IndieAuth Client

An IndieAuth client is a client that is attempting to authenticate a user given a URL that allows the discovery of an Authorization Endpoint, but does not need an OAuth 2.0 Bearer Token.

Identifiers

Issuer Identifier

The issuer identifier of the authorization server, which is primarily used to prevent authorization server mix-up attacks. The issuer identifier is a URL that uses the "https" scheme and has no query or fragment components as defined in [[!RFC9207]], however, the identifier MUST also be a prefix of the indieauth-metadata URL. For example, for an indieauth-metadata endpoint https://example.com/.well-known/oauth-authorization-server, the issuer URL could be https://example.com/, or for a metadata URL of https://example.com/wp-json/indieauth/1.0/metadata, the issuer URL could be https://example.com/wp-json/indieauth/1.0

User Profile URL

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, host names MUST be domain names and MUST NOT be ipv4 or ipv6 addresses.

Some examples of valid profile URLs are:

Some examples of invalid profile URLs are:

Client Identifier

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, host names 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].

URL Canonicalization

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 provides https://example.com for Discovery, the client MUST transform it to https://example.com/ when using it and comparing it.

Since domain names are case insensitive, the host component of the URL MUST be compared case insensitively. Implementations SHOULD convert the host to lowercase when storing and using URLs.

For ease of use, clients MAY allow users to enter just the host part of the URL, in which case the client MUST turn that into a valid URL before beginning the IndieAuth flow, by prepending 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.

Discovery

This specification uses the link rel registry as defined by [[!HTML]] for both HTML and HTTP link relations.

Discovery by Clients

Clients need to discover a few pieces of information when a user signs in. The client needs to discover the user's indieauth-metadata endpoint, which provides the location of the IndieAuth server's authorization endpoint and token endpoint, as well as other relevant information for the client. Clients MUST start by making a GET or HEAD request to [[!Fetch]] the user provided URL to discover the necessary values. Clients MUST follow HTTP redirects (up to a self-imposed limit). When using the Authorization flow to obtain an access token for use at another endpoint, such as a [[?Micropub]] endpoint, the client will also discover the micropub endpoint.

Clients MUST check for an HTTP Link header [[!RFC8288]] with a rel value of indieauth-metadata. If the content type of the document is HTML, then the client MUST check for an HTML <link> element with a rel value of indieauth-metadata. 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 URLs discovered MAY be relative URLs, in which case the client MUST resolve them relative to the current document 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 the event there is no indieauth-metadata URL provided, for compatibility with previous revisions of IndieAuth, the client SHOULD look for an HTTP Link header and HTML <link> element with a rel value of authorization_endpoint (and optionally token_endpoint) following the same order of predence as described above.

Note that the recommendation of looking for the rel=authorization_endpoint and rel=token_endpoint are included for backwards compatibility with previous IndieAuth profiles and may be removed from this specification in the future after wide enough adoption of the newer indieauth-metadata discovery method.

IndieAuth Server Metadata

IndieAuth metadata adopts OAuth 2.0 Authorization Server Metadata [[RFC8414]], with the notable difference that discovery of the URL happens via the IndieAuth link relation rather than the .well-known discovery method specified by RFC8414. For compatibility with other OAuth 2.0 implementations, use of the .well-known path as defined in RFC8414 is RECOMMENDED but optional.

The metadata endpoint returns information about the server as a JSON object with the following properties:

  • issuer - The server's issuer identifier.
  • authorization_endpoint - The Authorization Endpoint
  • token_endpoint - The Token Endpoint
  • introspection_endpoint - The Introspection Endpoint
  • introspection_endpoint_auth_methods_supported (optional) - JSON array containing a list of client authentication methods supported by this introspection endpoint.
  • revocation_endpoint (optional) - The Revocation Endpoint
  • revocation_endpoint_auth_methods_supported (optional) - JSON array containing the value "none". If a revocation endpoint is provided, this property should also be provided with the value ["none"], since the omission of this value defaults to client_secret_basic according to [[RFC8414]].
  • scopes_supported (recommended) - JSON array containing scope values supported by the IndieAuth server. Servers MAY choose not to advertise some supported scope values even when this parameter is used.
  • response_types_supported (optional) - JSON array containing the response_type values supported. This differs from [RFC8414] in that this parameter is OPTIONAL and that, if omitted, the default is code
  • grant_types_supported (optional) - JSON array containing grant type values supported. If omitted, the default value differs from [RFC8414] and is authorization_code
  • service_documentation (optional) - URL of a page containing human-readable information that developers might need to know when using the server. This might be a link to the IndieAuth spec or something more personal to your implementation.
  • code_challenge_methods_supported - JSON array containing the methods supported for PKCE. This parameter differs from [RFC8414] in that it is not optional as PKCE is REQUIRED.
  • authorization_response_iss_parameter_supported (optional) - Boolean parameter indicating whether the authorization server provides the iss parameter. If omitted, the default value is false. As the iss parameter is REQUIRED, this is provided for compatibility with OAuth 2.0 servers implementing the parameter.
  • userinfo_endpoint (optional) - The User Info Endpoint
HTTP/1.1 200 OK
Content-Type: application/json

{
  "issuer": "https://indieauth.example.com/",
  "authorization_endpoint": "https://indieauth.example.com/auth",
  "token_endpoint": "https://indieauth.example.com/token",
  "code_challenge_methods_supported": ["S256"]
}

Client Information Discovery

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.

Clients SHOULD publish an OAuth Client ID Metadata Document [[!ClientIDMetadataDocument]] at the client identifier URL.

The authorization server SHOULD [[!Fetch]] the URL to find more information about the client.

If the client_id contains the permitted IPv4 and IPv6 addresses 127.0.0.1 or [::1], or if the domain name resolves to these addresses, the authorization endpoint MUST NOT fetch the client_id.

Note that the server may want to perform some additional checks on the client_id before fetching it to avoid SSRF attacks. In particular, the server may want to resolve the domain name first and avoid fetching the document if the IP address is within the loopback range defined by [[RFC5735]] or any other implementation-specific internal IP address.

Client Metadata

This section is based on [[!ClientIDMetadataDocument]], and is included here for convenience, but the normative requirements in [[!ClientIDMetadataDocument]] take precedence.

Clients SHOULD have a JSON [[!RFC7159]] document at their client_id URL containing client metadata defined in [[!RFC7591]], the minimum properties for an IndieAuth client defined below. The authorization server MAY cache the client metadata it discovers at the client ID URL and SHOULD respect cache-control headers and set reasonable defaults if none are present.

  • client_id - the client identifier. The authorization server MUST verify that the client_id in the document matches the client_id of the URL where the document was retrieved. The client_uri MUST be a prefix of the client_id.
  • client_name - (optional) Human readable name of the client to be presented on the consent screen
  • client_uri - URL of a webpage providing information about the client
  • logo_uri - (optional) URL that references a logo or icon for the client
  • redirect_uris - (optional) An array of redirect URIs

Additional metadata properties defined in [[!ClientMetadata]] MAY be added, with the understanding that some authorization servers may not recognize them.

Clients SHOULD have a web page at their client_uri URL with basic information about the application, at least the application's name and icon. Authorization servers SHOULD display this URL to the user during authorization, so that the user can learn more about the application. Authorization servers SHOULD warn the user if the hostname of the client_uri is different from the hostname of the client_id.

Note: Earlier versions of this specification recommended an HTML document with h-app Microformats as the client_id, and authorization servers may wish to continue to support this for backwards compatibility.

Redirect URL

If a client wishes to use a redirect URL that has a different host 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 explicitly list those redirect URLs in the redirect_uri property of the client metadata so that authorization endpoints can be sure it is safe to redirect users there. 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_uris after resolving any relative URLs.

When a client chooses to serve a web page as its client_id, the client MAY publish one or more <link> tags or Link HTTP headers with a rel attribute of redirect_uri at the client_id URL to be used by the authorization server.

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>

Authorization

This section describes how to authenticate users and optionally obtain an access token using the OAuth 2.0 Authorization Code Flow with IndieAuth.

IndieAuth Flow DiagramAuthorization ServerBrowserClientProfile URLMetadata URLAuthorization EndpointToken Endpoint User enters a URL, and the User enters a URL, and theclient canonicalizes the URLclient canonicalizes the URL Client fetches URL to discover Client fetches URL to discoverrel=indieauth-metadatarel=indieauth-metadata Client fetches metadata URL to find Client fetches metadata URL to findauthorization_endpointauthorization_endpointand and token_endpointtoken_endpoint Client builds authorization request and Client builds authorization request andredirects to redirects to authorization_endpointauthorization_endpoint User visits their authorization endpoint and sees the authorization request User visits their authorization endpoint and sees the authorization request Authorization endpoint fetches client information (name, icon) Authorization endpoint fetches client information (name, icon) User authenticates, and approves the request. Authorization User authenticates, and approves the request. Authorizationendpoint issues authorization code, builds redirect back to client.endpoint issues authorization code, builds redirect back to client. User's browser is redirected to User's browser is redirected toclient with an client with an authorization codeauthorization code Client exchanges authorization code for an  Client exchanges authorization code for an access token by making a POST requestaccess token by making a POST requestto the token_endpointto the token_endpoint Token endpoint verifies code and returns Token endpoint verifies code and returnscanonical user profile URL with an access tokencanonical user profile URL with an access token Client fetches the user URL Client fetches the user URLif it differs from the original,if it differs from the original,and confirms the it declaresand confirms the it declaresthe same authorization serverthe same authorization server Client initiates login session Client initiates login sessionand the user is logged inand the user is logged in

Note: If the client is only trying to learn who the user is and does not need an access token, the client exchanges the authorization code for the user profile information at the Authorization Endpoint instead.

Discovery

After obtaining a URL from the End-User, and optionally applying URL Canonicalization to it, the client fetches the URL and looks for the indieauth-metadata rel values in the HTTP Link headers and HTML <link> tags as described in .

Link: <https://indieauth.example.com/.well-known/oauth-authorization-server>; rel="indieauth-metadata"

<link rel="indieauth-metadata" href="https://indieauth.example.com/.well-known/oauth-authorization-server">

The client fetches the metadata document and finds the authorization_endpoint and token_endpoint in the JSON body.

{
  "issuer": "https://indieauth.example.com/",
  "authorization_endpoint": "https://indieauth.example.com/auth",
  "token_endpoint": "https://indieauth.example.com/token",
  "code_challenge_methods_supported": ["S256"]
}

Authorization Request

The client builds the authorization request URL by starting with the discovered authorization_endpoint URL and adding parameters to the query component.

All IndieAuth clients MUST use PKCE ([[!RFC7636]]) to protect against authorization code injection and CSRF attacks. A non-canonical description of the PKCE mechanism is described below, but implementers should refer to [[RFC7636]] for details.

Clients use a unique secret per authorization request to protect against authorization code injection and CSRF attacks. The client first generates this secret, which it can later use along with the authorization code to prove that the application using the authorization code is the same application that requested it.

The client first creates a code verifier for each authorization request by generating a random string using the characters [A-Z] / [a-z] / [0-9] / - / . / _ / ~ with a minimum length of 43 characters and maximum length of 128 characters. This value is stored on the client and will be used in the authorization code exchange step later.

The client then creates the code challenge derived from the code verifier by calculating the SHA256 hash of the code verifier and Base64-URL-encoding the result.

code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

For backwards compatibility, authorization endpoints MAY accept authorization requests without a code challenge if the authorization server wishes to support older clients.

https://example.org/auth?response_type=code&
                         client_id=https://app.example.com/&
                         redirect_uri=https://app.example.com/redirect&
                         state=1234567890&
                         code_challenge=OfYAxt8zU2dAPDWQxTAUIteRzMsoj9QBdMIVEDOErUo&
                         code_challenge_method=S256&
                         scope=profile+create+update+delete&
                         me=https://user.example.net/

The client SHOULD provide the me query string parameter to the authorization endpoint, either the exact value the user entered, or the value after applying URL Canonicalization.

The authorization endpoint SHOULD fetch the client_id URL to retrieve application information and the client's registered redirect URLs, see Client Information Discovery for more information.

If the URL scheme, host or port of the redirect_uri in the request do not match that of the client_id, then the authorization endpoint SHOULD verify that the requested redirect_uri matches one of the redirect URLs published by the client, and SHOULD block the request from proceeding if not.

It is up to the authorization endpoint how to authenticate the user. This step is out of scope of OAuth 2.0, and is highly dependent on the particular implementation. Some authorization servers use typical username/password authentication, and others use alternative forms of authentication such as [[?RelMeAuth]], or delegate to other identity providers.

The authorization endpoint MAY use the provided me query component as a hint of which user is attempting to sign in, and to indicate which profile URL the client is expecting in the resulting profile URL response or access token response. This is specifically helpful for authorization endpoints where users have multiple supported profile URLs, so the authorization endpoint can make an informed decision as to which profile URL the user meant to identify as. Note that from the authorization endpoint's view, this value as provided by the client is unverified external data and MUST NOT be assumed to be valid data at this stage. If the logged-in user doesn't match the provided me parameter by the client, the authorization endpoint MAY either ignore the me parameter completely or display an error, at the authorization endpoint's discretion.

Once the user is authenticated, the authorization endpoint presents the authorization request to the user. The prompt MUST indicate which application the user is signing in to, and SHOULD provide as much detail as possible about the request, such as information about the requested scopes.

Authorization Response

If the user approves the request, the authorization endpoint generates an authorization code and builds the redirect back to the client.

The redirect is built by starting with the redirect_uri in the request, and adding the following parameters to the query component of the redirect URL:

  • code - The authorization code generated by the authorization endpoint. The code MUST expire shortly after it is issued to mitigate the risk of leaks, and MUST be valid for only one use. A maximum lifetime of 10 minutes is recommended. See OAuth 2.0 Section 4.1.2 for additional requirements on the authorization code.
  • state - The state parameter MUST be set to the exact value that the client set in the request.
  • iss - The issuer identifier for client validation.
HTTP/1.1 302 Found
Location: https://app.example.com/redirect?code=xxxxxxxx&
                                           state=1234567890&
                                           iss=https%3A%2F%2Findieauth.example.com

Upon the redirect back to the client, the client MUST verify:

  • That the state parameter in the request is valid and matches the state parameter that it initially created, in order to prevent CSRF attacks. The state value can also store session information to enable development of clients that cannot store data themselves.
  • That the iss parameter in the request is valid and matches the issuer parameter provided by the Server Metadata endpoint during Discovery as outlined in OAuth 2.0 Authorization Server Issuer Identification[[!RFC9207]]. Clients MUST compare the parameters using simple string comparison. If the value does not match the expected issuer identifier, clients MUST reject the authorization response and MUST NOT proceed with the authorization grant. For error responses, clients MUST NOT assume that the error originates from the intended authorization server.

See OAuth 2.0 [[!RFC6749]] Section 4.1.2.1 for how to indicate errors and other failures to the user and client.

Redeeming the Authorization Code

Once the client has obtained an authorization code, it can redeem it for an access token or the user's final profile URL.

Request

If the client needs an access token in order to make requests to a resource server such as a [[?Micropub]] endpoint, it can exchange the authorization code for an access token and the user's profile URL at the token endpoint.

If the client only needs to know the user who logged in and does not need to make requests to resource servers with an access token, the client exchanges the authorization code for the user's profile URL at the authorization endpoint.

After the client validates the state parameter, the client makes a POST request to the token endpoint or authorization endpoint to exchange the authorization code for the final user profile URL and/or access token. The POST request contains the following parameters:

  • grant_type=authorization_code
  • code - The authorization code received from the authorization endpoint in the redirect.
  • client_id - The client's URL, which MUST match the client_id used in the authentication request.
  • redirect_uri - The client's redirect URL, which MUST match the initial authentication request.
  • code_verifier - The original plaintext random string generated before starting the authorization request.
Example request to authorization endpoint
POST https://indieauth.example.com/auth
Content-type: application/x-www-form-urlencoded
Accept: application/json

grant_type=authorization_code
&code=xxxxxxxx
&client_id=https://app.example.com/
&redirect_uri=https://app.example.com/redirect
&code_verifier=a6128783714cfda1d388e2e98b6ae8221ac31aca31959e59512c59f5
Example request to token endpoint
POST https://indieauth.example.com/token
Content-type: application/x-www-form-urlencoded
Accept: application/json

grant_type=authorization_code
&code=xxxxxxxx
&client_id=https://app.example.com/
&redirect_uri=https://app.example.com/redirect
&code_verifier=a6128783714cfda1d388e2e98b6ae8221ac31aca31959e59512c59f5

Note that for backwards compatibility, the authorization endpoint MAY allow requests without the code_verifier. If an authorization code was issued with no code_challenge present, then the authorization code exchange MUST NOT include a code_verifier, and similarly, if an authorization code was issued with a code_challenge present, then the authorization code exchange MUST include a code_verifier.

Profile URL Response

If the client only needs to know the user who logged in, the client will exchange the authorization code at the authorization endpoint, and only the canonical user profile URL and possibly profile information is returned.

The authorization endpoint verifies that the authorization code is valid, has not yet been used, and that it was issued for the matching client_id and redirect_uri, and checks that the provided code_verifier hashes to the same value as given in the code_challenge in the original authorization request. If the request is valid, then the endpoint responds with a JSON [[!RFC7159]] object containing the property me, with the canonical user profile URL for the user who signed in, and optionally the property profile with the user's profile information as defined in Profile Information.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "me": "https://user.example.net/"
}

The resulting profile URL MAY be different from the URL provided to the client for discovery. This gives the authorization server an opportunity to canonicalize the user's URL, such as correcting http to https, or adding a path if required. See Differing User Profile URLs for security considerations client developers should be aware of.

See OAuth 2.0 [[!RFC6749]] Section 5.2 for how to respond in the case of errors or other failures.

Access Token Response

When the client receives an authorization code that was requested with one or more scopes that will result in an access token being returned, the client will exchange the authorization code at the token endpoint.

The token endpoint needs to verify that the authorization code is valid, and that it was issued for the matching client_id and redirect_uri, contains at least one scope, and checks that the provided code_verifier hashes to the same value as given in the code_challenge in the original authorization request. If the authorization code was issued with no scope, the token endpoint MUST NOT issue an access token, as empty scopes are invalid per Section 3.3 of OAuth 2.0 [[!RFC6749]].

The specifics of how the token endpoint verifies the authorization code are out of scope of this document, as typically the authorization endpoint and token endpoint are part of the same system and can share storage or another private communication mechanism.

If the request is valid, then the token endpoint can generate an access token and return the appropriate response. The token response is a JSON [[!RFC7159]] object containing:

  • access_token (required) - the OAuth 2.0 Bearer Token [[!RFC6750]].
  • me (required) - the canonical user profile URL for the user this access token corresponds to.
  • profile (optional) - the user's profile information as defined in Profile Information.
  • expires_in (recommended) - The lifetime in seconds of the access token.
  • refresh_token (optional) - The refresh token, which can be used to obtain new access tokens as defined in Refresh Tokens.

For example:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "XXXXXX",
  "token_type": "Bearer",
  "scope": "create update delete",
  "me": "https://user.example.net/"
}

The resulting profile URL MAY be different from the URL provided to the client for discovery. This gives the authorization server an opportunity to canonicalize the user's URL, such as correcting http to https, or adding a path if required. See Differing User Profile URLs for security considerations client developers should be aware of.

See OAuth 2.0 [[!RFC6749]] Section 5.2 for how to respond in the case of errors or other failures.

Profile Information

Requesting Profile Information

If the client would like to request the user's profile information in addition to confirming their profile URL, the client can include one or more scopes in the initial authorization request. The following scope values are defined by this specification to request profile information about the user:

  • profile (required) - This scope requests access to the user's default profile information which include the following properties: name, photo, url.
  • email - This scope requests access to the user's email address in the following property: email.

Note that because the profile scope is required when requesting profile information, the email scope cannot be requested on its own and must be requested along with the profile scope if desired.

When an authorization code is issued with any of the scopes defined above, then the response when exchanging the authorization code MAY include a new property, profile, alongside the me property in the response from the authorization endpoint or the token endpoint. The profile property is defined as a JSON [[!RFC7159]] object with the properties defined by each scope above.

For example, a complete response to a request with the scopes profile email create, including an access token and profile information, may look like the following:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "XXXXXX",
  "token_type": "Bearer",
  "scope": "profile email create",
  "me": "https://user.example.net/",
  "profile": {
    "name": "Example User",
    "url": "https://user.example.net/",
    "photo": "https://user.example.net/photo.jpg",
    "email": "user@example.net"
  }
}

As is always the case with OAuth 2.0, there is no guarantee that the scopes the client requests will be granted by the authorization server or the user. The client should not rely on the presence of profile information even when requesting the profile scope. As such, implementing support for returning profile information from the authorization server is entirely optional.

The information returned in the profile object is informational, and there is no guarantee that this information is "real" or "verified". The information provided is only what the user has chosen to share with the client, and may even vary depending on which client is requesting this data.

  • name - Name the user wishes to provide to the client. This is not to be considered by the client to be the full name of the user. Clients are expected to use this as a display name.
  • url - URL of the user's website. The url is not guaranteed to match the me URL, and may even have a different host. For example, a multi-author website may use the website's URL as the me URL, but return each specific author's own personal website in the profile data.
  • photo - A photo or image that the user wishes clients to use as a profile image.
  • email - (if email scope is requested) The email address a user wishes to provide to the client.

The client MUST NOT treat the information in the profile object as canonical or authoritative, and MUST NOT make any authentication or identification decisions based on this information.

For example, attempting to use the email returned in the profile object as a user identifier will lead to security holes, as any user can create an authorization endpoint that returns any email address in the profile response. A client using the email address returned here should treat it the same as if it had been hand-entered in the client application and go through its own verification process before using it.

Authorization Server Confirmation

Clients will initially prompt the user to enter a URL in order to discover the necessary endpoints to perform authentication or authorization. However, there may be differences between the URL that the user initially enters and the final resulting profile URL as returned by the authorization server. The differences may be anything from a differing scheme (http vs https), to even a URL with a different host.

Upon receiving the me URL in the response from the authorization server (either in the profile URL response or access token response) the client MUST verify the authorization server is authorized to make claims about the profile URL returned by confirming the returned profile URL declares the same authorization server.

The client MUST perform endpoint discovery on the returned me URL and verify that URL declares the same authorization endpoint as was discovered in the initial discovery step, unless the returned me URL is an exact match of the initially entered URL or any of the URLs encountered during the initial endpoint discovery, either from a possible redirect chain or as the final value.

Note that the step of checking for the existence of the returned profile URL in the initial endpoint discovery is an optional optimization step which may save the client from possibly needing to make another HTTP request. This step may be skipped for simplicity, as discovering the authorization server from the returned profile URL is sufficient to confirm the returned profile URL declares the same authorization server.

This verification step ensures that an authorization endpoint is not able to issue valid responses for arbitrary profile URLs, and that users on a shared domain cannot forge authorization on behalf of other users of that domain.

Examples

The following are some non-normative examples of real-world scenarios in which the initial user-entered URL may be different from the final resulting profile URL returned by the authorization server.

Basic Redirect

The basic redirect example covers cases such as:

Steps

  1. The user enters www.example.com into the client
  2. The client applies the steps from URL canonicalization to turn it into a URL: http://www.example.com/
  3. The client makes a GET request to http://www.example.com/
  4. The server returns a 301 redirect to https://example.com/
  5. The client makes a GET request to https://example.com/ and finds the authorization endpoint
  6. The client does the IndieAuth flow with that authorization endpoint. This results in the profile URL response with a me value of https://example.com/ as the canonical Profile URL.
  7. The client sees that the canonical Profile URL matches the URL that the authorization endpoint was discovered at, and accepts the value https://example.com/
Service Domain to Subdomain
  1. The user enters example.com into the client
  2. The client applies the steps from URL canonicalization to turn it into a URL: http://example.com/
  3. The client makes a GET request to http://example.com/
  4. The server returns a 301 redirect to https://example.com/
  5. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com
  6. The client does the IndieAuth flow with https://login.example.com. This results in the profile URL response with a me value of https://username.example.com/ as the canonical Profile URL.
  7. This is the first time the client has seen this URL, so must verify the relationship between this subdomain and the authorization server. It fetches https://username.example.com/ and finds the same authorization endpoint https://login.example.com
  8. The client accepts the me value of https://username.example.com/
Service Domain to Path
  1. The user enters example.com into the client
  2. The client applies the steps from URL canonicalization to turn it into a URL: http://example.com/
  3. The client makes a GET request to http://example.com/
  4. The server returns a 301 redirect to https://example.com/
  5. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com
  6. The client does the IndieAuth flow with https://login.example.com. This results in the profile URL response with a me value of https://example.com/username as the canonical Profile URL.
  7. This is the first time the client has seen this URL, so must verify the relationship between this subdomain and the authorization server. It fetches https://example.com/username and finds the same authorization endpoint https://login.example.com
  8. The client accepts the me value of https://example.com/username
Email-like Identifier
  1. The user enters user@example.com into the client
  2. The client applies the steps from URL canonicalization to turn it into a URL: http://user@example.com/
  3. The client makes a GET request to http://example.com/ providing the HTTP Basic Auth username user
  4. The server returns a 301 redirect to https://example.com/
  5. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com
    • Note: Alternatively the server can advertise the authorization endpoint in the response to the http://user@example.com/ request directly instead of needing a separate redirect
  6. The client does the IndieAuth flow with https://login.example.com, providing the user-entered user@example.com in the request as a hint to the server. This results in the profile URL response with a me value of https://example.com/username as the canonical Profile URL.
  7. This is the first time the client has seen this URL, so must verify the relationship between this subdomain and the authorization server. It fetches https://example.com/username and finds the same authorization endpoint https://login.example.com
  8. The client accepts the me value of https://example.com/username

Refresh Tokens

Refresh tokens are issued to the client by the authorization server and MAY be used at any time to obtain a new access token, usually when the current access token becomes invalid or expires, or to obtain a new token with identical or narrower scope (access tokens may have a shorter lifetime and fewer permissions than authorized by the resource owner).

Use of short-lived access tokens and the offering of refresh tokens is RECOMMENDED, however, issuing a refresh token is at the discretion of the authorization server, and may be issued based on properties of the client, properties of the request, policies within the authorization server, a choice by the user authorizing the request or any other criteria. If the authorization server issues a refresh token, it is included in the return when issuing an access token. If the authorization server decides not to issue refresh tokens, or the refresh token expires, the client MAY obtain new access tokens by starting the authorization flow over.

Authorization servers MAY revoke refresh tokens automatically in case of a security event, such as a password change or a logout at the authorization server, or when they are redeemed, in which case a new refresh token MAY be provided. Refresh tokens SHOULD expire if the client has been inactive for some time, i.e., the refresh token has not been used to obtain fresh access tokens for some time. The expiration time is at the discretion of the authorization server.

Refreshing an Access Token

To refresh an access token, the client makes a POST request to the token endpoint to exchange the refresh token for the new access token. The POST request contains the following parameters:

  • grant_type=refresh_token
  • refresh_token - The refresh token previously offered to the client.
  • client_id - The client ID that was used when the refresh token was issued.
  • scope (optional) - The client may request a token with the same or fewer scopes than the original access token. If omitted, is treated as equal to the original scopes granted.

For example:

POST https://example.org/token
Content-type: application/x-www-form-urlencoded
Accept: application/json

grant_type=refresh_token
&refresh_token=xxxxxxxx&client_id=https://app.example.com

If valid and authorized, the authorization server issues an access token as noted in Access Token Response. The authorization server MAY issue a new refresh token, in which case the client MUST discard the old refresh token and replace it with the new refresh token. The authorization server MAY revoke the old refresh token after issuing a new refresh token to the client. If a new refresh token is issued, the refresh token scope MUST be identical to that of the refresh token included by the client in the request.

Refresh tokens SHOULD expire if the client has not used the refresh token to obtain new access tokens for some time. The expiration time is at the discretion of the authorization server.

Access Token Verification

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.

Access Token Verification Request

If a resource server needs to verify that an access token is valid, it may do so using Token Introspection. IndieAuth extends OAuth 2.0 Token Introspection [[!RFC7662]] by adding that the introspection response MUST include an additional parameter, me.

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.

The resource server SHOULD make a POST request to the token endpoint containing the Bearer token in the token parameter, which will generate a token verification response. The endpoint MUST also require some form of authorization to access this endpoint and MAY identify that in the introspection_endpoint_auth_methods_supported parameter of the metadata response. If the authorization is insufficient for the request, the authorization server MUST respond with an HTTP 401 code.

POST https://indieauth.example.com/introspect
Content-type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Bearer xxxxxxxx

token=xxxxxxxx

Access Token Verification Response

The token endpoint verifies the access token (how this verification is done is up to the implementation), and returns information about the token:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "active": "true",
  "me": "https://user.example.net/",
  "client_id": https://app.example.com/",
  "scope": "create update delete"
  "exp": "1632443647",
  "iat": "1632443147"
}

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 still MUST return a 200 Response, with the only parameter being active(with its value set to "false"). The response SHOULD NOT include any additional information about an inactive token, including why the token is inactive.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "active": "false",
}

Token Revocation

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 implements OAuth 2.0 Token Revocation [[!RFC7009]] using the revocation endpoint defined in the server metadata:

Token Revocation Request

An example revocation request is below.

POST https://indieauth.example.com/revocation HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Accept: application/json

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.

A previous version of the spec used the token endpoint as the revocation endpoint with the additional parameter action=revoke. Servers that wish to support older versions of clients may wish to retain this behavior for backwards compatibility.

Accessing Protected Resources

The client accesses protected resources by presenting the access token to the resource server. The resource server MUST validate the access token and ensure that it has not expired and that its scope covers the requested resource.

Error Responses

When a request fails, the resource server responds using the appropriate HTTP status codes, and includes one of the following error codes in the response:

If the requests lacks any authentication information, the resource server SHOULD NOT include an error code or other information.

User Information

A client may wish to refresh or receive additional information about the authenticated end user outside of the authorization response. To fetch the user's profile information, the client makes a GET request to the userinfo endpoint, providing an access token that was issued with the profile and/or email scopes.

GET /userinfo HTTP/1.1
Host: indieauth.example.com
Authorization: Bearer xxxxxxxxxxx

The authorization server returns a JSON [[!RFC7159]] object identical to the profile property identified in Profile Information when a response_type value is used that results in an access token being issued and would require the same profile or email scopes. The considerations identified in Profile Information regarding the non-authoritative nature of the information would also apply here.

If the request lacks a provided access token, or the token does not contain appropriate scopes, the endpoint SHOULD respond with an error response as noted in Accessing Protected Resources.

Like the return of profile information in the authorization response, implementation of the userinfo endpoint is entirely optional. If implemented, discovery would be through the userinfo_endpoint property in the authorization server metadata document.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "name": "Example User",
    "url": "https://user.example.net/",
    "photo": "https://user.example.net/photo.jpg",
    "email": "user@example.net"
}

Security Considerations

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.

Preventing Phishing and Redirect Attacks

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 IndieAuth clients are likely to be public clients (if no jwks_uri is advertised in the client metatadata), 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.

IANA Considerations

The link relation types below are documented to be registered by IANA per Section 6.2.1 of [[!RFC8288]]:

Relation Name:
indieauth-metadata
Description:
Used for discovery of the OAuth 2.0 metadata document given an IndieAuth profile URL.
Reference:
IndieAuth Specification (https://indieauth.spec.indieweb.org/)
Relation Name:
redirect_uri
Description:
Used for an authorization server to discover the OAuth 2.0 redirect URI for a client given the client's IndieAuth client ID.
Reference:
IndieAuth Specification (https://indieauth.spec.indieweb.org/)

Resources

Articles

You can find a list of articles about IndieAuth on the IndieWeb wiki.

Implementations

You can find a list of IndieAuth implementations on indieauth.net

Acknowledgements

The editor wishes to thank the IndieWeb community and other implementers for their contributions, support, encouragement and enthusiasm, including but not limited to: Angelo Gladding, Amy Guy, Barnaby Walters, Benjamin Roberts, Bret Comnes, Christian Weiske, David Shanske, David Somers, Dmitri Shuralyov, Emelia Smith, Fluffy, François Kooman, Jamie Tanna, Jeena Paradies, Manton Reece, Martijn van der Ven, Sebastiaan Andeweg, Sven Knebel, and Tantek Çelik.

Change Log

Changes from 12 February 2022 to this version

Changes from 26 November 2020 to 12 February 2022

Changes from 26 September 2020 to 26 November 2020

Changes from 09 August 2020 to 26 September 2020

Changes from 25 January 2020 to 09 August 2020

Changes from 03 March 2019 to 25 January 2020

Changes from 07 July 2018 to 03 March 2019

Changes from 23 January 2018 to 07 July 2018

Changes from 07 July 2018 to 03 March 2019