IndieAuth Features

The indieweb-utils library provides a number of helper functions that will enable you to implement your own IndieAuth authentication and token endpoints in Python.

These functions may be useful if you want to bundle an authentication and token provider with a service you are building, such as a personal blog or a social reader.

This page outlines how to use the IndieAuth features provided in this library.

Get application scope reference

The library comes with a constant variable that lists various scopes that are commonly used in IndieWeb community applications. These scopes cover values you might see in Micropub, Microsub, and other applications.

You may want to use this reference to give a user additional information about the scopes to which they are granting access by authenticating with a service.

To access the scope reference, import the following variable:

from indieweb_utils import SCOPE_DEFINITIONS

Discover IndieAuth endpoints

The discover_indieauth_endpoints() function retrieves the indieauth-metadata endpoint for a web page, if available. It then returns an object with all of the valid values specified in the IndieAuth spec.

If an indieauth-metadata endpoint is not found, this function looks for the following endpoint values on the specified web page:

  • authorization_endpoint

  • token_endpoint

  • ticket_endpoint

The function will then return these values if they are specified.

This function should be used instead of discover_endpoints() because it performs the additional indieauth-metadata check that is now required in IndieAuth endpoint discovery.

indieweb_utils.discover_indieauth_endpoints(url: str) IndieAuthEndpoints[source]

Discover and return the IndieAuth endpoints associated with a resource.

Parameters:

url (str) – The URL of the resource whose endpoints should be discovered.

Returns:

The IndieAuth endpoints associated with the resource.

Return type:

IndieAuthEndpoints

Example:

import indieweb_utils

endpoints = indieweb_utils.discover_indieauth_endpoints("https://jamesg.blog")

print(endpoints.authorization_endpoint) # https://indieauth.com/auth
Raises:

requests.exceptions.RequestException – If the request to the resource fails.

Get a h-app object

You might want to retrieve a h-app object to show context to the user about the application that is requesting a user to authenticate.

You can do this using the following function:

indieweb_utils.get_h_app_item(web_page: str) ApplicationInfo[source]

Get the h-app item from the web page.

Parameters:
  • web_page (str) – The web page to parse.

  • client_id (str) – The client id of your application.

Returns:

The h-app item.

Return type:

ApplicationInfo

Example:

import indieweb_utils

app_url = "https://quill.p3k.io/"
client_id = "https://quill.p3k.io/"

h_app_item = indieweb_utils.get_h_app_item(
    app_url, client_id
)

print(h_app_item.name) # Quill
Raises:

HAppNotFound – Raised if no mf2 h-app data was found on the specified page.

This function returns an object with the name, logo, url, and summary found in a h-app item.

Note: The h-app item is not widely supported. As a result, you might want to add a fallback in the case that the h-app function does not find any values to return.

Get a profile response object

If you want your IndieAuth object to return profile information when the “profile” scope is requested, the get_profile() function may come in handy.

This function takes a URL and retrieves the name, photo, url, and email properties found on the h-card of the specified page. If a h-card is not provided, empty strings are returned.

Usage information for this function is below.

indieweb_utils.get_profile(me: str, html: str = '', soup: BeautifulSoup | None = None) Profile[source]

Return the profile information for the given me URL.

Parameters:

me (str) – The me URL to get the profile information for.

Returns:

The profile information.

Return type:

Profile

Get a profile from a url.

Parameters:

me (str) – The url to get the profile from.

Returns:

The profile.

Return type:

Profile

Example:

import indieweb_utils

me = "https://jamesg.blog"

profile = indieweb_utils.get_profile(me)

assert profile.email == "james@jamesg.blog"
assert profile.name == "James"
assert profile.photo == "https://jamesg.blog/me.jpg"
assert profile.url == "https://jamesg.blog
Raises:

ProfileError – Profile could not be retrieved.

IndieAuth Endpoint Scaffolding

indieweb-utils includes a indieauth.server module with scaffolding to help you build your own IndieAuth endpoints.

