Skip to content

Commit

Permalink
Add the "http_token" authentication mechanism
Browse files Browse the repository at this point in the history
It is pretty much the "Bearer TOKEN" method but which uses different keyword
"Token".  It is e.g. the one provided by Django REST Framework.
GitHub allows for both 'Bearer' and 'Token' keywords:
https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2022-11-28

In our case of DANDI we ran into it only now that we decided to add handling of
embargoed datasets, for which we need to authenticate to access the files.  We
are using Django REST Framework and insofar decision was made to retain current
approach (and fix WWW-Authentication header response), instead of an
alternative to allow for both  'Bearer' and 'Token' as was suggested in
dandi/dandi-archive#1830

As it is ad-hoc (nothing AFAIK in W3C standard) Authentication response I do
not think some automation would ever be created based on WWW-Authentication but
we will be able to support such providers.
  • Loading branch information
yarikoptic committed Jan 25, 2024
1 parent 08315c1 commit b22f9de
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
19 changes: 17 additions & 2 deletions datalad/downloaders/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,18 +387,33 @@ class HTTPDigestAuthAuthenticator(HTTPRequestsAuthenticator):

@auto_repr
class HTTPBearerTokenAuthenticator(HTTPRequestsAuthenticator):
"""Authenticate via HTTP Authorization header
"""Authenticate via HTTP 'Authorization: Bearer TOKEN' header
E.g. as defined for OAuth2 in RFC 6750
https://datatracker.ietf.org/doc/html/rfc6750
"""

DEFAULT_CREDENTIAL_TYPE = 'token'
AUTH_KEYWORD = 'Bearer'

def __init__(self, **kwargs):
# so we have __init__ solely for a custom docstring
super(HTTPBearerTokenAuthenticator, self).__init__(**kwargs)

def _post_credential(self, credentials, post_url, session):
# we do not need to post anything, just inject token into the session
session.headers['Authorization'] = "Bearer %s" % credentials['token']
session.headers['Authorization'] = f"{self.AUTH_KEYWORD} {credentials['token']}"


class HTTPTokenAuthenticator(HTTPBearerTokenAuthenticator):
"""Authenticate via HTTP 'Authorization: Token TOKEN' header
It is pretty much the "Bearer TOKEN" method but which uses different keyword
"Token". It is e.g. the one provided by Django REST Framework.
GitHub allows for both 'Bearer' and 'Token' keywords:
https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2022-11-28
"""
AUTH_KEYWORD = 'Token'


@auto_repr
Expand Down
2 changes: 2 additions & 0 deletions datalad/downloaders/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
HTTPDigestAuthAuthenticator,
HTTPBearerTokenAuthenticator,
HTTPDownloader,
HTTPTokenAuthenticator,
)
from .s3 import S3Authenticator, S3Downloader
from .shub import SHubDownloader
Expand All @@ -51,6 +52,7 @@
'http_auth': HTTPAuthAuthenticator,
'http_basic_auth': HTTPBasicAuthAuthenticator,
'http_digest_auth': HTTPDigestAuthAuthenticator,
'http_token': HTTPTokenAuthenticator,
'bearer_token': HTTPBearerTokenAuthenticator,
'bearer_token_anon': HTTPAnonBearerTokenAuthenticator,
'aws-s3': S3Authenticator, # TODO: check if having '-' is kosher
Expand Down
6 changes: 6 additions & 0 deletions datalad/downloaders/tests/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
HTTPBaseAuthenticator,
HTTPBearerTokenAuthenticator,
HTTPDownloader,
HTTPTokenAuthenticator,
process_www_authenticate,
)

Expand Down Expand Up @@ -740,6 +741,11 @@ def request_get_callback(request, uri, headers):
content = read_file(fpath)
assert_equal(content, "correct body")

# While having this test case setup, test the the odd brother
downloader = HTTPDownloader(credential=credential, authenticator=HTTPTokenAuthenticator())
downloader.download(url, path=d, overwrite=True)
assert_equal(request_get_callback.req.headers['Authorization'], "Token testtoken")


class FakeLorisCredential(Token):
"""Credential to test scenarios."""
Expand Down

0 comments on commit b22f9de

Please sign in to comment.