Giter VIP home page Giter VIP logo

authl's People

Contributors

dependabot[bot] avatar fluffy-critter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

authl's Issues

Flask: throttle initiation requests per me/ip

To prevent a site from being used in an amplification attack or part of an email bomb or whatever, the login endpoint should throttle requests made based on both the me parameter and on the originating IP address (as determined by eg flask.request.headers.get(“x-forwarded-for”,flask.request.remote_addr) or whatever the correct invocation is). The timeout should probably be stored in an expiringdict with the next timeout computed by adding the delta between the current timeout and the current time multiplied by some constant, with the initial timeout and constant being configurable.

Care should be taken to not accidentally make this a vector for maliciously locking people out, though.

Abstract token storage

There should be a central store for request tokens and other things (Mastodon client tokens, email request throttles, etc), which can then be offloaded into a shared/persisted data store (memcached/redis/Postgres/whatever) to allow for load balancing and the like.

Flask: provide callback list and match endpoint

Add a callback-related endpoint _cb/check/<url>, which checks the URL and returns the pertinent info from Authl.get_handler_for_url(url), providing the name and URL schema in a JSON blob like:

{
    'name': 'Email',
    'schema': '%',
    'placeholder': '[email protected]'
}

or the like.

If no matching endpoint exists, it should return null or undefined or false or something.

fediverse: caches error responses indefinitely

If an instance is down the lru_cache decorators around the instance check and client builder will prevent it from ever working again, until the app restarts or whatever. It would be good to use a cache with a timeout instead, and also to raise instead of return None as appropriate to avoid transitory results from being cached in the first place.

Short-term fix could just be to remove the caching because really who cares about a few extra requests.

InvalidURL exception when giving non-Mastodon Webfinger address

Via @snarfed, attempting to log on via @[email protected] generates an exception:

Traceback (most recent call last):
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/models.py", line 379, in prepare_url
    scheme, auth, host, port, path, query, fragment = parse_url(url)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/urllib3/util/url.py", line 234, in parse_url
    raise LocationParseError(url)
urllib3.exceptions.LocationParseError: Failed to parse: //@[email protected]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/fluffy/projects/Authl/authl/flask.py", line 179, in login
    handler, hid, id_url = instance.get_handler_for_url(me_url)
  File "/Users/fluffy/projects/Authl/authl/__init__.py", line 37, in get_handler_for_url
    request = request_url(url)
  File "/Users/fluffy/projects/Authl/authl/__init__.py", line 63, in request_url
    return requests.get(url)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/sessions.py", line 519, in request
    prep = self.prepare_request(req)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/sessions.py", line 462, in prepare_request
    hooks=merge_hooks(request.hooks, self.hooks),
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/models.py", line 313, in prepare
    self.prepare_url(url, params)
  File "/Users/fluffy/.local/share/virtualenvs/Authl-bMFs1yld/lib/python3.7/site-packages/requests/models.py", line 381, in prepare_url
    raise InvalidURL(*e.args)
requests.exceptions.InvalidURL: Failed to parse: //@[email protected]

Issue appears to be that the "this isn't a valid URL" exception handler needs to also catch requests.exceptions.InvalidURL.

Sign In with Apple

Should be straightforward to implement, and very useful for mobile app services.

Partial NASCAR interface

Maybe for major silo providers that don’t need an identity there could be NASCAR buttons that go straight to the appropriate login endpoint. Like

class Twitter(Handler):
    @property
    def iconic(self):
        return<img src= https://whatever/twitter.png>”, “https://twitter.com

And then the flask template could be like

<form method=post>
{%for icon,me in auth.handlers%}
{%if icon %}<button type=submit name=me value=“{me}”>{{icon|safe}}</button>{%endif%}
{%endfor%}
</form>

IndieAuth direct handler

While the IndieLogin handler is useful for things like RelMeAuth and the like, it'd also be nice to support IndieAuth directly, since that buys things like AutoAuth and also means that sites won't need to each register with IndieLogin.com (or another broker) to support IndieAuth.

Mastodon handler is way too permissive

Right now the Mastodon handler just trusts whatever profile uri the server gives back. There should be a validation step that ensures that the uri is at least one under the instance’s control, to prevent a trivial attack.

Retrieval of IndieWeb endpoints

When the IndieAuth profile is fetched it should also stash the various other indieweb endpoint links somewhere, possibly in profile['links'] or something.

Retain `me` value through error flow

If there's an error logging in, the disposition.Error should carry the failed identity URL so that the login form can present it as a default value.

Also, on the Flask handler, the error disposition could be passed through to the template, instead of using the message flashing mechanism which is a bit unwieldy.