Generate an authentication token

The generate_auth_token() function validates that an authentication request contains all required values. Then, this function generates a JWT-encoded token with the following pieces of information:

  • me

  • code_verifier

  • expires

  • client_id

  • redirect_uri

  • scope

  • code_challenge

  • code_challenge_method

You can later refer to these values during the stage where you decode a token.

Here is the syntax for this function:

indieweb_utils.indieauth.server.generate_auth_token(me: str, client_id: str, redirect_uri: str, response_type: str, state: str, code_challenge_method: str, final_scope: str, secret_key: str, **kwargs) AuthTokenResponse[source]

Generates an IndieAuth authorization token.

Parameters:
  • me (str) – The URL of the user’s profile.

  • client_id (str) – The client ID of the authorization request.

  • redirect_uri (str) – The redirect URI of the authorization request.

  • response_type (str) – The response type of the authorization request.

  • state (str) – The state of the authorization request.

  • code_challenge_method (str) – The code challenge method, used for PKCE.

  • final_scope (str) – The scopes approved by the user.

  • secret_key (str) – The secret key used to sign the token.

  • kwargs (dict) – Additional parameters to include in the token.

Returns:

The authorization token.

Return type:

str

Example:

import indieweb_utils
import random
import string
token = indieweb_utils.indieauth.server.generate_auth_token(
    me="https://test.example.com/user",
    client_id="https://example.com",
    redirect_uri="https://example.com/callback",
    response_type="code",
    state="".join(random.choice(string.ascii_letters) for _ in range(32)),
    code_challenge_method="S256",
    final_scope="read write",
    secret_key="secret"
)
Raises:

AuthenticationError – Authentication request is invalid.

This function returns both the code you should send to the client in the authentication redirect response as well as the code_verifier used in the token. This code_verifier should be saved, perhaps in session storage, for later use in checking the validity of a token redemption request.

Validate an authorization response

The _validate_indieauth_response function contains five checks. These five checks validate an authorization response according to the IndieAuth specification.

  1. Ensures the grant_type provided is authorization_code.

  2. Validates that a code, client_id, and redirect_uri are provided.

  3. Checks that the code challenge method provided is allowed.

  4. Verifies the length of the code challenge is within the range of 43 and 128 characters.

You should use this function to ensure a POST request to an authorization endpoint to redeem an authorization code (per 5.3 Redeeming the Authorization Code in the IndieAuth spec) is valid.

Here is the syntax for this function:

indieweb_utils.validate_authorization_response(grant_type: str, code: str, client_id: str, redirect_uri: str, code_challenge: str, code_challenge_method: str, allowed_methods: list = ['S256']) None[source]

Conducts checks to validate the response from an IndieAuth authorization endpoint.

Parameters:
  • grant_type (str) – The grant type of the authorization request.

  • code (str) – The code returned from the authorization request.

  • client_id (str) – The client ID of the authorization request.

  • redirect_uri (str) – The redirect URI of the authorization request.

  • code_challenge (str) – The code challenge, used for PKCE.

  • code_challenge_method (str) – The code challenge method, used for PKCE.

  • allowed_methods (list) – The list of allowed code challenge methods (default: [“S256”]).

Returns:

A boolean indicating whether the response is valid.

Return type:

bool

Example:

import indieweb_utils

indieweb_utils.validate_authorization_response(
    grant_type="authorization_code",
    code="12345",
    client_id="https://example.com",
    redirect_uri="https://example.com/callback",
    code_challenge="12345",
    code_challenge_method="S256",
    allowed_methods=["S256"]
)
Raises:

TokenValidationError – If the response is invalid.

This function does not return a value if an authorization response is valid. If a response is invalid, an exception will be raised with a relevant error message.

Redeem an IndieAuth code at a token endpoint

You can redeem an IndieAuth authorization code for an access token if needed. This is a common need for Micropub and Microsub clients.

