How To Implement API Authentication Using API Keys (2024)

Jaz Allibhai

·

Follow

7 min read

·

Feb 18, 2024

Learn how to securely implement authentication for your API using API keys

How To Implement API Authentication Using API Keys (2)

So you have just finished building out your API. You only want authorized users to be able to access your API. So you generate API keys for these users. When a user makes a request to one of your API endpoints, they send their API key with the request. Now you have to verify that they have sent a valid API key so they can access the endpoint.

This post will go through how this API authentication process works in more detail.

After that, there will be code examples in Python using an API Authentication library called Keycove. At the end of this post, there is a full API example that you can play around with built with FastAPI and Keycove.

How to store API keys when implementing API authentication

Just like passwords, API keys should not be directly stored in your database. Storing API keys directly in your database is bad practice and not secure. They should be hashed and/or encrypted first before being stored. This would ensure the keys cannot be used, even if someone malicious gained access to your database.

Encryption and hashing are both ways to transform data into a series of different characters for security purposes.

For example, by encrypting or hashing the string Hello World, the output may look like this: 364ac7cf699f309568d46d9d79d34c80fa9f6ec18d5f4f812e07dc4fg17aa65f

The difference between encryption and hashing

Encryption is reversible and hashing is irreversible.

If you encrypt the string Hello World, it will become a random string like 364ac7cf69.... You can decrypt that random string to get the original Hello World string back.

You cannot do this with hashing. Once you hash a string, that original string cannot be recovered.

Why we need both encryption and hashing for API authentication

Some apps, such as Github, only show you your API key once when it is created. After that, if you lose your API key, you have to create a new one. There is no way of recovering it.

If you want your API authentication behavior to be like this, then you only need hashing. A step-by-step list on how hashing is used for API authentication is available in the next section.

Other apps, such as Twilio, allow you to view your API key again even after it’s been created. These apps store an encrypted version of your API key in their database. When you request to see your API key again, the key is decrypted and shown to you.

In this case, the app would store both the hashed and encrypted version of your API key. The hashed api key is needed for authentication, and the encrypted API key is needed in case the user needs to view their api key again.

How hashing is used for authentication

  1. You generate a new api key for a user.
  2. You hash the api key and store it in your database.
  3. The user makes a request to your api using their api key.
  4. Now you need to verify that the user has permission to make that request using the steps below.
  5. To do this, you hash the api key they sent with the request.
  6. You search your database to see if the hashed api key matches a hashed api key that is stored in the database.
  7. If there is a match, then the request is valid, which means the user has permission to access that api endpoint. If there is no match in the database, the request is invalid.

Note: You should allow users to put their API key in the request header instead of the URL when making a request to your API. This is more secure because URLs can be exposed in the browser history, cache, or server logging/monitoring tools, while headers won’t usually be exposed.

Below is an example of how to generate, encrypt, and hash api keys in Python.

We will be using the Keycove library, which is specifically made for API authentication. With Keycove, you can create verify, and securely store API keys.

After that, I will go through a full API authentication example in FastAPI using the Keycove library.

(I am the creator of the open source Keycove library. Feel free to star Keycove on Github here! You can also find the Keycove docs in the Github repository, which are hosted in the README section.)

To install Keycove:

pip install keycove

To generate a new API key:

from keycove import generate_token

api_key = generate_token()
>>> 'rmQikHfXyJ_h92fHUiseNKd-f2deStO84o-t1QOkFuY'

To hash an API key:

from keycove import hash

hash(value_to_hash=api_key)
>>> '2ef7bde608ce5404e97d5f042f95f89f1c232871'

To encrypt an API key:

A secret key is required for encryption / decryption. This secret key should be kept secure and not exposed publicly. The same secret key that was used when encrypting an API key should be used to decrypt it.

You should use the generate_secret_key() function because the encrypt and decrypt functions need a specific type of secret key that is compatible with the underlying algorithms that these functions use.

Not just any random string will be compatible with the encrypt and decrypt functions.

from keycove import encrypt, generate_secret_key

secret_key = generate_secret_key()

