├── .gitignore ├── public ├── errata.php ├── assets │ ├── triangles.png │ ├── web-signin-splash.jpg │ ├── indieauth-logo-twitter.png │ ├── styles.css │ └── INDIEWEB-LS.svg ├── note │ ├── manifest.txt │ ├── authentication-flow-diagram.svg │ └── authorization-flow-diagram.svg ├── implementations.php ├── source │ ├── add-paragraph-ids.js │ ├── indieweb-cleanup.js │ ├── authorization-flow-diagram.svg │ └── index.php ├── pingback.php ├── webmention.php ├── index.php └── spec │ └── LIVINGSTANDARD.svg └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /public/errata.php: -------------------------------------------------------------------------------- 1 | $source, 21 | 'target' => $target 22 | ])); 23 | curl_exec($ch); 24 | 25 | header('Content-type: text/xml'); 26 | echo xmlrpc_encode('pingback_accepted'); 27 | } else { 28 | header('HTTP/1.1 400 Bad Request'); 29 | } 30 | -------------------------------------------------------------------------------- /public/assets/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #212121; 3 | font-family: "Helvetica Neue", "Calibri Light", Roboto, sans-serif; 4 | -webkit-font-smoothing: antialiased; 5 | -moz-osx-font-smoothing: grayscale; 6 | letter-spacing: 0.02em; 7 | } 8 | 9 | body { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | section { 15 | max-width: 600px; 16 | margin: 0 auto; 17 | padding: 4px; 18 | } 19 | 20 | section:after { 21 | display: block; 22 | content: ''; 23 | clear: both; 24 | } 25 | 26 | section.hang-left img { 27 | float: left; 28 | width: 400px; 29 | margin-left: -200px; 30 | margin-right: 20px; 31 | border: 4px #513162 solid; 32 | border-radius: 6px; 33 | } 34 | 35 | section.border-top { 36 | border-top: 2px #aaa solid; 37 | margin-top: 20px; 38 | } 39 | 40 | @media(max-width: 600px) { 41 | section { 42 | margin-left: 10px; 43 | margin-right: 10px; 44 | } 45 | } 46 | 47 | @media(max-width: 890px) { 48 | section.hang-left img { 49 | float: none; 50 | margin-left: 0; 51 | margin-right: 0; 52 | width: 100%; 53 | border: 0; 54 | } 55 | } 56 | 57 | 58 | .header-bar h1 { 59 | font-size: 40px; 60 | margin: 0; 61 | padding: 10px; 62 | text-align: center; 63 | } 64 | 65 | .featured { 66 | background: #513162; 67 | background: url(/assets/triangles.png); 68 | color: white; 69 | padding: 40px; 70 | font-size: 2em; 71 | text-align: center; 72 | margin-bottom: 20px; 73 | } 74 | 75 | a { 76 | color: #683881; 77 | } 78 | a:hover { 79 | color: #513162; 80 | } 81 | 82 | 83 | footer { 84 | background-color: #aaa; 85 | padding: 1em 0 4em 0; 86 | } 87 | 88 | footer ul { 89 | margin: 0; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /public/source/indieweb-cleanup.js: -------------------------------------------------------------------------------- 1 | function indiewebCleanup() { 2 | // Replace W3C logo with IndieWebCamp logo 3 | $(".head .logo img").attr("src", "https://spec.indieweb.org/index_files/logo.svg"); 4 | $(".head .logo img").attr("width", "120").attr("height", "100"); 5 | 6 | // Replace subhead 7 | var time = $(".head h2 time").html(); 8 | $(".head h2").html("IndieWeb Living Standard "+time); 9 | 10 | // CC0 11 | $(".copyright").html("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."); 12 | 13 | // Replace SOTD section 14 | $("#sotd p").remove(); 15 | $("#sotd").append("

This document was published by the IndieWeb community as a Living Standard.

"); 16 | $("#sotd").append("

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.

