Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
- `download` now returns Path consistently.
([#595])(https://github.com/nsidc/earthaccess/issues/595)
([@Sherwin-14](https://github.com/Sherwin-14))
- Users may now authenticate with an existing Earthdata login token with
environment variable `EARTHDATA_TOKEN`
([#484](https://github.com/nsidc/earthaccess/issues/484))
([@kgrimes2](https://github.com/kgrimes2))

### Removed

Expand Down
6 changes: 5 additions & 1 deletion docs/howto/authenticate.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ use:
auth = earthaccess.login(strategy="environment")
```

Alternatively, you can use an existing Earthdata Login token by setting the environment
variable `EARTHDATA_TOKEN` to it and using the same "environment" strategy, above.

If you wish to enter your Earthdata Login credentials when prompted, with
optional persistence to your `.netrc` file (see below), specify the interactive
strategy:
Expand All @@ -54,7 +57,8 @@ credentials in two locations:
used throughout documentation primarily for convenience. The only
requirement is that the *contents* of the file adhere to the
[`.netrc` file format](https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html).
2. `EARTHDATA_USERNAME` and `EARTHDATA_PASSWORD` environment variables
2. `EARTHDATA_USERNAME` and `EARTHDATA_PASSWORD` environment variables (or, optionally, `EARTHDATA_TOKEN`
to use an existing Earthdata Login token)

If neither of these options are configured, you can authenticate by calling the
`earthaccess.login()` method and manually entering your EDL account credentials.
Expand Down
4 changes: 3 additions & 1 deletion earthaccess/api.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have token as a stand-alone auth mechanism, this makes more sense. Like you said we could enhance the "interactive" way yo input a token first and if not provided, will do the username/password as usual.

Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ def login(
* **"all"**: (default) try all methods until one works
* **"interactive"**: enter username and password.
* **"netrc"**: retrieve username and password from ~/.netrc.
* **"environment"**: retrieve username and password from `$EARTHDATA_USERNAME` and `$EARTHDATA_PASSWORD`.
* **"environment"**: retrieve either a username and password pair from
the `EARTHDATA_USERNAME` and `EARTHDATA_PASSWORD` environment variables,
or an Earthdata login token from the `EARTHDATA_TOKEN` environment variable.
persist: will persist credentials in a .netrc file
system: the Earthdata system to access, defaults to PROD

Expand Down
25 changes: 17 additions & 8 deletions earthaccess/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,11 @@ def login(
* **"interactive"**: Enter a username and password.
* **"netrc"**: (default) Retrieve a username and password from ~/.netrc.
* **"environment"**:
Retrieve a username and password from $EARTHDATA_USERNAME and $EARTHDATA_PASSWORD.
persist: Will persist credentials in a `.netrc` file.
Retrieve either a username and password pair from the
`EARTHDATA_USERNAME` and `EARTHDATA_PASSWORD` environment variables,
or an Earthdata login token from the `EARTHDATA_TOKEN` environment
variable.
persist: Will persist username and password credentials in a `.netrc` file.
system: the EDL endpoint to log in to Earthdata, defaults to PROD

Returns:
Expand Down Expand Up @@ -245,7 +248,7 @@ def _interactive(
) -> bool:
username = input("Enter your Earthdata Login username: ")
password = getpass.getpass(prompt="Enter your Earthdata password: ")
authenticated = self._get_credentials(username, password)
authenticated = self._get_credentials(username, password, None)
if authenticated:
logger.debug("Using user provided credentials for EDL")
if persist_credentials:
Expand Down Expand Up @@ -282,7 +285,7 @@ def _netrc(self) -> bool:
f"Password not found in .netrc file {netrc_loc}"
)

authenticated = self._get_credentials(username, password)
authenticated = self._get_credentials(username, password, None)

if authenticated:
logger.debug("Using .netrc file for EDL")
Expand All @@ -292,21 +295,27 @@ def _netrc(self) -> bool:
def _environment(self) -> bool:
username = os.getenv("EARTHDATA_USERNAME")
password = os.getenv("EARTHDATA_PASSWORD")
token = os.getenv("EARTHDATA_TOKEN")

if not username or not password:
if (not username or not password) and not token:
raise LoginStrategyUnavailable(
"EARTHDATA_USERNAME and EARTHDATA_PASSWORD are not set in the current environment, try "
"setting them or use a different strategy (netrc, interactive)"
"Either the environment variables EARTHDATA_USERNAME and "
"EARTHDATA_PASSWORD must both be set, or EARTHDATA_TOKEN must be set for "
"the 'environment' login strategy."
)

logger.debug("Using environment variables for EDL")
return self._get_credentials(username, password)
return self._get_credentials(username, password, token)

def _get_credentials(
self,
username: Optional[str],
password: Optional[str],
user_token: Optional[str],
) -> bool:
if user_token is not None:
self.token = {"access_token": user_token}
self.authenticated = True
if username is not None and password is not None:
token_resp = self._find_or_create_token(username, password)

Expand Down
13 changes: 13 additions & 0 deletions tests/unit/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# package imports
import logging
import os
import unittest
from unittest import mock

Expand Down Expand Up @@ -74,6 +75,18 @@ def test_auth_can_create_proper_credentials(self, user_input, user_password):
self.assertEqual(auth.password, "password")
self.assertEqual(auth.token, json_response)

@responses.activate
def test_auth_can_parse_existing_user_token(self):
user_token = "ABCDEFGHIJKLMNOPQ"
os.environ["EARTHDATA_TOKEN"] = user_token
json_response = {"access_token": user_token}

# Test
auth = Auth()
auth.login(strategy="environment")
self.assertEqual(auth.authenticated, True)
self.assertEqual(auth.token, json_response)

@responses.activate
@mock.patch("getpass.getpass")
@mock.patch("builtins.input")
Expand Down