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

feat(api): long lived api keys #658

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open

Conversation

gphorvath
Copy link
Contributor

@gphorvath gphorvath commented Jun 18, 2024

Addresses #561

@gphorvath gphorvath added the enhancement ✨ New feature or request label Jun 18, 2024
@gphorvath gphorvath self-assigned this Jun 18, 2024
@gphorvath gphorvath requested a review from a team as a code owner June 18, 2024 21:34
Copy link

netlify bot commented Jun 18, 2024

Deploy Preview for leapfrogai-docs canceled.

Name Link
🔨 Latest commit aec152c
🔍 Latest deploy log https://app.netlify.com/sites/leapfrogai-docs/deploys/668ea69da3bb820007604dc7

@gphorvath gphorvath linked an issue Jun 18, 2024 that may be closed by this pull request
@gphorvath gphorvath marked this pull request as draft June 18, 2024 21:43
@gphorvath gphorvath requested a review from YrrepNoj June 18, 2024 21:43
@gphorvath gphorvath marked this pull request as ready for review July 3, 2024 22:41
@gphorvath gphorvath linked an issue Jul 8, 2024 that may be closed by this pull request
Copy link
Contributor

@justinthelaw justinthelaw left a comment

Choose a reason for hiding this comment

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

Some initial comments for discussion! Great work so far - this will be super helpful for delivery and all customers in general.


def _validate_checksum(unique_key: str, checksum: str):
"""Validate the checksum of an API key."""
return hashlib.sha256(unique_key.encode()).hexdigest()[:CHECKSUM_LENGTH] == checksum
Copy link
Contributor

Choose a reason for hiding this comment

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

I recommend changing this to secrets.compare_digest(calculated_checksum, checksum)

https://pycharm-security.readthedocs.io/en/latest/fixes/comparedigestfixer.html

"""
Validate and encode an API key.

Should be in the form: `{prefix}_{unique_key}_{checksum}`
Copy link
Contributor

@justinthelaw justinthelaw Jul 9, 2024

Choose a reason for hiding this comment

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

We should also add some sort of versioning prefix here to add backwards compatibility in case we change the API key algorithm, etc.

Example:

read_once_token = f"{KEY_PREFIX}_{VERSION}.{unique_key}.{checksum}"

Copy link
Contributor Author

@gphorvath gphorvath Jul 9, 2024

Choose a reason for hiding this comment

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

If we do this on the database side (see pgcrypto comment) do you think the created_at date in the table is sufficient for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that would definitely work! In the future, we can just use the created_on date to determine key version in case we need to be backwards compatible.

returns:
api_key: str # in the format {prefix}_{one_way_hash}_{checksum}
"""
one_way_hash = hashlib.sha256(unique_key.encode()).hexdigest()
Copy link
Contributor

@justinthelaw justinthelaw Jul 9, 2024

Choose a reason for hiding this comment

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

We should probably use a different method here, like bcrypt or pbkdf2_hmac. Both are more secure, if not a little more computationally intensive. For example, 1Password uses a version of pbkdf2_hmac, I believe.

https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords

Example:

key = hashlib.pbkdf2_hmac('sha256', unique_key.encode(), salt.encode(), 100000, dklen=64)
one_way_hash = key.hex()

Copy link
Contributor

Choose a reason for hiding this comment

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

If this is for security purposes, I would also toss pgcrypto in the running there as well.

Though if its not for security purposes then I think its unnecessary to change the algorithm any encoding will do if it gets the job done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, it was mainly for security best practice. It just depends on how secure we want these API keys to be!

You are right though - if we are only looking for functionality, everything here works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, definitely need security here. I'll look at these options

Copy link
Contributor Author

@gphorvath gphorvath Jul 9, 2024

Choose a reason for hiding this comment

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

Think I'm going with pgcrypto here. The main advantage is server side hashing. I will update when I finish the commit.

src/leapfrogai_api/pyproject.toml Show resolved Hide resolved
def test_create_api_key(create_api_key):
"""Test creating an API key. Requires a running Supabase instance."""
assert create_api_key.status_code is status.HTTP_200_OK
assert "api_key" in create_api_key.json(), "Create should return an API key."
Copy link
Contributor

Choose a reason for hiding this comment

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

May be worthwhile to also test if the API key is in the correct format (prefixes).

@@ -26,6 +26,7 @@
from leapfrogai_api.routers.supabase_session import Session

router = APIRouter(prefix="/openai/v1/vector_stores", tags=["openai/vector_stores"])
security = HTTPBearer()
Copy link
Member

Choose a reason for hiding this comment

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

Is this being used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps use constants for the magic numbers in this file or add more explanations in the comments are to why these (32, 4) numbers were chosen.

returns:
api_key: str # in the format {prefix}_{one_way_hash}_{checksum}
"""
one_way_hash = hashlib.sha256(unique_key.encode()).hexdigest()
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is for security purposes, I would also toss pgcrypto in the running there as well.

Though if its not for security purposes then I think its unnecessary to change the algorithm any encoding will do if it gets the job done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement ✨ New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Exposing API variable naming - confusing feat(API) Long Lived API Keys
4 participants