"); 17 | 18 | // Replace "this version" link 19 | $(".head dl dd:first a").text(respecConfig.lsURI).attr("href", respecConfig.lsURI); 20 | 21 | // Replace "previous version" link 22 | $(".head dl dd:nth-of-type(4) a").text(respecConfig.previousVersionURL).attr("href", respecConfig.previousVersionURL); 23 | 24 | // Remove "latest published version" link 25 | $(".head dl dt:nth-of-type(2)").remove(); 26 | $(".head dl dd:nth-of-type(2)").remove(); 27 | 28 | $("body").css("background-image", "url(https://spec.indieweb.org/INDIEWEB-LIVING-STANDARD.svg)"); 29 | } 30 | -------------------------------------------------------------------------------- /public/webmention.php: -------------------------------------------------------------------------------- 1 | $_POST['source'], 8 | 'target' => $_POST['target'] 9 | ])); 10 | curl_setopt($ch, CURLOPT_HEADER, true); 11 | $response = curl_exec($ch); 12 | $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); 13 | list($code, $status, $headers) = parse_headers(trim(substr($response, 0, $header_size))); 14 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 15 | $body = substr($response, $header_size); 16 | 17 | // Pass back the response 18 | header('HTTP/1.1 '.$code.' '.$status); 19 | foreach($headers as $key=>$val) { 20 | if(is_array($val)) 21 | foreach($val as $v) 22 | header($key . ': ' . $v, false); 23 | else 24 | header($key . ': ' . $val, false); 25 | } 26 | echo $body; 27 | } else { 28 | ?> 29 |
30 |
31 |
32 | 33 |
34 | 2 | 3 | 4 | 5 | IndieAuth 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |

IndieAuth

15 |
16 |
17 | 18 | 23 | 24 |
25 |

IndieAuth is a decentralized identity protocol built on top of OAuth 2.0.

26 |

This allows individual websites like someone's WordPress, Mastodon, or Gitea server to become its own identity provider, and can be used to sign in to other instances. Both users and applications are identified by URLs, avoiding the need for getting API keys or making new accounts.

27 |

Read more about how IndieAuth solves OAuth for the open web.

28 |
29 | 30 |
31 |

Read the Spec

32 | 33 |

The latest version of the IndieAuth spec is available at:

34 | 35 |

indieauth.spec.indieweb.org

36 | 37 |

The January 2018 published version is also available at:

38 | 39 |

w3.org/TR/indieauth

40 |
41 | 42 |
43 | 44 | 45 |

Logging in with IndieAuth

46 | 47 |

You can use IndieAuth to have your users authenticate with their own URL. Logging in to an app with IndieAuth tells the app who has logged in, where the user ID returned is a URL controlled by the user.

48 |

Logging in with IndieAuth

49 |
50 | 51 |
52 |

Obtaining an OAuth 2.0 Access Token with IndieAuth

53 | 54 |

If you're building an application that wants to access or modify a user's data, you'll need an OAuth 2.0 access token to use in API requests.

55 |

You can use IndieAuth to obtain an access token from the user's own token endpoint, while identifying them in the process.

56 |

Obtaining an Access Token

57 |
58 | 59 |
60 |

Choosing an IndieAuth Provider

61 | 62 |

In order to log in to apps that use IndieAuth, you'll need to tell these apps where your IndieAuth endpoints live. You can either delegate your domain to an external IndieAuth provider, run an IndieAuth provider yourself, or your IndieAuth provider may already be part of the same software that runs your website.

63 | 64 |

Public IndieAuth Providers

65 | 68 | 69 |

Self-Hosted IndieAuth Providers

70 | 77 | 78 |

Software with a Built-In IndieAuth Provider

79 | 83 | 84 |

Services with Built-In IndieAuth Support

85 | 88 |
89 | 90 | 91 |
92 |

Frequently Asked Questions

93 | 94 |

How is IndieAuth different from OpenID Connect?

95 |

See indieweb.org/How_is_IndieAuth_different_from_OpenID_Connect

96 | 97 |
98 | 99 | 100 |
101 | 102 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /public/spec/LIVINGSTANDARD.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 | 25 | 27 | 29 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 50 | 51 | 52 | 53 | 56 | 57 | 59 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /public/assets/INDIEWEB-LS.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 16 | 23 | 26 | 33 | 35 | 42 | 49 | 50 | 53 | 54 | 56 | 60 | 69 | 77 | 80 | 88 | 92 | 98 | 106 | 109 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /public/note/authentication-flow-diagram.svg: -------------------------------------------------------------------------------- 1 | IndieAuth Authentication Flow DiagramBrowserClientUser URLAuthorization EndpointUser enters their profile URLClient fetches URL to discoverrel=authorization_endpointClient builds authorization request andredirects to authorization_endpointUser visits their authorization endpoint and sees the authentication requestAuthorization endpoint fetches client information (name, icon)User authenticates, and approves the request.Authorization endpoint issues code, builds redirect back to client.User's browser is redirected toclient with an authorization codeClient verifies authorization code by making a POST requestto the authorization_endpointAuthorization endpoint returns canonical user profile URLClient initiates login sessionand the user is logged in -------------------------------------------------------------------------------- /public/note/authorization-flow-diagram.svg: -------------------------------------------------------------------------------- 1 | IndieAuth Authentication Flow DiagramBrowserClientUser URLAuthorization EndpointToken EndpointUser enters their profile URLClient fetches URL to discoverrel=authorization_endpointand rel=token_endpointClient builds authorization request andredirects to authorization_endpointUser visits their authorization endpoint and sees the authorization requestAuthorization endpoint fetches client information (name, icon)User authenticates, and approves the request.Authorization endpoint issues code, builds redirect back to client.User's browser is redirected toclient with an authorization codeClient exchanges authorization code for an access token by making a POST requestto the token_endpointToken endpoint verifies code and returnscanonical user profile URL with an access tokenClient initiates login sessionand the user is logged in -------------------------------------------------------------------------------- /public/source/authorization-flow-diagram.svg: -------------------------------------------------------------------------------- 1 | IndieAuth Flow DiagramBrowserClientUser URLAuthorization EndpointToken EndpointUser enters a URL, and theclient canonicalizes the URLClient fetches URL to discoverrel=authorization_endpointand rel=token_endpointClient builds authorization request andredirects to authorization_endpointUser visits their authorization endpoint and sees the authorization requestAuthorization endpoint fetches client information (name, icon)User authenticates, and approves the request.Authorization endpoint issues code, builds redirect back to client.User's browser is redirected toclient with an authorization codeClient exchanges authorization code for an access token by making a POST requestto the token_endpointToken endpoint verifies code and returnscanonical user profile URL with an access tokenClient confirms the user's profile URLdeclares the same authorization serverClient initiates login sessionand the user is logged in 2 | -------------------------------------------------------------------------------- /public/source/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IndieAuth 5 | 6 | 7 | 8 | 9 | 108 | 109 | 110 | 111 | 112 |
113 |

114 | IndieAuth is an identity layer on top of OAuth 2.0 [[RFC6749]], primarily used to obtain 115 | an OAuth 2.0 Bearer Token [[RFC6750]] for use by [[?Micropub]] clients. End-Users 116 | and Clients are all represented by URLs. IndieAuth enables Clients to 117 | verify the identity of an End-User, as well as to obtain an access 118 | token that can be used to access resources under the control of the End-User. 119 |

120 | 121 |
122 |

Author's Note

123 |

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

127 |
128 |
129 | 130 |
131 |
132 | 133 |
134 |

Introduction

135 | 136 |
137 |

Background

138 | 139 |

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.

140 |
141 | 142 |
143 |

OAuth 2.0 Extension

144 | 145 |

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

146 | 147 | 156 | 157 |

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

158 |
159 |
160 | 161 |
162 |

Conformance

163 | 164 |

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 165 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 166 | document are to be interpreted as described in [[!RFC2119]].

167 | 168 |
169 |

Conformance Classes

170 | 171 |

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.

172 | 173 |

Listed below are known types of IndieAuth implementations.

174 | 175 |
176 |

Authorization Endpoint

177 |

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

178 |
179 | 180 |
181 |

Token Endpoint

182 |

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

183 |
184 | 185 |
186 |

Micropub Client

187 |

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.

188 |
189 | 190 |
191 |

IndieAuth Client

192 |

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.

193 |
194 | 195 |
196 |
197 | 198 |
199 |

Identifiers

200 | 201 |
202 |

User Profile URL

203 | 204 |

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.

205 | 206 |

Some examples of valid profile URLs are:

207 | 208 | 213 | 214 |

Some examples of invalid profile URLs are:

215 | 224 |
225 | 226 |
227 |

Client Identifier

228 | 229 |

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

230 |
231 | 232 |
233 |

URL Canonicalization

234 | 235 |

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.

236 | 237 |

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.

238 | 239 |

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.

240 |
241 | 242 |
243 | 244 |
245 |

Discovery

246 | 247 |

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

249 | 250 |
251 |

Discovery by Clients

252 | 253 |

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.

254 | 255 |

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

256 | 257 |

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.

258 | 259 |

The endpoints discovered MAY be relative URLs, in which case the client MUST resolve them relative to the current document URL according to [[!URL]].

260 | 261 |

Clients MAY initially make an HTTP HEAD request [[!RFC7231]] to follow redirects and check for the Link header before making a GET request.

262 |
263 | 264 |
265 |

Client Information Discovery

266 | 267 |

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.

268 | 269 |

Since client identifiers are URLs, the authorization server SHOULD [[!Fetch]] the URL to find more information about the client.

270 | 271 |
272 |

Application Information

273 | 274 |

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.

275 | 276 |

278 |   
279 |   Example App
280 | ') ?>
281 | 282 |

This can be parsed with a Microformats2 parser, which will result in the following JSON structure.

283 | 284 |
{
285 |   "type": [
286 |     "h-app"
287 |   ],
288 |   "properties": {
289 |     "name": ["Example App"],
290 |     "logo": ["https://app.example.com/logo.png"],
291 |     "url": ["https://app.example.com/"]
292 |   }
293 | }
294 |
295 | 296 |
297 |

Redirect URL

298 | 299 |

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

300 | 301 |

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 discovered after resolving any relative URLs.

302 | 303 |
; rel="redirect_uri"
309 | 
310 | 
311 | 
312 |   
313 |     
314 |   
315 |   ...
316 | ') ?>
317 |
318 | 319 |
320 | 321 |
322 | 323 |
324 |

Authorization

325 | 326 |

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

327 | 328 | Client: User enters a URL, and the\nclient canonicalizes the URL 335 | Client->User URL: Client fetches URL to discover\n**rel=authorization_endpoint**\nand **rel=token_endpoint** 336 | Browser<--Client: Client builds authorization request and\nredirects to **authorization_endpoint** 337 | Browser->Authorization Endpoint: User visits their authorization endpoint and sees the authorization request 338 | Authorization Endpoint->Client: Authorization endpoint fetches client information (name, icon) 339 | Browser<--Authorization Endpoint: User authenticates, and approves the request.\nAuthorization endpoint issues code, builds redirect back to client. 340 | Browser->Client: User's browser is redirected to\nclient with an **authorization code** 341 | Client->Token Endpoint: Client exchanges authorization code for an \naccess token by making a POST request\nto the token_endpoint 342 | Client<--Token Endpoint: Token endpoint verifies code and returns\ncanonical user profile URL with an access token 343 | Client->User URL: Client confirms the user's profile URL\ndeclares the same authorization server 344 | Browser<--Client: Client initiates login session\nand the user is logged in 345 | --- 346 | 347 | https://sequencediagram.org/index.html#initialData=C4S2BsFMAIEkDsAmJIEECuwAW0Bi4B7Ad2gBEQBDAcwCcKBbAKEYCEbiBnSGgWgD4AwuBTxgALmgBVLjWiRR3DtApSASgBkANMqTRskADrwAxsPnBoxivALwQV4QC9IS-WvWMhI4P2nd3El7m0ABmkMDGWC7uegTQyBzGBABu3EYAVOk0kOAAvBSYWAQ0II4UoLYA+vKIAA4EIKKZRtaI0JnZecAEANby1Uj1jcCZrOxEMgA8PDxBooFmotAARugg4IhKBdjFpeUgttDZAI7oLhatRtnI2cbArnGZ20UlZRXwA3UNTeljnNz8DA7V77Q4AUUG33EUhk0GSIA4YFcURAsmeuzeB3gckhwx0bS40Tc6JB7yOkFO50YQJeezJEK+w34c2hNIxoOxNSGSzCESiSlM3mgjRCxXoHOgAAp4AxINp7LYAJR-CbcaY8NmkrHQBnc6F+NGFcz2coubStZS1WrsVLImAnM4cYAAOiMmrp2q5UOFHA4jssBEQcpWaw2SmuqMgdxWFGMPVilkWLpVMmZSYkBoA5EpluNYQjyTco8BIG1ukZBcEiGAcNZ2ukSR7Dkkg6MWfwACq9eQ63HzaAsuQAD0i1io0UbmObgZgorR2JaxmMLgefWxywAntBxT1GlRlNAAAoAeQAyh3yZSnUZunoorE1589Z4k+qu2ve4z+++e168akShCFABRnfFyWAdAaHgDgK2sWwTXAaB0Fha0CCAqAYmrbAdGUJcVwfeQX28XxYUkDQFiFJJ4CAmh6DtJCZGzaBUPQmAyPUIwg1MChsnojhZWUQp2TJGQAJTNUZhZCjgkaMBKBLJRCCoRpoC4X0sRaXQ3GQ-wCyU8c2kaIA 348 | 349 | Note: Set a viewbox matching "0 0 width height" and have the image scale, e.g. 350 | viewbox="0 0 1163 721" style="width: 100%; height: auto;" 351 | */ ?> 352 | 353 | 354 | 355 | 365 | 366 |

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.

367 | 368 |
369 |

Discovery

370 | 371 |

After obtaining a URL from the End-User, and optionally applying URL Canonicalization to it, 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 .

372 | 373 |
; rel="authorization_endpoint"
375 | Link: ; rel="token_endpoint"
376 | 
377 | 
378 | ') ?>
379 |
380 | 381 |
382 |

Authorization Request

383 | 384 |

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

385 | 386 |

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.

387 | 388 |

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.

389 | 390 |

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.

391 | 392 |

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.

393 | 394 | code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) 395 | 396 |

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

