Authentication handlers¶
Base handler class¶
The Handler
class defines the abstract interface for an
authentication handler. Handlers are registered to an authl.Authl
instance which then selects the handler based on the provided identity.
The basic flow for how a handler is selected is:
The
authl.Authl
instance checks to see if any handler knows how to handle the identity URL directly; if so, it returns the first match.The instance retrieves the URL, and hands the parse tree and response headers off to each handler to see if it’s able to handle the URL based on that; if so, it returns the first match.
In the case of a Webfinger address (e.g. @user@example.com
) it repeats this
process for every profile URL provided by the Webfinger response until it finds
a match.
- class authl.handlers.Handler¶
Base class for authentication handlers
- abstractmethod check_callback(url: str, get: dict, data: dict) Disposition ¶
Checks the authorization callback sent by the external provider.
- Parameters:
url (str) – the full URL of the verification request
get (dict) – the GET parameters for the verification
data (dict) – the POST parameters for the verification
- Returns:
a
authl.disposition
object to be handled by the frontend. Any errors which get raised internally should be caught and returned as an appropriateauthl.disposition.Error
.
- handles_page(url: str, headers, content, links) bool ¶
Returns
True
/truthy if we can handle the page based on page content- Parameters:
url (str) – the canonicized identity URL
headers (dict) – the raw headers from the page request, as a MultiDict (as provided by the Requests library)
content (bs4.BeautifulSoup) – the page content, as a BeautifulSoup4 parse tree
links (dict) – the results of parsing the Link: headers, as a dict of rel -> dict of ‘url’ and ‘rel’, as provided by the Requests library
- handles_url(url: str) str | None ¶
If this handler can handle this URL (or something that looks like it), return something truthy, e.g. a canonicized version of the URL. Otherwise, return None.
It is okay to check for an API endpoint (relative to the URL) in implementing this. However, if the content kept at the URL itself needs to be parsed to make the determination, implement that in
handles_page()
instead.Whatever value this returns will be passed back in to initiate_auth, so if that value matters, return a reasonable URL.
- abstractmethod initiate_auth(id_url: str, callback_uri: str, redir: str) Disposition ¶
Initiates the authentication flow.
- Parameters:
id_url (str) – Canonicized identity URL
callback_uri (str) – Callback URL for verification
redir (str) – Where to redirect the user to after verification
- Returns:
the
authl.disposition
to be handled by the frontend.
- abstract property cb_id: str¶
The callback ID for callback registration. Must be unique across all registered handlers, and should be short and stable.
- abstract property description: str¶
A description of the service, in HTML format.
- property generic_url: str | None¶
A generic URL that can be used with this handler irrespective of identity.
- property logo_html: str | None¶
A list of tuples of (html,label) for the login buttons.
The HTML should be an isolated
<svg>
element, or an<img src>
pointing to a publicly-usablehttps:
URL.
- abstract property service_name: str¶
The human-readable service name
- abstract property url_schemes: List[Tuple[str, str]]¶
A list of supported URL schemes for the login UI to fill in with a placeholder.
The list is of tuples of
(format, default_placeholder)
, where the format string contains a'%'
indicating where the placeholder goes.
Email handler¶
This handler emails a “magic link” to the user so that they can log in that way. It requires an SMTP server of some sort; see your hosting provider’s documentation for the appropriate configuration. This should also be able to work with your regular email provider.
See authl.from_config()
for the simplest configuration mechanism.
This handler registers itself with a cb_id
of "e"
.
- class authl.handlers.email_addr.EmailAddress(sendmail, notify_cdata, token_store: TokenStore, *, expires_time: int | None = None, pending_storage: dict | None = None, email_template_text: str = "Hello! Someone, possibly you, asked to log in using this email address. If this\nwas you, please visit the following link within the next {minutes} minutes:\n\n {url}\n\nIf this wasn't you, you can safely disregard this message.\n\n")¶
Authenticate using a “magic link” sent via email.
- Parameters:
sendmail – A function that, given an
email.message
object, sends it. It is the responsibility of this function to set the From and Subject headers before it sends.notify_cdata – the callback data to provide to the user for the next step instructions
token_store (tokens.TokenStore) – Storage for the identity tokens
expires_time (int) – how long the email link should be valid for, in seconds (default: 900)
pending_storage (dict) – Storage to keep track of pending email addresses, for DDOS/abuse mitigation. Defaults to an
ExpiringDict
that expires afterexpires_time
email_template_text (str) – the plaintext template for the sent email, provided as a template string
Email templates are formatted with the following parameters:
{url}
: the URL that the user should visit to complete login{minutes}
: how long the URL is valid for, in minutes
- handles_url(url)¶
Accepts any email address formatted as
user@example.com
ormailto:user@example.com
. The actual address is validated usingvalidate_email
.
- authl.handlers.email_addr.from_config(config, token_store: TokenStore)¶
Generate an
EmailAddress
handler from the provided configuration dictionary.- Parameters:
config (dict) –
The configuration settings for the handler. Relevant keys:
EMAIL_SENDMAIL
: a function to call to send the email; if omitted, generates one usingauthl.handlers.email_addr.simple_sendmail()
configured with:EMAIL_FROM
: theFrom:
address to use when sending an emailEMAIL_SUBJECT
: theSubject:
to use for a login emailSMTP_HOST
: the outgoing SMTP hostSMTP_PORT
: the outgoing SMTP portSMTP_USE_SSL
: whether to use SSL for the SMTP connection (defaults toFalse
). It is highly recommended to set this to True if yourSMTP_HOST
is anything other thanlocalhost
.SMTP_USERNAME
: the username to use with the SMTP serverSMTP_PASSWORD
: the password to use with the SMTP server
EMAIL_CHECK_MESSAGE
: Theauthl.disposition.Notify
client data. Defaults to a simple string-based message.EMAIL_TEMPLATE_FILE
: A path to a text file for the email message; if not specified a default template will be used. This file must use an UTF-8 encoding.EMAIL_EXPIRE_TIME
: How long a login email is valid for, in seconds (defaults to theEmailAddress
default value)
token_store (tokens.TokenStore) – the authentication token storage mechanism; see
authl.tokens
for more information.
- authl.handlers.email_addr.simple_sendmail(connector, sender_address, subject)¶
A simple SMTP sendmail handler.
- Parameters:
connector (function) – A factory-type function that returns an
smtplib.SMTP
-compatible object in the connected state. Useauthl.handlers.email_addr.smtplib_connector()
for an easy-to-use general-purpose connector.sender_address (str) – The email address to use for the sender
subject (str) – the subject line to attach to the message
:returns” a function that, when called with an
email.message.EmailMessage
, sets the From and Subject lines and sends the message via the provided connector.
- authl.handlers.email_addr.smtplib_connector(hostname, port, username=None, password=None, use_ssl=False)¶
A utility class that generates an SMTP connection factory.
- Parameters:
hostname (str) – The SMTP server’s hostname
port (int) – The SMTP server’s connection port
username (str) – The SMTP server username
password (str) – The SMTP server port
use_ssl (bool) – Whether to use SSL
Fediverse handler¶
This handler allows login via Fediverse instances; currently Mastodon and Pleroma are supported, as is anything else with basic support for the Mastodon client API.
See authl.from_config()
for the simplest configuration mechanism.
This handler registers itself with a cb_id
of "fv"
.
- class authl.handlers.fediverse.Fediverse(name: str, token_store: TokenStore, timeout: int | None = None, homepage: str | None = None)¶
Handler for Fediverse services (Mastodon, Pleroma)
Instantiate a Fediverse handler.
- Parameters:
name (str) – Human-readable website name
homepage (str) – Homepage for the website
token_store – Storage for session tokens
timeout (int) – How long to allow a user to wait to log in, in seconds
- handles_url(url)¶
Checks for an
/api/v1/instance
endpoint to determine if this is a Mastodon-compatible instance
- authl.handlers.fediverse.from_config(config, token_store: TokenStore)¶
Generate a Fediverse handler from the given config dictionary.
- Parameters:
config (dict) –
Configuration values; relevant keys:
FEDIVERSE_NAME
: the name of your website (required)FEDIVERSE_HOMEPAGE
: your website’s homepage (recommended)FEDIVERSE_TIMEOUT
: the maximum time to wait for login to complete
token_store (tokens.TokenStore) – The authentication token storage
IndieAuth handler¶
This handler allows people to log in from their own websites using the
IndieAuth federated protocol. See
authl.from_config()
for the simplest configuration mechanism.
Note that the client ID must match the domain name of your website. If you’re
using this with authl.flask
, there is a function,
authl.flask.client_id
, which provides this at runtime with no
configuration necessary. For other frameworks you will need to either configure
this with your public-facing domain name, or retrieve the domain name from
whatever framework you’re using. Please note also that the scheme (http
vs.
https
) must match.
This handler registers itself with a cb_id
of "ia"
.
- class authl.handlers.indieauth.IndieAuth(client_id: str | Callable[[...], str], token_store: TokenStore, timeout: int | None = None)¶
Supports login via IndieAuth.
- Parameters:
client_id – The client_id to send to the remote IndieAuth provider. Can be a string or a function that returns a string.
token_store –
Storage for the tokens.
*Security note:*
tokens.Serializer
is not supported, as it makes this handler subject to replay attacks when used with many common IndieAuth servers, and also prevents PKCE from being effective.timeout (int) – Maximum time to wait for login to complete (default: 600)
- handles_page(url, headers, content, links)¶
- Returns:
whether an
authorization_endpoint
was found on the page.
- handles_url(url)¶
If this page is already known to have an IndieAuth endpoint, we reuse that; otherwise this returns
None
so the Authl instance falls through toauthl.handlers.indieauth.IndieAuth.handles_page()
.
- authl.handlers.indieauth.find_endpoint(id_url: str, links: Dict | None = None, content: BeautifulSoup | None = None, rel: str = 'authorization_endpoint') Tuple[str | None, str] ¶
Given an identity URL, get its IndieAuth endpoint
- Parameters:
id_url (str) – an identity URL to check
links – a request.links object from a requests operation
content (BeautifulSoup) – a BeautifulSoup parse tree of an HTML document
rel (str) – the endpoint rel to retrieve
- Returns:
a tuple of
(endpoint_url, profile_url)
- authl.handlers.indieauth.find_endpoints(id_url: str, links: Dict | None = None, content: BeautifulSoup | None = None) Tuple[Dict[str, str], str] ¶
Given an identity URL, discover its IndieWeb endpoints
- Parameters:
id_url (str) – an identity URL to check
links – a request.links object from a requests operation
content (BeautifulSoup) – a BeautifulSoup parse tree of an HTML document
- Returns:
a tuple of
({endpoint_name:endpoint_url}, profile_url)
- authl.handlers.indieauth.from_config(config, token_store)¶
Generate an IndieAuth handler from the given config dictionary.
Possible configuration values:
INDIEAUTH_CLIENT_ID
: the client ID (URL) of your website (required)INDIEAUTH_PENDING_TTL
: timemout for a pending transction
- authl.handlers.indieauth.get_profile(id_url: str, server_profile: dict | None = None, links=None, content: BeautifulSoup | None = None, endpoints=None) dict ¶
Given an identity URL, try to parse out an Authl profile
- Parameters:
id_url (str) – The profile page to parse
server_profile (dict) – An IndieAuth response profile
links (dict) – Profile response’s links dictionary
content – Pre-parsed page content
endpoints (dict) – Pre-parsed page endpoints
- authl.handlers.indieauth.verify_id(request_id: str, response_id: str) str ¶
Given an ID from an identity request and its verification response, ensure that the verification response is a valid URL for the request. A response is considered valid if it declares the same authorization_endpoint.
- Parameters:
request_id (str) – The original requested identity
response_id (str) – The authorized response identity
- Returns:
the verified response ID
- Raises:
ValueError
if verification failed
Test handler¶
This is a handler which always lets people log in as any URL with the fake
scheme of test:
, with the exception of test:error
which generates an
error. This is only to be used for testing locally and should not be enabled in
production.
- class authl.handlers.test_handler.TestHandler¶
An Authl handler which always returns True for any URI beginning with ‘test:’. Primarily for testing purposes.
- handles_url(url)¶
Returns
True
if the URL starts with'test:'
.
- initiate_auth(id_url, callback_uri, redir)¶
Immediately returns a
disposition.Verified
, unless the URL is'test:error'
in which case it returns adisposition.Error
.