In APIs, we rely on two fundamental security concepts: authentication and authorization.
Authentication verifies who a user is. It’s like a digital bouncer at the entrance of your API, checking IDs to confirm identities. Common methods include username/password logins, API keys, and tokens.
Authorization determines what a user is allowed to do. Once a user’s identity is confirmed, authorization defines their permissions and access levels. For instance, some users might only be allowed to read data, while others have permission to modify or delete it.
HTTP Basic Authentication
HTTP Basic Authentication is a straightforward mechanism that relies on usernames and passwords. The client sends its credentials encoded in Base64 within the Authorization header of the request.
Example:
Authorization: Basic Qm9iOnRoZWZhcm1lcg== // Encoded "Bob:thefarmer"
Here’s how it works:
- The client sends a request to the server.
- The server, unable to authenticate the client, responds with a 401 Unauthorized status code and includes the WWW-Authenticate header, prompting the client for credentials.
- The client resends the request, this time including its encoded username and password in the Authorization header.
- The server verifies the credentials and, if valid, allows the request to proceed.
sequenceDiagram participant Client participant Server Client->>Server: GET /protected-resource Server-->>Client: 401 Unauthorized<br/>WWW-Authenticate: Basic realm="Access to the staging site" Note over Client: User enters credentials Client->>Server: GET /protected-resource<br/>Authorization: Basic Qm9iOnRoZWZhcm1lcg== Note over Server: Server decodes and verifies credentials alt Credentials are valid Server-->>Client: 200 OK<br/>(Protected resource content) else Credentials are invalid Server-->>Client: 401 Unauthorized end
HTTP Basic Authentication has advantages and disadvantages:
Advantage | Disadvantage |
---|---|
Simplicity of implementation. | Passwords are sent with every request, increasing the risk of interception. |
Widely supported by clients and servers. | Overhead from authenticating every request. |
Credentials are only encoded (Base64), not encrypted, providing limited security. |
Due to these limitations, HTTP Basic Authentication is generally not recommended for modern APIs, especially those handling sensitive data.
API Keys: Identifying Applications, Not Users
API keys offer a more secure way to identify the application making a request, rather than an individual user. They are unique, randomly generated strings assigned to each application or developer using the API.
Key Point: API keys are not a replacement for user authentication; they simply identify the calling application. Additional authentication mechanisms are typically needed to verify individual users.
Example:
apiKey=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Here’s a common API key workflow:
- An application requests an API key from the provider.
- The provider generates a unique API key and assigns it to the application.
- The application includes this API key in the headers of its requests.
- The server, upon receiving a request, validates the API key against its records to verify the application’s identity.
sequenceDiagram participant App as Application participant Provider as API Provider participant API as API Server App->>Provider: Request API key Provider->>Provider: Generate unique API key Provider-->>App: Provide API key<br/>(e.g., a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6) Note over App: Store API key securely App->>API: API request with key in header<br/>(e.g., X-API-Key: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6) API->>API: Validate API key alt API key is valid API-->>App: 200 OK (Request processed) else API key is invalid API-->>App: 401 Unauthorized end
API keys provide several advantages over HTTP Basic Authentication:
- Multiple keys can be generated for a single account, allowing for finer-grained control.
- Keys can be easily revoked if compromised.
- Because API keys are not tied to user credentials, their compromise doesn’t necessarily expose user accounts.
However, API keys also have limitations:
- They need to be kept secret, as their exposure can compromise the application’s identity.
- They need to be sent with every request, potentially adding overhead.
- They don’t inherently provide authorization; they only identify the application making the request.
JSON Web Tokens (JWT)
JSON Web Tokens (JWT) are a popular standard for securely transmitting information between parties as compact, self-contained JSON objects. JWTs can be used for both authentication and authorization, making them a versatile choice for securing APIs.
A JWT consists of three parts:
- Header: Contains information about the token type (JWT) and the algorithm used to sign it (e.g., HS256).
- Payload: Contains claims, which are statements about the user or application the token represents (e.g., user ID, permissions, expiration time).
- Signature: Ensures the integrity of the token, verifying that it hasn’t been tampered with. It’s created by signing the header and payload using a secret key.
JWTs are typically encoded in Base64 and represented as a single string with periods separating the parts:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Here’s a typical JWT workflow:
- The client authenticates with the server, providing credentials (e.g., username/password).
- Upon successful authentication, the server generates a JWT containing relevant user information and permissions.
- The client receives the JWT and stores it securely (e.g., in local storage).
- For subsequent requests, the client includes the JWT in the Authorization header, typically using the Bearer scheme.
- The server validates the JWT’s signature and extracts the claims to verify the user’s identity and permissions.
sequenceDiagram participant Client participant Server participant API Client->>Server: Authenticate (username/password) Server->>Server: Verify credentials Server->>Server: Generate JWT Server-->>Client: Return JWT Note over Client: Store JWT securely Client->>API: API request with JWT in header<br/>(Authorization: Bearer [JWT]) API->>API: Validate JWT signature API->>API: Extract and verify claims alt JWT is valid API-->>Client: 200 OK (Request processed) else JWT is invalid or expired API-->>Client: 401 Unauthorized end
JWTs offer several advantages:
- Scalability: They are stateless, making them suitable for distributed systems.
- Temporary Access: They can have expiration times, limiting the window of vulnerability.
- Fine-grained Authorization: Claims within the payload can define specific permissions for each user or application.
However, JWTs also present challenges:
- Processing Overhead: Verifying signatures and parsing claims can add some processing overhead.
- Key Management: The secret key used to sign JWTs must be kept secure.
API Keys vs. JWTs
- API Keys: Best for identifying applications and controlling access at a coarser level.
- JWTs: More suitable for user authentication and fine-grained authorization, as they can embed user-specific information and permissions.
Feature | HTTP Basic Authentication | API Keys | JWTs |
---|---|---|---|
Format | Base64 encoded username:password | Long random string | Three-part encoded string |
Typical Use Case | Simple authentication for less sensitive apps | Application/client authentication | User authentication and authorization |
Transmission | Sent in request header | Typically sent in request header or query parameter | Sent in request header |
Stateless | Yes | Yes | Yes |
Expiration | No built-in expiration | No built-in expiration | Can include expiration claim |
Revocation | Requires changing password | Requires revoking and reissuing key | Requires maintaining a blacklist or using short expiration times |
Security | Low (unless used with HTTPS) | Moderate | High |
Information Capacity | Limited to username | Limited | Can contain arbitrary claims/data |
Scalability | Good | Good | Excellent |
Implementation Complexity | Low | Low | Moderate |
Self-contained | No | No | Yes |
Suitable for | Server-to-server communication | API access control | Single sign-on, mobile apps, web apps |
Real-World Example
Imagine you’re building a travel booking platform. You need to pull in real-time flight data, so you turn to a third-party API. This is where system design meets security: how do you control access to that API and make sure only your platform is using it?
Scenario:
- Your platform needs to display flight details fetched from a third-party API.
- You need to:
- Authenticate: Prove it’s really your platform making the requests.
- Track Usage: Monitor how many requests your platform makes (you might have to pay for them!).
So, Which One for Our Travel Platform?
In this scenario, API keys are the better fit. Here’s why:
- API keys are easy to implement and manage, reducing code complexity.
- We mainly need to prove it’s our platform making the request (authentication), not fine-grained control over specific actions (authorization).
Key Takeaways:
- API keys are great for straightforward API authentication and usage tracking.
- JWTs shine when you need more granular authorization control and richer data exchange.