397 | 398 | 408 | 409 |
418 | 419 |

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.

420 | 421 |

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.

422 | 423 |

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.

424 | 425 |

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.

426 | 427 |

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.

428 | 429 |

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.

430 | 431 |
432 |

Authorization Response

433 | 434 |

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

435 | 436 |

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:

437 | 438 |
    439 |
  • 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.
  • 440 |
  • state - The state parameter MUST be set to the exact value that the client set in the request.
  • 441 |
442 | 443 |
447 | 448 |

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.

449 | 450 |

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

451 |
452 |
453 | 454 |
455 |

Redeeming the Authorization Code

456 | 457 |

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

458 | 459 |
460 |

Request

461 | 462 |

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.

463 | 464 |

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.

465 | 466 |

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:

467 | 468 |
    469 |
  • grant_type=authorization_code
  • 470 |
  • code - The authorization code received from the authorization endpoint in the redirect.
  • 471 |
  • client_id - The client's URL, which MUST match the client_id used in the authentication request.
  • 472 |
  • redirect_uri - The client's redirect URL, which MUST match the initial authentication request.
  • 473 |
  • code_verifier - The original plaintext random string generated before starting the authorization request.
  • 474 |
475 | 476 | Example request to authorization endpoint 477 |
488 | 489 | Example request to token endpoint 490 |
501 |
502 | 503 |

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.