Differentiate between profile URL redirections

Per indieweb/indieauth#36, IndieAuth profiles (and probably other profiles) need a better way to handle URL redirections than simply accepting the final URL. Currently we just chase all redirections and use the final response URL as the canonical URL, but instead we should keep track of redirections and use the last URL that came before the first temporary redirect.

This is the edge-casiest of edge cases and only really applies to IndieAuth/IndieLogin (which are the only handlers which even retrieve the profile page) but it would still be helpful to do the redirection chase. See below for an example of what (again, very rare) edge case would fail.

For that matter, if the canonical profile URL is different, the URL-based detection logic could be rerun so that e.g. https://beesbuzz.biz/twitter will be treated as https://twitter.com/fluffy and not dropped as an unhandled auth type.

IndieAuth test cases

Provided profile URL Redirection chain me URL Pass/fail
http://alice.example.com permanent -> https://alice.example.com https://alice.example.com pass
http://alice.example.com temporary -> https://alice.example.com https://alice.example.com fail? (different scheme)
http://alice.example.com temporary -> https://alice.example.com http://alice.example.com pass
https://alice.example.com permanent -> https://example.com/~alice https://alice.example.com fail (different domain)
https://alice.example.com temporary -> https://example.com/~alice https://alice.example.com pass
https://alice.example.com permanent -> https://example.com/~alice https://example.com/~alice/ pass

IndieAuth handler doesn't verify the `me` in the verification response

The IndieAuth handler erroneously accepts whatever me value comes in from the verification response. This is a major security concern, as anyone could set up a rogue authorization_endpoint which provides any arbitrary me value, allowing someone to log in as anyone else.

This is a high-priority vulnerability in versions of Authl <= 0.3.0.

Standardize user profile fields

Come up with a set of profile fields to make use of and map into a common format for display/recording purposes

Possible things to track:

  • display name
  • email address (if permitted by service)
  • profile URL (not necessarily the same as the identity URL, e.g. Twitter or Facebook)
  • homepage (not necessarily the same as the profile URL)
  • pronouns
  • arbitrary key/val pairs as provided by some services (e.g. Mastodon)
  • birthday?

On the Flask wrapper, only the identity URL should be stored in the session by default; everything else should be provided via the on_verified callback so that it's up to the app to store it reasonably (for example, in a UserProfile table or something).

Twitter auth needs external or stateless storage

Provide a token storage interface in the config API (dict-like interface). Default to in-process LRUDict but provide some sample implementations:

  • redis
  • memcached
  • flask session
  • ...?

While we're at it, the existing token_store thing should probably be renamed to make it less confusing (especially since it isn't a store anymore)

Sign in with Tumblr

Tumblr is still a thing, would be nice to allow logins with that.

TBD if it's actually reasonable to do though, because of the capricious nature of their profile URLs. According to the API docs, there is a permanent UID (which they incorrectly call a uuid) so the Twitter userID-in-fragment hack (e.g. username.tumblr.com#uid) could work.

There is an official client which should make this a lot easier, and looks very similar in usage to the Twitter client.

Email callback failed

User error report from @SpudRat3

error 500 Exception occurred
request time 2020-02-27 14:18:57-08:00
url https://beesbuzz.biz/_cb/e?t=WyJyYXlpaWlAd29ya3Nob3AzZC5jb20iLCIvNTMzNyJd.XlhAGQ.MaBvCkXF2EBLhIGP97C4_Gr7Xt8
path /_cb/e
full_path /_cb/e?t=WyJyYXlpaWlAd29ya3Nob3AzZC5jb20iLCIvNTMzNyJd.XlhAGQ.MaBvCkXF2EBLhIGP97C4_Gr7Xt8
endpoint authl.callback
url_rule /_cb/<hid>
Exception information KeyError: '_authl.prefill'

User reported second attempt worked

Mastodon handler

collected notes for Mastodon handler:

we can use Mastodon.py for this. basic process appears to be:

client_id, client_secret = Mastodon.create_app(
    'authl-beesbuzz.biz',
    scopes=('read'),
    api_base_url='https://instance.name',
    redirect_uris=[callback_uri])
client = Mastodon(
    client_id=client_id, client_secret=client_secret,
    api_base_url='https://instance.name')
return disposition.Redirect(
    client.auth_request_url(redirect_uris=callback_uri,scopes=('read')))

and then the callback handler will somehow get a client token that can then be used to somehow look up the user ID.

Remove url parameter from Handler.check_callback

