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 | = file_get_contents('authorization-flow-diagram.svg') ?>
354 |
355 | 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 |
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 |
399 | response_type=code - Indicates to the authorization server that an authorization code should be returned as the response
400 | client_id - The client URL
401 | redirect_uri - The redirect URL indicating where the user should be redirected to after approving the request
402 | state - A parameter set by the client which will be included when the user is redirected back to the client. This is used to prevent CSRF attacks. The authorization server MUST return the unmodified state value back to the client.
403 | code_challenge - The code challenge as previously described.
404 | code_challenge_method - The hashing method used to calculate the code challenge, e.g. "S256"
405 | scope - (optional) A space-separated list of scopes the client is requesting, e.g. "profile", or "profile create". If the client omits this value, the authorization server MUST NOT issue an access token for this authorization code. Only the user's profile URL may be returned without any scope requested. See Profile Information for details about which scopes to request to return user profile information.
406 | me - (optional) The URL that the user entered
407 |
408 |
409 | = htmlspecialchars(
410 | 'https://example.org/auth?response_type=code&
411 | client_id=https://app.example.com/&
412 | redirect_uri=https://app.example.com/redirect&
413 | state=1234567890&
414 | code_challenge=OfYAxt8zU2dAPDWQxTAUIteRzMsoj9QBdMIVEDOErUo&
415 | code_challenge_method=S256&
416 | scope=profile+create+update+delete&
417 | me=https://user.example.net/') ?>
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 | = htmlspecialchars(
444 | 'HTTP/1.1 302 Found
445 | Location: https://app.example.com/redirect?code=xxxxxxxx
446 | state=1234567890') ?>
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 | = htmlspecialchars(
478 | 'POST https://example.org/auth
479 | Content-type: application/x-www-form-urlencoded
480 | Accept: application/json
481 |
482 | grant_type=authorization_code
483 | &code=xxxxxxxx
484 | &client_id=https://app.example.com/
485 | &redirect_uri=https://app.example.com/redirect
486 | &code_verifier=a6128783714cfda1d388e2e98b6ae8221ac31aca31959e59512c59f5
487 | ') ?>
488 |
489 | Example request to token endpoint
490 | = htmlspecialchars(
491 | 'POST https://example.org/token
492 | Content-type: application/x-www-form-urlencoded
493 | Accept: application/json
494 |
495 | grant_type=authorization_code
496 | &code=xxxxxxxx
497 | &client_id=https://app.example.com/
498 | &redirect_uri=https://app.example.com/redirect
499 | &code_verifier=a6128783714cfda1d388e2e98b6ae8221ac31aca31959e59512c59f5
500 | ') ?>
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 | = htmlspecialchars(
513 | 'HTTP/1.1 200 OK
514 | Content-Type: application/json
515 |
516 | {
517 | "me": "https://user.example.net/"
518 | }') ?>
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 |
621 | - entering a domain with a www prefix and resolving it to the main domain
622 | - entering a URL with no scheme or with http and resolving it to an https URL
623 | - entering a short domain and resolving it to a different longer domain
624 |
625 |
626 |
627 | Steps
628 |
629 |
630 | - The user enters
www.example.com into the client
631 | - The client applies the steps from URL canoncalization to turn it into a URL:
http://www.example.com/
632 | - The client makes a GET request to
http://www.example.com/
633 | - The server returns a 301 redirect to
https://example.com/
634 | - The client makes a GET request to
https://example.com/ and finds the authorization endpoint
635 | - 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.
636 | - 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/
637 |
638 |
639 | Service Domain to Subdomain
640 |
641 |
642 | - The user enters
example.com into the client
643 | - The client applies the steps from URL canoncalization to turn it into a URL:
http://example.com/
644 | - The client makes a GET request to
http://example.com/
645 | - The server returns a 301 redirect to
https://example.com/
646 | - The client makes a GET request to
https://example.com/ and finds the authorization endpoint, https://login.example.com
647 | - 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.
648 | - 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
649 | - The client accepts the
me value of https://username.example.com/
650 |
651 |
652 | Service Domain to Path
653 |
654 |
655 | - The user enters
example.com into the client
656 | - The client applies the steps from URL canoncalization to turn it into a URL:
http://example.com/
657 | - The client makes a GET request to
http://example.com/
658 | - The server returns a 301 redirect to
https://example.com/
659 | - The client makes a GET request to
https://example.com/ and finds the authorization endpoint, https://login.example.com
660 | - 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.
661 | - 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
662 | - The client accepts the
me value of https://example.com/username
663 |
664 |
665 | Email-like Identifier
666 |
667 |
668 | - The user enters
user@example.com into the client
669 | - The client applies the steps from URL canoncalization to turn it into a URL:
http://user@example.com/
670 | - The client makes a GET request to
http://example.com/ providing the HTTP Basic Auth username user
671 | - The server returns a 301 redirect to
https://example.com/
672 | - 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 |
675 | - 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.
676 | - 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
677 | - The client accepts the
me value of https://example.com/username
678 |
679 |
680 |
681 |
682 |