504 | 505 |
506 |

Profile URL Response

507 | 508 |

When the client receives an authorization code that was requested with either no scope or only profile scopes (defined below), the client will exchange the authorization code at the authorization endpoint, and only the canonical user profile URL and possibly profile information is returned.

509 | 510 |

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.

511 | 512 |
519 | 520 |

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.

521 | 522 |

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

523 |
524 | 525 |
526 |

Access Token Response

527 | 528 |

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.

529 | 530 |

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

531 | 532 |

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.

533 | 534 |

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 the OAuth 2.0 Bearer Token [[!RFC6750]], as well as a property me, containing the canonical user profile URL for the user this access token corresponds to, and optionally the property profile with the user's profile information as defined in Profile Information. For example:

535 | 536 |
HTTP/1.1 200 OK
537 | Content-Type: application/json
538 | 
539 | {
540 |   "access_token": "XXXXXX",
541 |   "token_type": "Bearer",
542 |   "scope": "create update delete",
543 |   "me": "https://user.example.net/"
544 | }
545 | 546 |

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.

547 | 548 |

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

549 |
550 | 551 |
552 |

Profile Information

553 | 554 |
Requesting Profile Information
555 | 556 |

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:

557 | 558 |
    559 |
  • profile (required) - This scope requests access to the user's default profile information which include the following properties: name, photo, url.
  • 560 |
  • email - This scope requests access to the user's email address in the following property: email.
  • 561 |
