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:
- 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.
Retrieve Valid Links for Use in RelMeAuth
To authenticate a user with RelMeAuth, you need to validate that there is a two-way link between two resources.
IndieWeb Utils implements a helper function that checks whether the URLs linked with rel=me on a web page contain a link back to the source.
To check whether there is a two-way rel=me link between two resources, you can use this function:
- indieweb_utils.get_valid_relmeauth_links(url: str, require_rel_me_link_back: bool = True, html: str | None = None, parsed_mf2: Parser | None = None) List[str] [source]
Get the valid links on a page that point back to a rel=me URL per RelMeAuth.
refs: https://indieweb.org/RelMeAuth refs: http://microformats.org/wiki/RelMeAuth
- Url:
The url to parse.
- Require_rel_me_link_back:
Whether to require a rel=me link back to the specified URL. If this property is set to False, this function will return all sites that link back to the specified URL, even if they do not have a rel=me attribute. If this property is set to True (the default), this function will only return sites that have a rel=me link pointing back to your URL.
- Returns:
The valid relmeauth links.
- Return type:
Example:
import indieweb_utils url = "https://jamesg.blog" valid_relmeauth_links = indieweb_utils.get_valid_relmeauth_links(url) for link in valid_relmeauth_links: print(link)
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.
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:
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.
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:
An authorization code has not expired.
The specified redirect URI matches the decoded redirect URI.
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:
- 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:
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:
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:
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.