Introduction And Lesson Overview

Welcome to the first lesson of our course on API Key Authentication & Security. In this lesson, you will learn the basics of generating and securely storing API keys — a fundamental part of building secure web applications and APIs. API keys are unique codes that allow applications or users to access certain features or data. They are widely used to control and monitor access to APIs, and they help keep your services safe from unauthorized use.

By the end of this lesson, you will understand how to generate strong, random API keys, how to store them securely in your database, and how to set up an endpoint for users to create their own API keys. You will also learn about best practices for managing API keys throughout their lifecycle. This knowledge will prepare you for the hands-on exercises that follow, where you will practice these skills in a real coding environment.

Understanding The API Key Model

Let's start by looking at the data model for API keys. In our example, we use JPA (Java Persistence API) with Hibernate to define the ApiKey model. This model represents how API keys are stored in the database. Here is the code for the model:

Each API key has several important properties. The id is a unique identifier for each key. The userId links the key to a specific user in your system using a foreign key relationship. The name allows users to label their keys for easy identification. The stores a hashed version of the , which is important for security (we will discuss this soon). The field lets you enable or disable keys without deleting them, and sets an expiration date for each key. This structure helps you manage safely and efficiently.

Configuring Spring Security For Password Hashing

Before we can generate and hash API keys, we need to set up Spring Security's password hashing functionality. Spring Security provides a PasswordEncoder interface that handles secure hashing using algorithms like BCrypt. To use this in our application, we need to add the Spring Security dependency to our project (this is already included in the CodeSignal environment) and create a configuration class:

The BCryptPasswordEncoder uses the BCrypt hashing algorithm, which is specifically designed for password and key storage. BCrypt is a strong choice because it includes a built-in work factor (also called a cost parameter) that controls how computationally expensive the hashing process is. The default work factor is 10, which means the algorithm performs 2^10 (1,024) iterations. You can increase this value to make hashing slower and more secure:

A higher work factor provides better protection against brute-force attacks because each hashing attempt takes more time. However, it also means legitimate hash generation and verification will be slower. For most applications, a work factor between 10 and 12 provides a good balance between security and performance. As computers become faster over time, you may want to increase this value.

Once this bean is defined, Spring will automatically make it available for dependency injection throughout your application. This allows any controller or service to use the PasswordEncoder by simply declaring it as a constructor parameter or field.

Generating A Secure API Key

When creating an API key, it is important to make sure it is random and hard to guess. In our example, we use Java's SecureRandom class to generate a secure key. We also use Spring's PasswordEncoder that we configured in the previous section. Here is the relevant code:

Notice the private final PasswordEncoder passwordEncoder; field at the top. This is where Spring will inject the PasswordEncoder bean we configured earlier. When Spring creates an instance of our controller class, it will automatically provide the PasswordEncoder implementation through constructor injection (you'll see the full controller constructor later in this lesson). This is one of the key benefits of Spring's dependency injection system — you don't have to manually create or manage these dependencies.

The SecureRandom class generates 32 random bytes, which gives us 256 bits of entropy. This makes the key very difficult to guess or brute-force. We then convert these bytes to a hexadecimal string for easy storage and use. Adding a prefix like helps identify the key as belonging to your application, which can be useful for debugging or logging.

Hashing The API Key For Secure Storage

Storing raw API keys in your database is risky. If your database is ever compromised, attackers could use those keys to access your services. To prevent this, we hash the API key before saving it using the PasswordEncoder we configured earlier:

The passwordEncoder.encode() method uses BCrypt to transform the original key into a fixed-length string that cannot be reversed. BCrypt automatically handles salt generation (random data added to the input) and incorporates the work factor we configured. Even if someone gains access to your database, they cannot recover the original API key from the hash.

When a user presents an API key, you can use passwordEncoder.matches() to verify it against the stored hash:

This comparison is secure because BCrypt applies the same hashing process to the provided key and checks if it matches the stored hash. This is the same approach used for storing passwords securely in web applications.

Creating The API Endpoint For Key Generation

Now let's look at how we allow users to create new API keys. We use a Spring Boot controller that handles POST requests to /api/api-keys/create. This endpoint is protected by JWT authentication, which checks that the user is logged in and authorized. Here is the main part of the controller:

Best Practices In API Key Management

Managing API keys is not just about generating and storing them. You should also think about their lifecycle. Each key in our model has an expiresAt field, which means keys will automatically become invalid after a certain date. The isActive field allows you to deactivate keys without deleting them, which is useful if a key is compromised or is no longer needed.

Here are some tips for managing API keys safely:

  • Always hash API keys before storing them using Spring's PasswordEncoder with BCrypt.
  • Choose an appropriate BCrypt work factor based on your security requirements and performance constraints (typically 10-12).
  • Limit the number of active keys per user to reduce risk.
  • Set expiration dates for keys and encourage users to rotate them regularly.
  • Never log or display the raw API key after creation.
  • Encourage users to store their keys securely, such as in environment variables or secret managers.

Following these practices will help keep your application and your users safe.

Summary And Next Steps

In this lesson, you learned how to configure Spring Security's PasswordEncoder for secure hashing with BCrypt, design a secure API key model using JPA, generate strong random keys with SecureRandom, hash them for safe storage using dependency injection, and create an endpoint for users to generate their own API keys using Spring Boot. You also learned about best practices for managing API keys throughout their lifecycle.

You are now ready to move on to the hands-on practice exercises, where you will apply what you have learned in a real coding environment. Remember, on CodeSignal, all the necessary libraries are pre-installed, so you can focus on writing and testing your code. Great job reaching the end of this lesson! Keep up the good work, and let's continue building your skills in API key authentication and security.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal