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:

  1. 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.
  2. 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

check_callback(url: str, get: dict, data: dict) → authl.disposition.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 appropriate authl.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) → Optional[str]

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.

initiate_auth(id_url: str, callback_uri: str, redir: str) → authl.disposition.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.

cb_id

The callback ID for callback registration. Must be unique across all registered handlers, and should be short and stable.

description

A description of the service, in HTML format.

generic_url

A generic URL that can be used with this handler irrespective of identity.

logo_html

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-usable https: URL.

service_name

The human-readable service name

url_schemes

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 from_config() for the simplest configuration mechanism.

class authl.handlers.email_addr.EmailAddress(sendmail, notify_cdata, token_store: authl.tokens.TokenStore, expires_time: int = None, pending_storage: dict = None, email_template_text: str = "Hello! Someone, possibly you, asked to log in using this email address. If thisnwas you, please visit the following link within the next {minutes} minutes:nn {url}nnIf this wasn't you, you can safely disregard this message.nn")

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 after expires_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 or mailto:user@example.com. The actual address is validated using validate_email.

authl.handlers.email_addr.from_config(config, token_store: authl.tokens.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 using simple_sendmail() configured with:
      • EMAIL_FROM: the From: address to use when sending an email
      • EMAIL_SUBJECT: the Subject: to use for a login email
      • SMTP_HOST: the outgoing SMTP host
      • SMTP_PORT: the outgoing SMTP port
      • SMTP_USE_SSL: whether to use SSL for the SMTP connection (defaults to False). It is highly recommended to set this to True if your SMTP_HOST is anything other than localhost.
      • SMTP_USERNAME: the username to use with the SMTP server
      • SMTP_PASSWORD: the password to use with the SMTP server
    • EMAIL_CHECK_MESSAGE: The authl.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.
    • EMAIL_EXPIRE_TIME: How long a login email is valid for, in seconds (defaults to the EmailAddress 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. Use 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 from_config() for the simplest configuration mechanism.

class authl.handlers.fediverse.Fediverse(name: str, token_store: authl.tokens.TokenStore, timeout: Optional[int] = None, homepage: Optional[str] = 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: authl.tokens.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 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.

class authl.handlers.indieauth.IndieAuth(client_id: Union[str, Callable[[...], str]], token_store: authl.tokens.TokenStore, timeout: int = None)

Supports login via IndieAuth.

SECURITY NOTE: When used with tokens.Serializer, this is subject to certain classes of replay attack; for example, if the user endpoint uses irrevocable signed tokens for the code grant (which is done in many endpoints, e.g. SelfAuth), an attacker can replay a transaction that it intercepts. As such it is very important that the timeout be configured to a reasonably short time to mitigate this.

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
  • 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 to handles_page().

authl.handlers.indieauth.find_endpoint(id_url: str, links: Dict[KT, VT] = None, content: bs4.BeautifulSoup = None, rel: str = 'authorization_endpoint') → Tuple[Optional[str], 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[KT, VT] = None, content: bs4.BeautifulSoup = 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, links=None, content: bs4.BeautifulSoup = 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

Twitter

This handler allows third-party login using Twitter. To use it you will need to register your website as an application via the Twitter developer portal and retrieve your client_key and client_secret from there. You will also need to register your website’s Twitter callback handler(s). Remember to include all URLs that the callback might be accessed from, including test domains.

It is highly recommended that you only store the client_key and client_secret in an environment variable rather than by checked-in code, as a basic security precaution against credential leaks.

See from_config() for the simplest configuration mechanism.

class authl.handlers.twitter.Twitter(client_key: str, client_secret: str, timeout: int = None, storage: dict = None)

Twitter login handler.

Parameters:
  • client_key (str) – The Twitter client_key value
  • client_secret (str) – The Twitter client_secret value
  • timeout (int) – How long, in seconds, the user has to complete the login
  • storage (dict) – A dict-like object that persistently stores the OAuth token and secret during the login transaction. This needs to persist on at least a per-user basis. It is safe to use the user session or browser cookies for this storage.
static build_profile(user_info: dict) → dict

Convert a Twitter userinfo JSON into an Authl profile

authl.handlers.twitter.from_config(config, storage)

Generate a Twitter handler from the given config dictionary.

Posible configuration values:

  • TWITTER_CLIENT_KEY: The Twitter app’s client_key
  • TWITTER_CLIENT_SECRET: The Twitter app’s client_secret
  • TWITTER_TIMEOUT: How long to wait for the user to log in

It is *HIGHLY RECOMMENDED* that the client key and secret be provided via environment variables or some other mechanism that doesn’t involve checking these values into source control and exposing them on the file system.

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 a disposition.Error.