562 | 563 |

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.

564 | 565 |

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.

566 | 567 |

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:

568 | 569 |
HTTP/1.1 200 OK
570 | Content-Type: application/json
571 | 
572 | {
573 |   "access_token": "XXXXXX",
574 |   "token_type": "Bearer",
575 |   "scope": "profile email create",
576 |   "me": "https://user.example.net/",
577 |   "profile": {
578 |     "name": "Example User",
579 |     "url": "https://user.example.net/",
580 |     "photo": "https://user.example.net/photo.jpg",
581 |     "email": "user@example.net"
582 |   }
583 | }
584 | 585 |

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.

586 | 587 |

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.

588 | 589 |

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.

590 | 591 |

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.

592 | 593 |

Similarly, the url returned in the profile object 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.

594 | 595 |
596 | 597 |
598 | 599 |
600 |

Authorization Server Confirmation

601 | 602 | 603 |

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.

604 | 605 |

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.

606 | 607 |

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.

608 | 609 |

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.

610 | 611 |

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.

612 | 613 |

Examples

614 | 615 |

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.

616 | 617 |
Basic Redirect
618 | 619 |

The basic redirect example covers cases such as: 620 |

625 |

626 | 627 |

Steps

628 | 629 |
    630 |
  1. The user enters www.example.com into the client
  2. 631 |
  3. The client applies the steps from URL canoncalization to turn it into a URL: http://www.example.com/
  4. 632 |
  5. The client makes a GET request to http://www.example.com/
  6. 633 |
  7. The server returns a 301 redirect to https://example.com/
  8. 634 |
  9. The client makes a GET request to https://example.com/ and finds the authorization endpoint
  10. 635 |
  11. 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.
  12. 636 |
  13. 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/
  14. 637 |
