Authl


NameAuthl JSON
Version 0.7.1 PyPI version JSON
download
home_pagehttps://plaidweb.site/
SummaryFramework-agnostic authentication wrapper
upload_time2024-12-28 03:29:51
maintainerNone
docs_urlNone
authorfluffy
requires_python<4.0,>=3.9
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # Authl
A Python library for managing federated identity

[![Documentation Status](https://readthedocs.org/projects/authl/badge/?version=latest)](https://authl.readthedocs.io/en/latest/?badge=latest)

## About

Authl is intended to make it easy to add federated identity to Python-based web
apps without requiring the creation of site-specific user accounts, but also
without requiring the user to choose from a myriad of buttons or links to select
any specific login provider.

All it should take is a single login form that asks for how the user wants to be
identified.

## Current state

The basic API works, and provides an easy drop-in set of endpoints for
[Flask](http://flask.pocoo.org).

Currently supported authentication mechanisms:

* Directly authenticating against email using a magic link
* Federated authentication against Fediverse providers
    ([Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social))
* Federated authentication against [IndieAuth](https://indieauth.net/)
* Silo authentication against [Twitter](https://twitter.com/)
* Test/loopback authentication for development purposes

Planned functionality:

* Pluggable OAuth mechanism to easily support additional identity providers such as:
    * OpenID Connect (Google et al)
    * Facebook
    * GitHub
* OpenID 1.x (Wordpress, LiveJournal, Dreamwidth, etc.)
* A more flexible configuration system

## Rationale

Identity is hard, and there are so many competing standards which try to be the
be-all end-all Single Solution. OAuth and OpenID Connect want lock-in to silos,
IndieAuth wants every user to self-host their own identity site, and OpenID 1.x
has fallen by the wayside. Meanwhile, users just want to be able to log in with
the social media they're already using (siloed or not).

Any solution which requires all users to have a certain minimum level of
technical ability is not a workable solution.

All of these solutions are prone to the so-called "[NASCAR
problem](https://indieweb.org/NASCAR_problem)" where every supported login
provider needs its own UI. But being able to experiment with a more unified UX
might help to fix some of that.

## Documentation

Full API documentation is hosted on [readthedocs](https://authl.readthedocs.io).

## Usage

Basic usage is as follows:

1. Create an Authl object with your configured handlers

    This can be done by instancing individual handlers yourself, or you can use
    `authl.from_config`

2. Make endpoints for initiation and progress callbacks

    The initiation callback receives an identity string (email address/URL/etc.)
    from the user, queries Authl for the handler and its ID, and builds a
    callback URL for that handler to use. Typically you'll have a single
    callback endpoint that includes the handler's ID as part of the URL scheme.

    The callback endpoint needs to be able to receive a `GET` or `POST` request
    and use that to validate the returned data from the authorization handler.

    Your callback endpoint (and generated URL thereof) should also include
    whatever intended forwarding destination.

3. Handle the `authl.disposition` object types accordingly

    A `disposition` is what should be done with the agent that initiated the
    endpoint call. Currently there are the following:

    * `Redirect`: return an HTTP redirection to forward it along to another URL
    * `Notify`: return a notification to the user that they must take another
      action (e.g. check their email)
    * `Verified`: indicates that the user has been verified; set a session
      cookie (or whatever) and forward them along to their intended destination
    * `Error`: An error occurred; return it to the user as appropriate

## Flask usage

To make life easier with Flask, Authl provides an `authl.flask.AuthlFlask`
wrapper. You can use it from a Flask app with something like the below:

```python
import uuid
import logging

import flask
import authl.flask

logging.basicConfig(level=logging.INFO)
LOGGER = logging.getLogger(__name__)

app = flask.Flask('authl-test')

app.secret_key = str(uuid.uuid4())
authl = authl.flask.AuthlFlask(
    app,
    {
        'SMTP_HOST': 'localhost',
        'SMTP_PORT': 25,
        'EMAIL_FROM': 'authl@example.com',
        'EMAIL_SUBJECT': 'Login attempt for Authl test',
        'INDIELOGIN_CLIENT_ID': authl.flask.client_id,
        'TEST_ENABLED': True,
        'MASTODON_NAME': 'authl testing',
        'MASTODON_HOMEPAGE': 'https://github.com/PlaidWeb/Authl'
    },
    tester_path='/check_url'
)


@app.route('/')
@app.route('/some-page')
def index():
    """ Just displays a very basic login form """
    LOGGER.info("Session: %s", flask.session)
    LOGGER.info("Request path: %s", flask.request.path)

    if 'me' in flask.session:
        return 'Hello {me}. Want to <a href="{logout}">log out</a>?'.format(
            me=flask.session['me'], logout=flask.url_for(
                'logout', redir=flask.request.path[1:])
        )

    return 'You are not logged in. Want to <a href="{login}">log in</a>?'.format(
        login=flask.url_for('authl.login', redir=flask.request.path[1:]))


@app.route('/logout/')
@app.route('/logout/<path:redir>')
def logout(redir=''):
    """ Log out from the thing """
    LOGGER.info("Logging out")
    LOGGER.info("Redir: %s", redir)
    LOGGER.info("Request path: %s", flask.request.path)

    flask.session.clear()
    return flask.redirect('/' + redir)
```

This will configure the Flask app to allow IndieLogin, Mastodon, and email-based
authentication (using the server's local sendmail), and use the default login
endpoint of `/login/`. The `index()` endpoint handler always redirects logins
and logouts back to the same page when you log in or log out (the `[1:]` is to
trim off the initial `/` from the path). The logout handler simply clears the
session and redirects back to the redirection path.

The above configuration uses Flask's default session lifetime of one month (this
can be configured by setting `app.permanent_session_lifetime` to a `timedelta`
object, e.g. `app.permanent_session_lifetime = datetime.timedelta(hours=20)`).
Sessions will also implicitly expire whenever the application server is
restarted, as `app.secret_key` is generated randomly at every startup.

### Accessing the default stylesheet

If you would like to access `authl.flask`'s default stylesheet, you can do it by
passing the argument `asset='css'` to the login endpoint. For example, if you
are using the default endpoint name of `authl.login`, you can use:

```python
flask.url_for('authl.login', asset='css')
```

from Python, or e.g.

```html
<link rel="stylesheet" href="{{url_for('authl.login', asset='css')}}">
```

from a Jinja template.

            

Raw data

            {
    "_id": null,
    "home_page": "https://plaidweb.site/",
    "name": "Authl",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "fluffy",
    "author_email": "fluffy@beesbuzz.biz",
    "download_url": "https://files.pythonhosted.org/packages/80/19/a7b9b6105d2d1357ed51e89468e99d1fd20d992844db83c1778395eaa3bb/authl-0.7.1.tar.gz",
    "platform": null,
    "description": "# Authl\nA Python library for managing federated identity\n\n[![Documentation Status](https://readthedocs.org/projects/authl/badge/?version=latest)](https://authl.readthedocs.io/en/latest/?badge=latest)\n\n## About\n\nAuthl is intended to make it easy to add federated identity to Python-based web\napps without requiring the creation of site-specific user accounts, but also\nwithout requiring the user to choose from a myriad of buttons or links to select\nany specific login provider.\n\nAll it should take is a single login form that asks for how the user wants to be\nidentified.\n\n## Current state\n\nThe basic API works, and provides an easy drop-in set of endpoints for\n[Flask](http://flask.pocoo.org).\n\nCurrently supported authentication mechanisms:\n\n* Directly authenticating against email using a magic link\n* Federated authentication against Fediverse providers\n    ([Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social))\n* Federated authentication against [IndieAuth](https://indieauth.net/)\n* Silo authentication against [Twitter](https://twitter.com/)\n* Test/loopback authentication for development purposes\n\nPlanned functionality:\n\n* Pluggable OAuth mechanism to easily support additional identity providers such as:\n    * OpenID Connect (Google et al)\n    * Facebook\n    * GitHub\n* OpenID 1.x (Wordpress, LiveJournal, Dreamwidth, etc.)\n* A more flexible configuration system\n\n## Rationale\n\nIdentity is hard, and there are so many competing standards which try to be the\nbe-all end-all Single Solution. OAuth and OpenID Connect want lock-in to silos,\nIndieAuth wants every user to self-host their own identity site, and OpenID 1.x\nhas fallen by the wayside. Meanwhile, users just want to be able to log in with\nthe social media they're already using (siloed or not).\n\nAny solution which requires all users to have a certain minimum level of\ntechnical ability is not a workable solution.\n\nAll of these solutions are prone to the so-called \"[NASCAR\nproblem](https://indieweb.org/NASCAR_problem)\" where every supported login\nprovider needs its own UI. But being able to experiment with a more unified UX\nmight help to fix some of that.\n\n## Documentation\n\nFull API documentation is hosted on [readthedocs](https://authl.readthedocs.io).\n\n## Usage\n\nBasic usage is as follows:\n\n1. Create an Authl object with your configured handlers\n\n    This can be done by instancing individual handlers yourself, or you can use\n    `authl.from_config`\n\n2. Make endpoints for initiation and progress callbacks\n\n    The initiation callback receives an identity string (email address/URL/etc.)\n    from the user, queries Authl for the handler and its ID, and builds a\n    callback URL for that handler to use. Typically you'll have a single\n    callback endpoint that includes the handler's ID as part of the URL scheme.\n\n    The callback endpoint needs to be able to receive a `GET` or `POST` request\n    and use that to validate the returned data from the authorization handler.\n\n    Your callback endpoint (and generated URL thereof) should also include\n    whatever intended forwarding destination.\n\n3. Handle the `authl.disposition` object types accordingly\n\n    A `disposition` is what should be done with the agent that initiated the\n    endpoint call. Currently there are the following:\n\n    * `Redirect`: return an HTTP redirection to forward it along to another URL\n    * `Notify`: return a notification to the user that they must take another\n      action (e.g. check their email)\n    * `Verified`: indicates that the user has been verified; set a session\n      cookie (or whatever) and forward them along to their intended destination\n    * `Error`: An error occurred; return it to the user as appropriate\n\n## Flask usage\n\nTo make life easier with Flask, Authl provides an `authl.flask.AuthlFlask`\nwrapper. You can use it from a Flask app with something like the below:\n\n```python\nimport uuid\nimport logging\n\nimport flask\nimport authl.flask\n\nlogging.basicConfig(level=logging.INFO)\nLOGGER = logging.getLogger(__name__)\n\napp = flask.Flask('authl-test')\n\napp.secret_key = str(uuid.uuid4())\nauthl = authl.flask.AuthlFlask(\n    app,\n    {\n        'SMTP_HOST': 'localhost',\n        'SMTP_PORT': 25,\n        'EMAIL_FROM': 'authl@example.com',\n        'EMAIL_SUBJECT': 'Login attempt for Authl test',\n        'INDIELOGIN_CLIENT_ID': authl.flask.client_id,\n        'TEST_ENABLED': True,\n        'MASTODON_NAME': 'authl testing',\n        'MASTODON_HOMEPAGE': 'https://github.com/PlaidWeb/Authl'\n    },\n    tester_path='/check_url'\n)\n\n\n@app.route('/')\n@app.route('/some-page')\ndef index():\n    \"\"\" Just displays a very basic login form \"\"\"\n    LOGGER.info(\"Session: %s\", flask.session)\n    LOGGER.info(\"Request path: %s\", flask.request.path)\n\n    if 'me' in flask.session:\n        return 'Hello {me}. Want to <a href=\"{logout}\">log out</a>?'.format(\n            me=flask.session['me'], logout=flask.url_for(\n                'logout', redir=flask.request.path[1:])\n        )\n\n    return 'You are not logged in. Want to <a href=\"{login}\">log in</a>?'.format(\n        login=flask.url_for('authl.login', redir=flask.request.path[1:]))\n\n\n@app.route('/logout/')\n@app.route('/logout/<path:redir>')\ndef logout(redir=''):\n    \"\"\" Log out from the thing \"\"\"\n    LOGGER.info(\"Logging out\")\n    LOGGER.info(\"Redir: %s\", redir)\n    LOGGER.info(\"Request path: %s\", flask.request.path)\n\n    flask.session.clear()\n    return flask.redirect('/' + redir)\n```\n\nThis will configure the Flask app to allow IndieLogin, Mastodon, and email-based\nauthentication (using the server's local sendmail), and use the default login\nendpoint of `/login/`. The `index()` endpoint handler always redirects logins\nand logouts back to the same page when you log in or log out (the `[1:]` is to\ntrim off the initial `/` from the path). The logout handler simply clears the\nsession and redirects back to the redirection path.\n\nThe above configuration uses Flask's default session lifetime of one month (this\ncan be configured by setting `app.permanent_session_lifetime` to a `timedelta`\nobject, e.g. `app.permanent_session_lifetime = datetime.timedelta(hours=20)`).\nSessions will also implicitly expire whenever the application server is\nrestarted, as `app.secret_key` is generated randomly at every startup.\n\n### Accessing the default stylesheet\n\nIf you would like to access `authl.flask`'s default stylesheet, you can do it by\npassing the argument `asset='css'` to the login endpoint. For example, if you\nare using the default endpoint name of `authl.login`, you can use:\n\n```python\nflask.url_for('authl.login', asset='css')\n```\n\nfrom Python, or e.g.\n\n```html\n<link rel=\"stylesheet\" href=\"{{url_for('authl.login', asset='css')}}\">\n```\n\nfrom a Jinja template.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Framework-agnostic authentication wrapper",
    "version": "0.7.1",
    "project_urls": {
        "Documentation": "https://authl.readthedocs.io/",
        "Homepage": "https://plaidweb.site/",
        "Repository": "https://github.com/PlaidWeb/Authl"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5b00facc8385c8788f9c7780e25eef3886539d4b3aec3c4e2a335123e5cf80a7",
                "md5": "10c98c3a7693e0cb16650a90a9e18e1c",
                "sha256": "36b435297ebd5b84a498aed510b5a151997fec8e66d5e084bc53789e5c688ea9"
            },
            "downloads": -1,
            "filename": "authl-0.7.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "10c98c3a7693e0cb16650a90a9e18e1c",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.9",
            "size": 41443,
            "upload_time": "2024-12-28T03:29:50",
            "upload_time_iso_8601": "2024-12-28T03:29:50.032269Z",
            "url": "https://files.pythonhosted.org/packages/5b/00/facc8385c8788f9c7780e25eef3886539d4b3aec3c4e2a335123e5cf80a7/authl-0.7.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8019a7b9b6105d2d1357ed51e89468e99d1fd20d992844db83c1778395eaa3bb",
                "md5": "e7fa294a46973258b363f8829426ecf8",
                "sha256": "dfeaa07b3104e2ef51faeea3ff80b42558fa581cb96ada8940caf83e1adda62d"
            },
            "downloads": -1,
            "filename": "authl-0.7.1.tar.gz",
            "has_sig": false,
            "md5_digest": "e7fa294a46973258b363f8829426ecf8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.9",
            "size": 36551,
            "upload_time": "2024-12-28T03:29:51",
            "upload_time_iso_8601": "2024-12-28T03:29:51.162585Z",
            "url": "https://files.pythonhosted.org/packages/80/19/a7b9b6105d2d1357ed51e89468e99d1fd20d992844db83c1778395eaa3bb/authl-0.7.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-28 03:29:51",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "PlaidWeb",
    "github_project": "Authl",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": false,
    "lcname": "authl"
}
        
Elapsed time: 0.62232s