The redeem_code() function validates all the required parameters are provided in a request. Then, this function decodes the provided code using the code, secret key, and algorithm specified. If the code is invalid or the code challenge in the decoded code is invalid, AuthenticationErrors will be raised. The function will also verify that:

  1. An authorization code has not expired.

  2. The specified redirect URI matches the decoded redirect URI.

  3. The specified client ID matches the decoded client ID.

Finally, this function encodes an access token that you can return in response to a request to create a token for a token endpoint.

Here is the syntax for this function:

indieweb_utils.redeem_code(grant_type: str, code: str, client_id: str, redirect_uri: str, code_verifier: str, secret_key: str, algorithms: list = ['HS256'], **kwargs) TokenEndpointResponse[source]

Redeems an IndieAuth code for an access token.

Parameters:
  • grant_type (str) – The grant type of the authorization request.

  • code (str) – The code returned from the authorization request.

  • client_id (str) – The client ID of the authorization request.

  • redirect_uri (str) – The redirect URI of the authorization request.

  • code_verifier (str) – The code verifier, used for PKCE.

  • secret_key (str) – The secret key used to sign the token.

  • algorithms (list) – The list of algorithms to use for signing the token (default: [“HS256”]).

  • kwargs (dict) – Additional parameters to include in the token.

Returns:

A token endpoint response object.

Return type:

TokenEndpointResponse

Example:

import indieweb_utils

token_response = indieweb_utils.indieauth.server.redeem_code(
    grant_type="authorization_code",
    code="code",
    client_id="https://example.com",
    redirect_uri="https://example.com/callback",
    code_verifier="code_verifier",
    secret_key="secret"
)

print(token_response.access_token)
print(token_response.token_type)
print(token_response.scope)
print(token_response.me)
Raises:
  • AuthorizationCodeExpiredError – If the authorization code has expired.

  • TokenValidationError – If the decoded code is invalid.

  • AuthenticationError – If the token request is invalid.

Validate an access token created by a token endpoint

A server may ask your token endpoint to validate that a provided token is in fact valid. This may be done by a Micropub client to ensure a token is still active, for example.

You can validate an access token using the validate_access_token() function.

This function decodes an authorization code using the specified secret key and algorithm(s). Then, the function will check that the authorization code has not yet expired.

If the code can be decoded, the me, client_id, and scope values will be returned.

Here is the syntax for the function:

indieweb_utils.validate_access_token(authorization_code: str, secret_key: str, algorithms: list = ['HS256']) DecodedAuthToken[source]

Validates an access token provided by a token endpoint.

Parameters:
  • authorization_code (str) – The authorization code returned from the authorization request.

  • secret_key (str) – The secret key used to sign the token.

  • algorithms (list) – The algorithms used to sign the token (default: [“HS256”]).

Returns:

An object with the me, client_id, and scope values from the access token.

Return type:

DecodedAuthToken

Example:

import indieweb_utils

try:
    decoded_token = indieweb_utils.indieauth.server.validate_access_token(
        authorization_code="code",
        secret_key="secret"
    )

    print(decoded_token.me)
    print(decoded_token.client_id)
    print(decoded_token.scope)
    print(decoded_token.decoded_authorization_code)
except indieweb_utils.AuthenticationError as e:
    print(e)
except indieweb_utils.AuthorizationCodeExpiredError as e:
    print(e)
Raises:
  • AuthorizationCodeExpiredError – Authorization code has expired.

  • AuthenticationError – Authorization code provided is invalid.

Determine if a user is authenticated

To check if a user is authenticated in a Flask application, use the following function:

indieweb_utils.is_authenticated(token_endpoint: str, headers: dict, session: dict, approved_user: bool | None = None) bool[source]

Check if a user has provided a valid Authorization header or access token in session. Designed for use with Flask.

Parameters:
  • token_endpoint – The token endpoint of the user’s IndieAuth server.

  • headers – The headers sent by a request.

  • session – The session object from a Flask application.

  • approved_user – The optional URL of the that is approved to use the API.