encrypt(value_to_encrypt=api_key, secret_key=secret_key)
>>> 'gAAAAABgTD0yR3O4hV7Kb7PZ6N4iZA3uJNeL3_ZI2QmGJHbLZUj4Cy5B2Pgh4lX3JNLUZ4Q8OvJZ8OZyXUYd8l4XQJZIV64nJA=='

To decrypt an API key:

from keycove import decrypt, encrypt, generate_secret_key

secret_key = generate_secret_key()
encrypted_value = encrypt(value_to_encrypt="rmQikHfXyJ_h92fHUiseNKd-f2deStO84o-t1QOkFuY", secret_key=secret_key)

decrypt(encrypted_value=encrypted_value, secret_key=secret_key)
>>> 'rmQikHfXyJ_h92fHUiseNKd-f2deStO84o-t1QOkFuY'

Below is a full example of API authentication in FastAPI and Keycove.

It is not recommended to organize all your API code in one code block like it is below. It is shown this way so you can copy and paste this into a .py file, start the server, and play around with this API with the interactive FastAPI docs.

The example uses Keycove with FastAPI, but Keycove can be used for API authentication with Flask, Django, or any other Python framework.

Beforehand, you need to install the dependencies:

pip install fastapi “uvicorn[standard]” sqlalchemy keycove

To start the server, run:

uvicorn filename:app — reload

To play around with the API in the interactive docs, go to:

http://localhost:8000/docs

Full API authentication with FastAPI and Keycove:

from fastapi import FastAPI, Depends, HTTPException, status, Header
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from keycove import encrypt, decrypt, hash, generate_token
from sqlalchemy.orm import Session

app = FastAPI()

SQLALCHEMY_DATABASE_URL = "sqlite:///db.sqlite3"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

class Key(Base):
__tablename__ = "keys"
id = Column(Integer, primary_key=True, autoincrement=True)
hashed_key = Column(String, nullable=False, unique=True)
encrypted_key = Column(String, nullable=True)

Base.metadata.create_all(bind=engine)

# this secret key was generated using the generate_secret_key function.
# do not use this secret key in production!!!
# do not store your secret key in your code - this is just for demonstration purposes
# you should store your secret key in an environment variable
secret_key = "EZdgwBIak481WZB8ZkZmzKHTDkRQFzDjeTrhSlU_v2g="

def verify_api_key(api_key: str = Header(None), db: Session = Depends(get_db)) -> None:
"""
This function verifies the provided API key by hashing it and checking if the hashed key exists in the database.
If the hashed key does not exist in the database, it raises an HTTPException with a 404 status code.

Parameters:
api_key (str): The API key to verify. This is expected to be provided in the request header.
db (Session): The database session to use for querying the database.

Raises:
HTTPException: If the provided API key is not valid.
"""

key = db.query(Key).filter(Key.hashed_key == hash(api_key)).first()
if not key:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"The provided API key is not valid. Please provide a valid API key.",
)

@app.get("/protected")
def protected_route(verify_api_key: None = Depends(verify_api_key)):
"""
This function is a protected route that requires a valid API key to access.
If the API key is valid, it returns a message indicating that access has been granted.

Parameters:
verify_api_key (None): This is a dependency that ensures the API key is verified before the function is accessed.

Returns:
dict: A dictionary with a message indicating that access has been granted.
"""

return {"message": "access granted"}

@app.post("/create_api_key")
def create_api_key(db: Session = Depends(get_db)):
"""
This function creates a new API key, hashes it, encrypts it, and stores it in the database.
It then returns the new API key.

Parameters:
db (Session): The database session to use for querying the database.

Returns:
dict: A dictionary with the new API key.
"""

api_key = generate_token()
hashed_key = hash(api_key)
encrypted_key = encrypt(api_key, secret_key)
new_key = Key(hashed_key=hashed_key, encrypted_key=encrypted_key)

db.add(new_key)
db.commit()
db.refresh(new_key)

return {"api_key": api_key}

