Skip to main content
RESTful Principles
CHAPTER 14 Beginner

API Keys and Token Authentication

Updated: May 13, 2026
5 min read

# CHAPTER 14

API Keys and Token Authentication

1. Introduction

We know that REST APIs require the client to present an "ID card" with every request. In Chapter 14, we will learn how to actually build this system from scratch using PHP and MySQL. We will explore two of the most common methods: API Keys (for server-to-server or application-level access) and Bearer Tokens (for user-level access). We will cover database design, token generation, and the validation process.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Design a MySQL database schema to store API tokens.
  • Generate secure, random API keys using PHP.
  • Implement an authentication middleware/check in your PHP script.
  • Understand the difference between Application API Keys and User Access Tokens.
  • Authenticate requests by validating tokens against the database.

3. Beginner-Friendly Explanation

  • API Key: Think of this as a VIP pass given to a specific *company* or *app*. For example, when your server talks to Stripe to process payments, it uses an API Key. It represents the app, not a specific human.
  • User Token: Think of this as a hotel room key given to a specific *human*. When you log into the mobile app, you get a token. It represents *you*.

Both work the exact same way technically: they are long, random strings of text checked against a database.

4. Real-World Examples

  • API Key: Google Maps API. You generate an API key in the Google Console and hardcode it into your website so your site is allowed to load the map.
  • User Token: GitHub Personal Access Tokens. You generate a token on GitHub and use it in your terminal to push code, acting on behalf of your user account.

5. Detailed Code Examples

Let's build a basic User Token authentication system.

1. Database Schema (users table):

sql
123456
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    password_hash VARCHAR(255),
    api_token VARCHAR(100) UNIQUE NULL
);

2. Generating a Token on Login (login.php):

php
12345678910111213141516
<?php
// ... assume we checked $_POST['username'] and password_verify() succeeded ...

// Generate a secure 64-character random string
$newToken = bin2hex(random_bytes(32));

// Save it to the database for this user
$stmt = $pdo->prepare("UPDATE users SET api_token = :token WHERE id = :id");
$stmt->execute([&#039;:token' => $newToken, ':id' => $user['id']]);

// Return the token to the client
echo json_encode([
    "message" => "Login successful",
    "token" => $newToken
]);
?>

3. Validating the Token on a Protected Route (profile.php):

php
123456789101112131415161718192021222324252627
<?php
$headers = getallheaders();
$authHeader = $headers[&#039;Authorization'] ?? '';

// Extract the token from "Bearer xyz123..."
if (preg_match(&#039;/Bearer\s(\S+)/', $authHeader, $matches)) {
    $clientToken = $matches[1];

    // Check database
    $stmt = $pdo->prepare("SELECT id, username FROM users WHERE api_token = :token");
    $stmt->execute([&#039;:token' => $clientToken]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($user) {
        // Success! We know exactly who this is.
        echo json_encode(["message" => "Welcome, " . $user[&#039;username']]);
    } else {
        // Token provided, but not found in DB
        http_response_code(401);
        echo json_encode(["error" => "Invalid Token"]);
    }
} else {
    // No token provided at all
    http_response_code(401);
    echo json_encode(["error" => "Authorization header missing or malformed"]);
}
?>

6. Request/Response Examples

Protected Request:
http
123
GET /profile.php HTTP/1.1
Host: api.myapp.com
Authorization: Bearer 8f7d9a8c7b6...

Successful Response:

json
123
{
  "message": "Welcome, admin"
}

7. HTTP Examples

Sometimes, simple API Keys are passed using a custom header rather than the standard Bearer format.
http
12
GET /data HTTP/1.1
X-API-Key: my_super_secret_app_key_99

*Note: While common for server-to-server APIs, Bearer tokens are preferred for user authentication.*

8. JSON Examples

When a user logs out, the API should "revoke" the token by setting it to NULL in the database.
json
1234
{
  "success": true,
  "message": "Successfully logged out. Token revoked."
}

9. Best Practices

  • Use randombytes(): Never use rand(), mtrand(), or md5(time()) to generate tokens. They are predictable and easily hacked. Always use a cryptographically secure function like randombytes().
  • Database Indexing: Ensure your apitoken column in MySQL has a UNIQUE INDEX. Since every single API request requires a WHERE apitoken = ? lookup, an index will make this query lightning fast.

10. Common Mistakes

  • Storing API Keys in Frontend Code: If you build a React or Vue app, do NOT hardcode your secret API keys in the JavaScript. Anyone can open Chrome DevTools and steal them.
  • Not revoking tokens: If a user resets their password, you should regenerate or revoke their API token. Otherwise, an attacker who stole the token can still access the account!

11. Mini Exercises

  1. 1. What PHP function is used to convert binary data (from randombytes) into a readable hexadecimal string?
*(Answer: bin2hex())*

12. Coding Challenges

Challenge 1: Write a PHP snippet that takes the Authorization header, checks if it starts with "Bearer ", and extracts just the token string into a variable called $token.

13. MCQs with Answers

Question 1

What is the most secure way to generate a random token in PHP?

Question 2

When authenticating a request, how does the server know WHICH user the token belongs to?

Question 3

Why should the apitoken column in MySQL be indexed?

14. Interview Questions

  • Q: Walk me through the database flow of a token-based login system, from the /login request to a subsequent protected /profile request.
  • Q: What is the difference between an API Key and a User Bearer Token?
  • Q: How do you securely handle "Log Out" in a stateless API?

15. FAQs

Q: Doesn't looking up the token in the database on EVERY request slow down the server? A: Yes, it is a database hit on every request. For small to medium apps, an indexed MySQL lookup takes less than 1 millisecond, which is fine. However, for massive apps like Netflix, hitting the database on every request is too slow. They use caching (like Redis) or JWTs (which we cover next!).

16. Summary

In Chapter 14, we built a working authentication system. We generated a secure random token using PHP's random_bytes(), saved it to the database, and required the client to pass it via the Authorization: Bearer header. We then learned how to extract that header, query the database, and verify the user's identity statelessly.

17. Next Chapter Recommendation

Database token lookups are great, but what if we could verify a user *without* ever querying the database? This magical feat is possible with JSON Web Tokens. Proceed to Chapter 15: JWT Authentication to learn the industry standard for scalable API security.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·