638 | 639 |
Service Domain to Subdomain
640 | 641 |
    642 |
  1. The user enters example.com into the client
  2. 643 |
  3. The client applies the steps from URL canoncalization to turn it into a URL: http://example.com/
  4. 644 |
  5. The client makes a GET request to http://example.com/
  6. 645 |
  7. The server returns a 301 redirect to https://example.com/
  8. 646 |
  9. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com
  10. 647 |
  11. 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.
  12. 648 |
  13. 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
  14. 649 |
  15. The client accepts the me value of https://username.example.com/
  16. 650 |
651 | 652 |
Service Domain to Path
653 | 654 |
    655 |
  1. The user enters example.com into the client
  2. 656 |
  3. The client applies the steps from URL canoncalization to turn it into a URL: http://example.com/
  4. 657 |
  5. The client makes a GET request to http://example.com/
  6. 658 |
  7. The server returns a 301 redirect to https://example.com/
  8. 659 |
  9. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com
  10. 660 |
  11. 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.
  12. 661 |
  13. 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
  14. 662 |
  15. The client accepts the me value of https://example.com/username
  16. 663 |
664 | 665 |
Email-like Identifier
666 | 667 |
    668 |
  1. The user enters user@example.com into the client
  2. 669 |
  3. The client applies the steps from URL canoncalization to turn it into a URL: http://user@example.com/
  4. 670 |
  5. The client makes a GET request to http://example.com/ providing the HTTP Basic Auth username user
  6. 671 |
  7. The server returns a 301 redirect to https://example.com/
  8. 672 |
  9. The client makes a GET request to https://example.com/ and finds the authorization endpoint, https://login.example.com 673 |
    • 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
    674 |
  10. 675 |
  11. 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.
  12. 676 |
  13. 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
  14. 677 |
  15. The client accepts the me value of https://example.com/username
  16. 678 |