@app.get("/decrypt_api_key")
def decrypt_api_key(
api_key: str = Header(None),
verify_api_key: str = Depends(verify_api_key),
db: Session = Depends(get_db),
):
"""
This function decrypts a given API key using a provided secret key.
The same secret key that was used to encrypt the API key should be used to decrypt it.
The function first hashes the provided API key and then queries the database for a key with the same hash.
If such a key is found, the function decrypts the encrypted key stored in the database and returns it.

Parameters:
api_key (str): The API key to decrypt.
verify_api_key (str): A dependency that ensures the API key is verified before the function is accessed.
db (Session): The database session to use for querying the database.

Returns:
str: The decrypted API key.

Raises:
HTTPException: If the provided API key is not valid.
"""

key = db.query(Key).filter(Key.hashed_key == hash(api_key)).first()
return decrypt(key.encrypted_key, secret_key)

@app.get("/keys")
def get_keys(db: Session = Depends(get_db)):
"""
This function retrieves all the hashed keys from the database.

Parameters:
db (Session): The database session to use for querying the database.

Returns:
dict: A dictionary with all the hashed keys.
"""

keys = db.query(Key).all()
return {"keys": [key.hashed_key for key in keys]}

You’ve made it to the end of the post! If you haven’t already, feel free to star Keycove on Github. Thanks for reading!

How To Implement API Authentication Using API Keys (2024)
Top Articles
ACAT Eligible Custodians
6 Things to Know Before You Change Brokers | The Motley Fool
Omega Pizza-Roast Beef -Seafood Middleton Menu
Spn 1816 Fmi 9
Lifewitceee
Kraziithegreat
Collision Masters Fairbanks
Triumph Speed Twin 2025 e Speed Twin RS, nelle concessionarie da gennaio 2025 - News - Moto.it
P2P4U Net Soccer
Walgreens Alma School And Dynamite
Barstool Sports Gif
J Prince Steps Over Takeoff
The Haunted Drury Hotels of San Antonio’s Riverwalk
Encore Atlanta Cheer Competition
Hillside Funeral Home Washington Nc Obituaries
Bernie Platt, former Cherry Hill mayor and funeral home magnate, has died at 90
Magicseaweed Capitola
Grace Caroline Deepfake
Peraton Sso
Does Breckie Hill Have An Only Fans – Repeat Replay
Honda cb750 cbx z1 Kawasaki kz900 h2 kz 900 Harley Davidson BMW Indian - wanted - by dealer - sale - craigslist
Craigslist Sparta Nj
Mahpeople Com Login
Why Does Lawrence Jones Have Ptsd
Heart and Vascular Clinic in Monticello - North Memorial Health
Purdue 247 Football
Seeking Arrangements Boston
UMvC3 OTT: Welcome to 2013!
Hdmovie2 Sbs
48 Oz Equals How Many Quarts
Getmnapp
Best Middle Schools In Queens Ny
A Christmas Horse - Alison Senxation
Horses For Sale In Tn Craigslist
Grave Digger Wynncraft
Greyson Alexander Thorn
Craigslist Middletown Ohio
Moonrise Time Tonight Near Me
Dreamcargiveaways
Craigslist List Albuquerque: Your Ultimate Guide to Buying, Selling, and Finding Everything - First Republic Craigslist
Anya Banerjee Feet
How to play Yahoo Fantasy Football | Yahoo Help - SLN24152
Levothyroxine Ati Template
Adam Bartley Net Worth
Complete List of Orange County Cities + Map (2024) — Orange County Insiders | Tips for locals & visitors
Skyward Marshfield
Mother Cabrini, the First American Saint of the Catholic Church
Marcel Boom X
About us | DELTA Fiber
300 Fort Monroe Industrial Parkway Monroeville Oh
2121 Gateway Point
Latest Posts
Article information

Author: Melvina Ondricka

Last Updated:

Views: 5703

Rating: 4.8 / 5 (68 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Melvina Ondricka

Birthday: 2000-12-23

Address: Suite 382 139 Shaniqua Locks, Paulaborough, UT 90498

Phone: +636383657021

Job: Dynamic Government Specialist

Hobby: Kite flying, Watching movies, Knitting, Model building, Reading, Wood carving, Paintball

Introduction: My name is Melvina Ondricka, I am a helpful, fancy, friendly, innocent, outstanding, courageous, thoughtful person who loves writing and wants to share my knowledge and understanding with you.