Returns:

True if the user is authenticated, False otherwise.

Return type:

bool

Example:

import indieweb_utils
from Flask import flask, request

app = Flask(__name__)

@app.route("/")
def index():
    user_is_authenticated = indieweb_utils.is_authenticated(
        "https://tokens.indieauth.com/token",
        request.headers,
        session,
        "https://example.com/",
    )

    if user_is_authenticated is False:
        return "Not authenticated"

    return "Authenticated"
Raises:

AuthenticationError – The token endpoint could not be accessed.

This function checks if an authorization token is provided in a header or user storage. If a token is provided, that token is verified with the specified token endpoint.

A True value is returned if a user has provided a token and that token is valid. A False value is returned if a user has not provided a token or if the token is invalid.

Handle an IndieAuth callback request

The last stage of the IndieAuth authentication flow for a client is to verify a callback response and exchange the provided code with a token.

This function implements a callback handler to verify the response frmo an authorization server and redeem a token.

To use this function, you need to pass in the following arguments:

indieweb_utils.indieauth_callback_handler(*, code: str, state: str, token_endpoint: str, code_verifier: str, session_state: str, me: str, callback_url: str, client_id: str, required_scopes: List[str]) IndieAuthCallbackResponse[source]

Exchange a callback ‘code’ for an authentication token.

Parameters:
  • code (str) – The callback ‘code’ to exchange for an authentication token.

  • state (str) – The state provided by the authentication server in the callback response.

  • token_endpoint (str) – The token endpoint to use for exchanging the callback ‘code’ for an authentication token.

  • code_verifier (str) – The code verifier to use for exchanging the callback ‘code’ for an authentication token.

  • session_state (str) – The state stored in session used to verify the callback state is valid.

  • me (str) – The URL of the user’s profile.

  • callback_url (str) – The callback URL used in the original authentication request.

  • client_id (str) – The client ID used in the original authentication request.

  • required_scopes (list[str]) – The scopes required for the application to work. This list should not include optional scopes.

Returns:

A message indicating the result of the callback (success or failure) and the token endpoint response. The endpoint response will be equal to None if the callback failed.

Return type:

tuple[str, dict]

Example:

import indieweb_utils
from Flask import flask

app = Flask(__name__)

@app.route("/indieauth/callback")
def callback():
    response = indieweb_utils.indieauth_callback_handler(
        code=request.args.get("code"),
        state=request.args.get("state"),
        token_endpoint="https://tokens.indieauth.com/token",
        code_verifier=session["code_verifier"],
        session_state=session["state"],
        me=session["me"],
        callback_url=session["callback_url"],
        client_id=session["client_id"],
        required_scopes=["create", "update", "delete"],
    )

    return response.message
Raises:

AuthenticationError – The token endpoint could not be accessed or authentication failed.

This function verifies that an authorization server has returned a valid response and redeems a token.

You can leave the “me” value equal to None if any URL should be able to access your service.

Otherwise, set “me” to the URL of the profile that should be able to access your service.

Setting a me value other than None may be useful if you are building personal services that nobody else should be able to access.

If successful, this function returns an IndieAuthCallbackResponse object that contains:

indieweb_utils.IndieAuthCallbackResponse(message: str, response: dict)[source]

This class contains a response value. This value is equal to the JSON response sent by the IndieAuth web server.

An example endpoint response looks like this:

{
    "me": "https://jamesg.blog/",
    "access_token": "ACCESS_TOKEN",
    "scope": "SCOPE_LIST"
}

This function does not check whether a URL has an OAuth provider. Your application should check the list of valid rel me links and only use those that integrate with the OAuth providers your RelMeAuth service supports. For example, if your service does not support Twitter, you should not present Twitter as a valid authentication option to a user, even if the get_valid_relmeauth_links() function found a valid two-way rel=me link.