Authentication and Authorization:

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.

Authentication vs Authorization API Key Authentication Authorized Personnel Only Authorization

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:

  1. The client sends a request to the server.
  2. 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.
  3. The client resends the request, this time including its encoded username and password in the Authorization header.
  4. 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:

AdvantageDisadvantage
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:

  1. An application requests an API key from the provider.
  2. The provider generates a unique API key and assigns it to the application.
  3. The application includes this API key in the headers of its requests.
  4. 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.
JSON Web Token (JWT) Structure Header Payload Signature Token type (JWT) Signing algorithm (e.g., HS256) Claims (e.g., user ID, permissions, expiration time) Ensures token integrity Created using secret key Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 Payload: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c The JWT is typically transmitted as a string, with each part Base64Url encoded and separated by dots (.)

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:

  1. The client authenticates with the server, providing credentials (e.g., username/password).
  2. Upon successful authentication, the server generates a JWT containing relevant user information and permissions.
  3. The client receives the JWT and stores it securely (e.g., in local storage).
  4. For subsequent requests, the client includes the JWT in the Authorization header, typically using the Bearer scheme.
  5. 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.
FeatureHTTP Basic AuthenticationAPI KeysJWTs
FormatBase64 encoded username:passwordLong random stringThree-part encoded string
Typical Use CaseSimple authentication for less sensitive appsApplication/client authenticationUser authentication and authorization
TransmissionSent in request headerTypically sent in request header or query parameterSent in request header
StatelessYesYesYes
ExpirationNo built-in expirationNo built-in expirationCan include expiration claim
RevocationRequires changing passwordRequires revoking and reissuing keyRequires maintaining a blacklist or using short expiration times
SecurityLow (unless used with HTTPS)ModerateHigh
Information CapacityLimited to usernameLimitedCan contain arbitrary claims/data
ScalabilityGoodGoodExcellent
Implementation ComplexityLowLowModerate
Self-containedNoNoYes
Suitable forServer-to-server communicationAPI access controlSingle 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.