None of the handlers use that argument and I can't see any way that it would even be used anyway. I think it was a leftover from a time when the handlers were going to form their own callback URL path, rather than leaving that up to the fronting application/framework wrapper.

Flask: Improve the default login template

Provide a better login template for the default login page, possibly with some customization options (such as being able to provide a stylesheet or additional markup or whatever).

Ideally it will include support for the friendly callback discovery/listing functionality that was mocked up in the UI wireframes (and provided by #3).

Allow testing multiple WebFinger URLs

If a WebFinger profile provides multiple profile links, iterate through all of them until one href provides a supported identity type.

This will require some rearchitecting; currently webfinger detection is a quick-and-dirty hack.

token_store should be an actual class/interface

Right now token_store is always just an itsdangerous signer, but really it should be an interface with set(value,redir) get and pop, and the default should be an implementation that is backed by the signer. get and pop should return a tuple of (value,redir). signer-backed impl uses utils.unpack_token as its implementation.

Make callback URLs protocol-stable

Right now the callback URL just uses its position in the handler list, which isn't compatible with some OAuth providers (notably Twitter) which require registering a stable callback URL. It also means that if configuration changes while someone is logging in, they will have an odd result occur.

It would be better if the handler gets a stable name that's associated with it.

Investigate using HTTP authentication instead of session cookies

The various protocols such as IndieAuth and AutoAuth intend for authorization bearing to be handled by an auth header, rather than a cookie jar. In the Flask configuration especially this seems like a thing that should be abided by.

The basic protocol is described in RFC 7235 and the IndieAuth/AutoAuth usage is discussed in the AutoAuth spec.

This is probably something that needs to be handled on a per-application basis but implementing it in the tests and documenting it in a central place will be helpful for others.

I am also probably severely misinterpreting something, because as far as I can tell the IndieAuth flow doesn't say anything about how authentication is stored between the site and the end user, and all of the IndieAuth-enabled things I'm finding seem to use a cookie jar for storing the actual session. So I'm not clear on how the AutoAuth flow is supposed to work in the first place.

See about making the JS partially work on older browsers

If the JS code can be structured such that there's no top-level async functions, and old browsers (e.g. IE11) simply elide the attempt to check the remote URL, older browsers could at least have the enable/disable flag working on the button. Which is a slight, if trivial, improvement.

Remove pipenv dependency from development

Instead of using a Pipfile/Pipfile.lock for local dev environment it'd be better for the Makefile to set up a local virtualenv for running the tools from, or something.

In the long run maybe we should switch to poetry instead of setuptools but that has its own set of issues that seem tangential to what we care about with Authl.

OpenID 1.x handler

In case we really want to support Ubuntu Launchpad and Livejournal. And Dreamwidth except they're talking about adding IndieAuth support anyway.

Clarify OAuth identities

Technically an OAuth-based identity does not need a full user URL, it only requires providing the common domain name of the service (E.g. Mastodon.social, twitter.com, etc). It might be nice to signal that in some way, or to even make the standard UX just forward to the authority if the user clicks on the appropriate link for the service in the list (like linking to /login?me=http://twitter.com or whatever).

But it might be nice to just pretend that the full URL is important for the sake of consistency anyway. (But it might look like a bug if someone has two identities on a remote service and they want to specify one but the handler gives them another...)

Login form loses redirection path on error

If an error occurs during authentication, the login redirection path is lost.

Reproduction: in test.py, attempt to log on from localhost:5000/some-path via Mastodon/Twitter/etc., and deny the login. Result: a successful login goes to localhost:5000/ instead.

Flask: just redirect if the user is already logged in

If the user is already logged in with the same identity as the me argument, just redirect to the redir target. That way someone can set a bookmark of, say, https://example.com/login/blog?me=http://fred.example.com and just keep the same login if they're already identified, or be redirected to their own login page if not.

Improve email safety

The EmailHandler should keep track of the addresses it's sent a link to in the last few minutes, and not allow spamming an address. This is both to prevent Authl from being used as an intermediary for flooding someone else's email address, and to avoid issues with a /_login/[email protected] address living in someone's history causing the user to get spammed (due to browser prefetch or whatever)

Either make it stateless, or allow persistent storage

On cloud-based deployments like Heroku, and on load balancers (I repeat myself), there's not currently any way for state to be preserved across process boundaries; all of the state is stored in an ExpiringDict.

It would be better to go back to storing the state value in an itsdangerous-signed token, which was the original design and is generally safe (modulo the concern of replay attacks, which is mitigated by making the signature expire anyway).

Alternately, allow for a persistent backing store for the state tokens, but that's got a lot of other implications to worry about and should only be a last resort.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.