Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved API key handling #3094

Merged
merged 29 commits into from
Nov 4, 2024
Merged

Improved API key handling #3094

merged 29 commits into from
Nov 4, 2024

Conversation

tillprochaska
Copy link
Contributor

@tillprochaska tillprochaska commented May 25, 2023

This PR includes a number of changes related to API keys:

  • New users do not get an API key by default anymore. Instead, they have to generate one first if they want to use the Aleph API.
  • User can regenerate their API key which invalidates the old API key.
  • Users can see their API key once after they’ve (re-)generated it, but not again after that.
  • Users receive an email notification when an API key is (re-)generated.
  • New API keys automatically expire after 90 days. Users will receive an email notification 7 days before their key will expire and another notification when it has expired.
  • Existing API keys (i.e. keys generated before rolling out this change) do not expire automatically, but there is a CLI command aleph reset-api-key-expiration to set the expiration date for these keys.

Some additional information about implementation decisions:

  • I’ve refactored the settings screen, both with regards to the visual appearance as well as the implementation. The settings screen was a little cluttered before, and this PR as well as a few follow-up PRs to come will add even more settings to the screen. This refactoring prepares for that.

  • It’s quite common to allow users to generate multiple API keys for different purposes, and to be able to revoke/expire them individually. While this has advantages, it also adds complexity to the implementation. Assuming that most users do indeed use the API key as a personal API key to e.g. programmatically search Aleph for a data analysis etc. rather than integrating the API key into ten different external services, I’ve decided to avoid this complexity, at least for now.


Screen.Recording.2024-04-16.at.19.38.19.mov

To do:

  • Right now, the settings UI displays "Reset API key" even for new users who do not have an API key. The settings UI should handle that case (something along the lines of "You currently do not have an API key. Generate an API key in order to use the Aleph API.")
  • The API endpoint and some methods in the code base are currently named "reset_api_key" or similar. It would probably make sense to rename them to "generate_api_key" as these endpoints/methods are also used to set an initial API key.

@tillprochaska tillprochaska force-pushed the feature/1027-reset-api-key branch 2 times, most recently from 3ea2d5a to 71e7678 Compare May 25, 2023 12:57
@tillprochaska tillprochaska linked an issue May 26, 2023 that may be closed by this pull request
@tillprochaska
Copy link
Contributor Author

Related idea: Is it necessary to create an API key for every user by default? Most users probably do not use the API at all. We could reuse the logic to reset to let users create an initial key on demand.

@tillprochaska tillprochaska force-pushed the feature/1027-reset-api-key branch 2 times, most recently from e135d0d to 5a88cea Compare April 16, 2024 18:09
@tillprochaska
Copy link
Contributor Author

@Rosencrantz As mentioned before I still need to double-check a few technical implementation details, but let me know what you think about the UX.

@tillprochaska tillprochaska marked this pull request as ready for review April 29, 2024 18:00
@tillprochaska tillprochaska marked this pull request as draft April 30, 2024 18:10
@tillprochaska
Copy link
Contributor Author

I’ve converted this back to a draft as I started implementing additional changes on this branch to avoid merge conflicts. However, all changes on this branch are ready for review and I’d appreciate a review of what’s already there.

@tillprochaska tillprochaska changed the title Reset API key Improved API key handling Apr 30, 2024
@tillprochaska tillprochaska marked this pull request as ready for review May 6, 2024 09:55
aleph/logic/api_keys.py Show resolved Hide resolved
aleph/logic/api_keys.py Show resolved Hide resolved
aleph/templates/email/api_key_expired.html Outdated Show resolved Hide resolved
ui/src/components/Settings/ApiKeySettings.tsx Outdated Show resolved Hide resolved
aleph/tests/test_role_model.py Show resolved Hide resolved
aleph/logic/api_keys.py Outdated Show resolved Hide resolved
aleph/logic/api_keys.py Outdated Show resolved Hide resolved
aleph/model/role.py Outdated Show resolved Hide resolved
</h4>

<p>
{expiresAt && (
Copy link
Contributor Author

@tillprochaska tillprochaska May 22, 2024

Choose a reason for hiding this comment

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

This currently only handles API keys that will expire in the future. There should be a separate case that handles API keys that have expired.

Case 1: User doesn’t have an API key:

You need an API key in order to access the Aleph API. Generate an API key to get started."

Case 2: User has an API key that hasn’t yet expired:

Your API key will expire on {date}. You can regenerate your API key at any time. When you regenerate your API key, your old key will stop working.

Case 3: User has an expired API key.

Your API key expired on {date}. Regenerate your API key to continue using the Aleph API.

Case 4: User has an API key that does not expire:

When you regenerate your API key, your old key will stop working.

How API keys are reset and displayed has changed since the initial version of API keys: Users will be able to view an API key exactly once after it has been created/reset. This requires a slightly different user interface. We’re also planning a few more changes to API keys in the future, and these UI changes prepare for that.
The existing settings UI was a little cluttered and unstructured. We’re going to add new settings in this PR and in follow-up PRs, so I took the time to clean up the UI (both visually and implementation-wise).
This is a hacky workaround, but a proper fix would require quite some refactoring. Considering that this hack is pretty isolated and not going to affect any other parts of the UI and that we will need to upgrade to Blueprint 5 at some point anyway, I’ve opted for the quick-and-dirty solution for now.
In the future, roles won’t have an API key by default anymore. As an alternative, we generate session tokens explicitly.
Most users do not need API access so there’s no reason to generate an API key for them by default.
Previously, an API was generate automatically for new users, i.e. every user had an API key. This has now changed, and the settings UI needs to properly handle situations where a user doesn’t yet have an API key.

As this increases the complexity of the UI state, I’ve refactored the component to make use of a local reducer.
This method is now also used to generate an initial key for users who do not yet have an API key.
While the logic initially was quite simply, there will be more business logic related to API keys, e.g. sending notifications ahead of and when an API key has expired.
Initially, I added this to the role model as the model to be consistent with the model's `set_password` method. However, as the logic to generate an API token has become more complex, it is clear that it shouldn't live in the model.
Aleph represents both users and groups using the role model. However, some API keys (such as `has_password` or `has_api_key` are not relevant for groups).
We merged a different migration in the meantime (8adf50) and as a result Alembic wasn’t able to figure out how to upgrade the database unambiguously.
452e46 changes the way we handle authentication in tests. Previously, we used API keys to authenticate users. Now, as we don’t generate API keys for users by default anymore, we use session tokens (the same mechanism the UI uses as well). In general, authentication using session tokens and API keys is quite similar: In both cases, the token is passed as a request header.

However, there’s one significant difference between session tokens and API keys: When using API keys, data about the user (including group memberships) is loaded from the database on every request. When using session tokens, this data is cached in Redis for the lifetime of the session. For end users, this means they have to sign out and in again after their group memberships have been updated.

When I originally implemented this change in our tests, it didn’t affect any of the tests as none of them did rely on updating group memberships. However, I’ve since implemented and merged additional tests in #3865, including one test that covers updating the user’s group memberships after the initial sign in. When I rebased this branch to include these new tests, this particular test failed.

The solution for this is the same as for end users: In the test case, we need to reauthenticate the test user after updating the group memberships.
We merged other migrations in the meantime and as a result Alembic wasn’t able to figure out how to upgrade the database unambiguously.
@tillprochaska tillprochaska merged commit ff03c2a into develop Nov 4, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refresh my personal API key
4 participants