66°F

Aaron Parecki

  • Articles
  • Notes
  • Photos
  • How to Sign Users In with IndieAuth

    April 13, 2021

    This post will show you step by step how you can let people log in to your website with their own IndieAuth website so you don't need to worry about user accounts or passwords.

    What is IndieAuth? IndieAuth is an extension of OAuth 2.0 that enables an individual website like someone's WordPress, Gitea or OwnCast instance to become its own identity provider. This means you can use your own website to sign in to other websites that support IndieAuth.

    You can learn more about the differences between IndieAuth and OAuth by reading OAuth for the Open Web.

    What You'll Need

    You'll need a few tools and libraries to sign users in with IndieAuth.

    • An HTTP client.
    • A URL parsing library.
    • A hashing library that supports SHA256.
    • A library to find <link> tags in HTML.
    • The ability to show an HTML form to the user.

    IndieAuth Flow Summary

    Here is a summary of the steps to let people sign in to your website with IndieAuth. We'll dive deeper into each step later in this post.

    • Present a sign-in form asking the user to enter their server address.
    • Fetch the URL to discover their IndieAuth server.
    • Redirect them to their IndieAuth server with the details of your sign-in request in the query string.
    • Wait for the user to be redirected back to your website with an authorization code in the query string.
    • Exchange the authorization code for the user's profile information by making an HTTP request to their IndieAuth server.

    Step by Step

    Let's dive into the details of each step of the flow. While this is meant to be an approachable guide to IndieAuth, eventually you'll want to make sure to read the spec to make sure you're handling all the edge cases you might encounter properly.

    Show the Sign-In Form

    First you'll need to ask the user to enter their server address. You should show a form with a single HTML field, <input type="url">. You need to know at least the server name of the user's website.

    To improve the user experience, you should add some JavaScript to automatically add the https:// scheme if the user doesn't type it in.

    The form should submit to a route on your website that will start the flow. Here's a complete example of an IndieAuth sign-in form.

    <form action="/indieauth/start" method="post">
      <input type="url" name="url" placeholder="example.com">
      <br>
      <input type="submit" value="Sign In">
    </form>
    

    When the user submits this form, you'll start with the URL they enter and you're ready to begin the IndieAuth flow.

    Discover the IndieAuth Server

    There are potentially two URLs you'll need to find at the URL the user entered in order to complete the flow: the authorization endpoint and token endpoint.

    The authorization endpoint is where you'll redirect the user to so they can sign in and approve the request. Eventually they'll be redirected back to your app with an authorization code in the query string. You can take that authorization code and exchange it for their profile information. If your app wanted to read or write additional data from their website, such as when creating posts using Micropub, it could exchange that code at the second endpoint (the token endpoint) to get an access token.

    To find these endpoints, you'll fetch the URL the user entered (after validating and normalizing it first) and look for <link> tags on the web page. Specifically, you'll be looking for <link rel="authorization_endpoint" href="..."> and <link rel="token_endpoint" href="..."> to find the endpoints you need for the flow. You'll want to use an HTML parser or a link rel parser library to find these URLs.

    Start the Flow by Redirecting the User

    Now you're ready to send the user to their IndieAuth server to have them log in and approve your request.

    You'll need to take the authorization endpoint you discovered in the previous request and add a bunch of parameters to the query string, then redirect the user to that URL. Here is the list of parameters to add to the query string:

    • response_type=code - This tells the server you are doing an IndieAuth authorization code flow.
    • client_id= - Set this value to the home page of your website the user is signing in to.
    • redirect_uri= - This is the URL where you want the user to be returned to after they log in and approve the request. It should have the same domain name as the client_id value.
    • state= - Before starting this step, you should generate a random value for the state parameter and store it in a session and include it in the request. This is for CSRF protection for your app.
    • code_challenge= - This is the base64-urlencoded SHA256 hash of a random string you will generate. We'll cover this in more detail below.
    • code_challenge_method=S256 - This tells the server which hashing method you used, which will be SHA256 or S256 for short.
    • me= - (optional) You can provide the URL the user entered in your sign-in form as a parameter here which can be a hint to some IndieAuth servers that support multiple users per server.
    • scope=profile - (optional) If you want to request the user's profile information such as their name, photo, or email, include the scope parameter in the request. The value of the scope parameter can be either profile or profile email. (Make sure to URL-encode the value when including it in a URL, so it will end up as profile+email or profile%20email.)

    Calculating the Code Challenge

    The Code Challenge is a hash of a secret (called the Code Verifier) that you generate before redirecting the user. This lets the server know that the thing that will later make the request for the user's profile information is the same thing that started the flow. You can see the full details of how to create this parameter in the spec, but the summary is:

    • Create a random string (called the Code Verifier) between 43-128 characters long
    • Calculate the SHA256 hash of the string
    • Base64-URL encode the hash to create the Code Challenge

    The part that people most often make a mistake with is the Base64-URL encoding. Make sure you are encoding the raw hash value, not a hex representation of the hash like some hashing libraries will return.

    Once you're ready with all these values, add them all to the query string of the authorization endpoint you previously discovered. For example if the user's authorization endpoint is https://indieauth.rocks/authorize because their website is https://indieauth.rocks, then you'd add these parameters to the query string to create a URL like:

    https://indieauth.rocks/authorize?response_type=code
      &client_id=https://example-app.com
      &redirect_uri=https://example-app.com/redirect
      &state=a46a0b27e67c0cb53
      &code_challenge=eBKnGb9SEoqsi0RGBv00dsvFDzJNQOyomi6LE87RVSc
      &code_challenge_method=S256
      &me=https://indieauth.rocks
      &scope=profile
    

    Note: The user's authorization endpoint might not be on the same domain as the URL they entered. That's okay! That just means they have delegated their IndieAuth handling to an external service.

    Now you can redirect the user to this URL so that they can approve this request at their own IndieAuth server.

    Handle the Redirect Back

    You won't see the user again until after they've logged in to their website and approved the request. Eventually the IndieAuth server will redirect the user back to the redirect_uri you provided in the authorization request. The authorization server will add two query parameters to the redirect: code and state. For example:

    https://example-app.com/redirect?code=af79b83817b317afc9aa
      &state=a46a0b27e67c0cb53
    

    First you need to double check that the state value in the redirect matches the state value that you included in the initial request. This is a CSRF protection mechanism. Assuming they match, you're ready to exchange the authorization code for the user's profile information.

    Exchange the Authorization Code for the User's Profile Info

    Now you'll need to make a POST request to exchange the authorization code for the user's profile information. Since this code was returned in a redirect, the IndieAuth server needs an extra confirmation that it was sent back to the right thing, which is what the Code Verifier and Code Challenge are for. You'll make a POST request to the authorization endpoint with the following parameters:

    • grant_type=authorization_code
    • code= - The authorization code as received in the redirect.
    • client_id= - The same client_id as was used in the original request.
    • redirect_uri= The same redirect_uri as was used in the original request.
    • code_verifier= The original random string you generated when calculating the Code Challenge.

    This is described in additional detail in the spec.

    Assuming everything checks out, the IndieAuth server will respond with the full URL of the user, as well as their stated profile information if requested. The response will look like the below:

    {
      "me": "https://indieauth.rocks/",
      "profile": {
        "name": "IndieAuth Rocks",
        "url": https://indieauth.rocks/"
        "photo": "https://indieauth.rocks/profile.jpg"
      }
    }
    

    Wait! We're not done yet! Just because you get information in this response doesn't necessarily mean you can trust it yet! There are two important points here:

    • The information under the profile object must ALWAYS be treated as user-supplied data, not treated as canonical or authoritative in any way. This means for example not de-duping users based on the profile.url field or profile.email field.
    • If the me URL is not an exact match of the URL the user initially entered, you need to re-discover the authorization endpoint of the me URL returned in this response and make sure it matches exactly the authorization server you found in the initial discovery step.

    You can perform the same discovery step as in the beginning, but this time using the me URL returned in the authorization code response. If that authorization endpoint matches the same authorization endpoint that you used when you started the flow, everything is fine and you can treat this response as valid.

    This last validation step is critical, since without it, anyone could set up an authorization endpoint claiming to be anyone else's server. More details are available in the spec.

    Now you're done!

    The me URL is the value you should use as the canonical and stable identifier for this user. You can use the information in the profile object to augment this user account with information like the user's name or profile information. If the user logs in again later, look up the user from their me URL and update their name/photo/email with the most recent values in the profile object to keep their profile up to date.

    Testing Your IndieAuth Client

    To test your IndieAuth client, you'll need to find a handful of IndieAuth providers in the wild you can use to sign in to it. Here are some to get you started:

    • Micro.blog - All micro.blog accounts are IndieAuth identities as well. You can use a free account for testing.
    • WordPress - With the IndieAuth plugin installed, a WordPress site can be its own IndieAuth server as well.
    • Drupal - The IndieWeb module for Drupal will let a Drupal instance be its own IndieAuth server.
    • Selfauth - Selfauth is a single PHP file that acts as an IndieAuth server.

    Eventually I will get around to finishing the test suite at indieauth.rocks so that you have a testing tool readily available, but in the mean time the options above should be enough to get you started.

    Getting Help

    If you get stuck or need help, feel free to drop by the IndieWeb chat to ask questions! Myself and many others are there all the time and happy to help troubleshoot new IndieAuth implementations!

    Portland, Oregon • 56°F
    #indieauth #indieweb #oauth
    Tue, Apr 13, 2021 9:15pm -07:00
    3 likes 3 bookmarks 1 reply 4 mentions
    • Jamie Tanna
    • Jonas Voss
    • Marty McGuire
    • Johan Bové
    • Barry Frost
    • Jan Boddez
    • Jean-Philippe Caruana jp.caruana.fr
      Thank you for this excellent step by step guide, much clearer, I think, than the spec or the explanation on indieweb.org
      Fri, Apr 16, 2021 6:29am -07:00

    Other Mentions

    • Dr. Roy Schestowitz (罗伊) twitter.com/schestowitz
      ● NEWS ● #AaronParecki ☞ How to Sign Users In with IndieAuth aaronparecki.com/2021/04/13/26/…
      Wed, May 5, 2021 5:02am +00:00 (via brid.gy)
    • Tom Larkworthy twitter.com/tomlarkworthy
      I am working on a MIT licensed federated auth server using this beautiful technology aaronparecki.com/2021/04/13/26/… HOSTED ON @observablehq !!!!! it will issue Firebase compatible tokens so can be used as drop in replacement for Firebase Auth (which stores personal data in US).
      Thu, Apr 15, 2021 11:39am +00:00 (via brid.gy)
    • Baldur Bjarnason twitter.com/fakebaldur
      “How to Sign Users In with IndieAuth • Aaron Parecki” I really like IndieAuth as a tech (basically a more manageable profile of Oauth) but suspect that it has little appeal outside of indieblogger circles. aaronparecki.com/2021/04/13/26/…
      Thu, Apr 15, 2021 10:08am +00:00 (via brid.gy)
    • joe jenett the.dailywebthing.com/an-excellent-step-by-step-guide
      an excellent step-by-step guide
      Mon, Jul 12, 2021 6:39am -07:00
Posted in /articles using ia.net/writer

Hi, I'm Aaron Parecki, Senior Security Architect at Okta, and co-founder of IndieWebCamp. I maintain oauth.net, write and consult about OAuth, and participate in the OAuth Working Group at the IETF. I also help people learn about video production and livestreaming and dabble in product design.

I've been tracking my location since 2008 and I wrote 100 songs in 100 days. I've spoken at conferences around the world about owning your data, OAuth, quantified self, and explained why R is a vowel. Read more.

  • Security Architect at Okta
  • IndieWebCamp Founder
  • OAuth WG Member

  • 🎥 YouTube Tutorials and Reviews
  • 🏠 We're building a triplex!
  • ⭐️ Life Stack
  • ⚙️ Home Automation
  • All
  • Articles
  • Bookmarks
  • Notes
  • Photos
  • Replies
  • Reviews
  • Trips
  • Videos
  • Contact
© 1999-2023 by Aaron Parecki. Powered by p3k. This site supports Webmention.
Except where otherwise noted, text content on this site is licensed under a Creative Commons Attribution 3.0 License.
IndieWebCamp Microformats Webmention W3C HTML5 Creative Commons
WeChat ID
aaronpk_tv