679 | 680 |
681 | 682 |
683 | 684 |
685 |

Access Token Verification

686 | 687 |

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.

688 | 689 |

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.

690 | 691 |
692 |

Access Token Verification Request

693 | 694 |

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.

695 | 696 |
GET https://example.org/token
697 |   Authorization: Bearer xxxxxxxx
698 |   Accept: application/json
699 |
700 | 701 |
702 |

Access Token Verification Response

703 | 704 |

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

705 | 706 | 711 | 712 |
HTTP/1.1 200 OK
713 |   Content-Type: application/json
714 | 
715 |   {
716 |   "me": "https://user.example.net/",
717 |   "client_id": https://app.example.com/",
718 |   "scope": "create update delete"
719 |   }
720 | 721 |

Specific implementations MAY include additional parameters as top-level JSON properties. Clients SHOULD ignore parameters they don't recognize.

722 | 723 |

If the token is not valid, the endpoint MUST return an appropriate HTTP 400, 401 or 403 response. The response body is not significant.

724 |
725 |
726 | 727 |
728 |

Token Revocation

729 | 730 |

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:

731 | 732 | 736 | 737 |
738 |

Token Revocation Request

739 | 740 |

An example revocation request is below.

741 | 742 |
POST https://example.org/token HTTP/1.1
743 |   Content-Type: application/x-www-form-urlencoded
744 |   Accept: application/json
745 | 
746 |   action=revoke
747 |   &token=xxxxxxxx
748 | 749 |

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.

750 |
751 |
752 | 753 | 754 |
755 |

Security Considerations

756 | 757 |

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.

758 | 759 |
760 |

Preventing Phishing and Redirect Attacks

761 | 762 |

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.

763 | 764 |

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.

765 | 766 |

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.

767 |
768 | 769 |
770 | 771 |
772 |

IANA Considerations

773 | 774 |

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

775 | 776 |
777 |
Relation Name:
778 |
authorization_endpoint
779 | 780 |
Description:
781 |
Used for discovery of the OAuth 2.0 authorization endpoint given an IndieAuth profile URL.
782 | 783 |
Reference:
784 |
IndieAuth Specification (https://indieauth.spec.indieweb.org/)
785 |
786 | 787 |
788 |
Relation Name:
789 |
token_endpoint
790 | 791 |
Description:
792 |
Used for discovery of the OAuth 2.0 token endpoint given an IndieAuth profile URL.
793 | 794 |
Reference:
795 |
IndieAuth Specification (https://indieauth.spec.indieweb.org/)
796 |
797 | 798 |
799 |
Relation Name:
800 |
redirect_uri
801 | 802 |
Description:
803 |
Used for discovery of the OAuth 2.0 redirect URI given an IndieAuth client ID.
804 | 805 |
Reference:
806 |
IndieAuth Specification (https://indieauth.spec.indieweb.org/)
807 |
808 |
809 | 810 | 818 | 819 |
820 |

Resources

821 | 822 |

823 |

829 |

830 | 831 |
832 |

Articles

833 | 834 |

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

835 |
836 | 837 |
838 |

Implementations

839 | 840 |

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

841 |
842 | 843 |
844 | 845 |
846 |

Acknowledgements

847 | 848 |

The editor wishes to thank the IndieWeb 849 | community and other implementers for their support, encouragement and enthusiasm, 850 | 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.

851 |
852 | 853 |
854 |

Change Log

855 | 856 |
857 |

Changes from 26 September 2020 to this version

858 | 863 |
864 | 865 |
866 |

Changes from 09 August 2020 to 26 September 2020

867 | 873 |
874 | 875 |
876 |

Changes from 25 January 2020 to 09 August 2020

877 | 885 |
886 | 887 |
888 |

Changes from 03 March 2019 to 25 January 2020

889 | 894 |
895 | 896 |
897 |

Changes from 07 July 2018 to 03 March 2019

898 | 902 |
903 | 904 |
905 |

Changes from 23 January 2018 to 07 July 2018

906 | 910 |
911 | 912 |
913 |

Changes from 07 July 2018 to 03 March 2019

914 | 915 | 920 |
921 |
922 | 923 | 939 | 940 | 941 | --------------------------------------------------------------------------------