In our previous articles, we explored how authentication works using passwords, hashing, sessions, cookies, and JWTs.
But there's another interesting problem in backend development.
Imagine you're building a weather application. Your application needs to retrieve weather information from an external service such as a weather API.
Or perhaps you're building an application that allows users to upload files to a cloud storage service.
How does your application communicate with these external services securely?
How does one application prove that it is allowed to access another application's resources?
This is where concepts like API Keys, Access Tokens, and OAuth 2.0 come into the picture.
Let's understand them from the basics.
1. What Are API Keys?
An API (Application Programming Interface) allows different software applications to communicate with each other.
For example, imagine we have a weather application.
Instead of maintaining our own weather database, we can consume data from an external weather API.
A simplified request might look like this:
GET /current?city=Hyderabad HTTP/1.1 Host: api.weather-service.com But how does the weather service know which application is making this request?
How does it identify the application and enforce its usage limits?
One common approach is an API Key.
An API key is a unique string issued by an API provider to identify a client application or project.
For example:
API_KEY=sk_example_123456789 The application includes this key when making requests to the API.
One possible implementation is through an HTTP header:
GET /current?city=Hyderabad HTTP/1.1 Host: api.weather-service.com X-API-Key: your_api_key The server extracts the key and checks whether it is valid.
If the key is valid and the application is allowed to access the requested resource, the server processes the request.
How API Key Authentication Works
Let's break down the process.
Developer | | 1. Registers application v API Provider | | 2. Generates API Key v Developer's App | | 3. Sends API request | with API Key v API Provider | | 4. Validates API Key | v API Response For example:
- A developer creates an account with an API provider.
- The provider generates an API key.
- The developer stores the key securely.
- The application includes the key in API requests.
- The provider validates the key and applies the associated access policies.
Where Are API Keys Used?
API keys are commonly used for:
- Accessing weather APIs.
- Integrating mapping services.
- Connecting to third-party data providers.
- Identifying applications consuming public APIs.
- Enforcing usage quotas and rate limits.
However, API keys are not a complete authentication or authorization solution for every use case.
They primarily identify the application or project associated with the key. They do not automatically establish the identity of an individual user.
2. API Key vs Access Token
You might wonder whether an API key and an access token are the same thing.
They can look similar because both are usually represented as strings.
However, they serve different purposes.
| API Key | Access Token |
|---|---|
| Commonly identifies an application or project. | Represents an authorized grant of access. |
| Often long-lived until rotated or revoked. | Usually has a defined lifetime. |
| Commonly used for application-level API access. | Commonly used to access protected resources. |
| May provide broad access depending on configuration. | Can be restricted by scopes and permissions. |
An access token is a credential presented to a resource server to access protected resources.
For example, a user might authorize an application to read their cloud storage files.
The application receives an access token that represents this granted access.
The token can then be used when making requests to the relevant API.
GET /files Authorization: Bearer ACCESS_TOKEN The resource server validates the token and determines whether it grants the requested access.
Important: API keys can also be scoped, restricted, and rotated. Similarly, not every access token is necessarily a JWT. The format and security properties depend on the provider and implementation.
3. The Problem: Sharing Passwords Between Applications
Let's consider another scenario.
You're building a task management application and want users to connect their cloud storage accounts.
Your application needs permission to access some of their information.
One approach would be to ask users for their account email and password.
But this creates several problems:
- Your application would need to handle users' passwords.
- Users would have to trust your application with their credentials.
- Your application might gain more access than it actually needs.
- Revoking access would become difficult.
- A security breach could expose credentials used across multiple applications.
There should be a better way.
Instead of asking users for their passwords, applications should be able to request permission to access specific resources.
This is the problem that OAuth 2.0 helps solve.
4. What Is OAuth 2.0?
OAuth 2.0 is an authorization framework that allows an application to obtain limited access to resources on behalf of a user or another client.
The important distinction is that OAuth 2.0 is primarily about authorization, not authentication.
It allows users to grant an application permission to access specific resources without sharing their account password with that application.
For example:
Imagine you are building a photo editing application.
A user wants to import images from their cloud storage account.
Instead of asking for their password, your application redirects them to the provider's authorization page.
The user signs in and reviews the permissions being requested.
If they approve, the authorization server issues an authorization response that allows your application to obtain an access token.
Your application can then use that token to access the resources permitted by the granted scopes.
The application never needs to know the user's account password.
A Real-World Example
Consider the familiar Sign in with Google button.
When a user clicks this button, the application can initiate an authorization flow.
The user is redirected to Google's authorization server, where they can authenticate and grant the requested permissions.
One important detail: when the goal is to authenticate a user and obtain reliable identity information, applications generally use OpenID Connect (OIDC) on top of OAuth 2.0.
We'll explore that distinction later.
5. The Four Main Roles in OAuth 2.0
Before understanding the authorization flow, let's understand the four important roles.
1. Resource Owner
The resource owner is the entity that owns the protected resources.
In a typical user-facing application, this is the user.
Example: A user who owns files stored in a cloud storage service.
2. Client
The client is the application requesting access to protected resources.
Example: Your photo editing application.
3. Authorization Server
The authorization server authenticates the resource owner when necessary, obtains authorization, and issues tokens to the client.
Example: The identity provider's authorization service.
4. Resource Server
The resource server hosts the protected resources and accepts valid access tokens.
Example: The cloud storage API.
Here's how they relate:
+------------------+ | Resource Owner | | (User) | +------------------+ | | Grants permission v +------------------+ +-------------------+ | Client |------>| Authorization | | (Your App) | | Server | +------------------+ +-------------------+ | | | | Issues token | | v v +------------------+ +-------------------+ | Resource Server |<------| Access Token | | (Storage API) | +-------------------+ +------------------+ The diagram is simplified, but it captures the main responsibilities of each component.
6. Understanding the OAuth 2.0 Authorization Code Flow
OAuth 2.0 supports multiple authorization grant types.
For modern web and mobile applications, the Authorization Code Flow with PKCE is a widely recommended approach.
PKCE stands for Proof Key for Code Exchange.
Let's understand the flow step by step.
Imagine our application wants permission to read a user's cloud storage files.
Step 1: The User Initiates Authorization
The user clicks:
Connect Cloud Storage Our application redirects the browser to the authorization server.
The authorization request contains parameters such as:
response_type=code client_id=YOUR_CLIENT_ID redirect_uri=https://example.com/callback scope=files.read state=RANDOM_STATE code_challenge=GENERATED_CHALLENGE code_challenge_method=S256 Let's understand the important parameters.
| Parameter | Purpose |
|---|---|
| response_type | Specifies that the client wants an authorization code. |
| client_id | Identifies the application. |
| redirect_uri | Specifies where the authorization server should send the response. |
| scope | Defines the requested permissions. |
| state | Helps protect against cross-site request forgery and request substitution. |
| code_challenge | A transformed version of a client-generated secret used by PKCE. |
| code_challenge_method | Specifies how the challenge was generated. |
The actual scopes and supported parameters depend on the provider.
Step 2: User Grants Permission
The authorization server displays a consent screen.
For example:
+--------------------------------------+ | Connect Cloud Storage | | | | This application wants to: | | | | ✓ View your stored files | | | | [ Allow ] [ Deny ] | +--------------------------------------+ The user reviews the requested permissions.
If they approve, the authorization server continues the flow.
Step 3: Authorization Code Is Returned
The authorization server redirects the browser to the registered callback URL.
For example:
https://example.com/callback?code=AUTH_CODE&state=RANDOM_STATE The application receives an authorization code.
This code is not the access token.
It is a short-lived credential that the client can exchange for tokens.
The application should also validate the returned state value against the value it originally generated.
Step 4: Exchange Authorization Code for Tokens
The client sends a request to the authorization server's token endpoint.
A simplified example:
POST /token HTTP/1.1 Host: authorization-server.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=AUTH_CODE &redirect_uri=https://example.com/callback &client_id=YOUR_CLIENT_ID &code_verifier=ORIGINAL_CODE_VERIFIER The authorization server validates the authorization code and PKCE verifier.
For confidential clients, client authentication is also required.
If everything is valid, the server returns tokens.
Example response:
{ "access_token": "ACCESS_TOKEN", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "REFRESH_TOKEN", "scope": "files.read" } The exact response fields depend on the provider and grant.
Step 5: Access Protected Resources
Now the application can use the access token to access the permitted resources.
For example:
GET /files Authorization: Bearer ACCESS_TOKEN The resource server validates the token and checks whether it grants the requested permission.
If everything is valid, the API returns the requested data.
Complete Authorization Code Flow
Let's put everything together:
User | | 1. Clicks Connect v Client Application | | 2. Redirects to authorization server v Authorization Server | | 3. User authenticates if needed | 4. User grants permission | | 5. Returns authorization code v Client Application | | 6. Exchanges code + PKCE verifier v Authorization Server | | 7. Issues access token v Client Application | | 8. Sends access token v Resource Server | | 9. Validates token and permissions v Protected Resource The main idea is that the application obtains limited access without ever handling the user's account password.
7. What Are OAuth Scopes?
During the authorization flow, you might have noticed a parameter called scope.
A scope represents a requested level of access.
For example, imagine an application integrating with a file storage service.
It might request:
files.read This could allow the application to read files.
Alternatively:
files.write This could allow it to modify files.
A broader permission might allow access to both.
The actual scope names and their meaning are defined by the API provider.
Why Are Scopes Important?
Suppose your application only needs to display a user's files.
It shouldn't request permission to delete or modify them.
This follows the Principle of Least Privilege.
The principle states that an application should receive only the permissions necessary to perform its intended task.
Requesting fewer permissions reduces the potential damage if the application or its credentials are compromised.
8. OAuth 2.0 Grant Types
OAuth 2.0 supports different ways for clients to obtain authorization.
Some important grant types include:
| Grant Type | Typical Use Case |
|---|---|
| Authorization Code | User-facing applications accessing resources on behalf of users. |
| Authorization Code with PKCE | Recommended for public clients and commonly used by modern web and mobile applications. |
| Client Credentials | Machine-to-machine communication without a user acting as the resource owner. |
| Refresh Token | Obtaining new access tokens using a previously issued refresh token. |
What About the Implicit Grant?
You might come across something called the Implicit Grant.
It was historically used by browser-based applications to obtain access tokens directly from the authorization endpoint.
However, it is no longer recommended for modern applications.
The Authorization Code Flow with PKCE is generally preferred.
What About the Password Grant?
The Resource Owner Password Credentials Grant allowed applications to collect a user's username and password and exchange them for tokens.
This grant is also no longer recommended.
Modern applications should use authorization flows that avoid sharing user passwords with the client application.
9. OAuth 2.0 vs OpenID Connect
This is one of the most important distinctions to understand.
We often hear people say:
Login with Google uses OAuth. While OAuth 2.0 is involved in many such integrations, OAuth 2.0 itself is an authorization framework.
It does not define a standard way for a client to verify a user's identity.
This is where OpenID Connect (OIDC) comes in.
OIDC is an identity layer built on top of OAuth 2.0.
It introduces an ID Token, which contains information about the authenticated user.
For example:
{ "iss": "https://identity-provider.com", "sub": "user123", "aud": "my-client-id", "exp": 1800000000 } The ID Token is generally a JWT containing identity claims.
The client validates the token according to OIDC requirements to establish the user's identity.
OAuth vs OIDC
| OAuth 2.0 | OpenID Connect |
|---|---|
| Authorization framework. | Identity layer built on OAuth 2.0. |
| Used to obtain access to protected resources. | Used to authenticate users. |
| Defines access tokens. | Adds ID Tokens. |
| Uses scopes for requested access. | Defines standard identity scopes and claims. |
A simple way to remember this:
- OAuth 2.0: What is this application allowed to access?
- OpenID Connect: Who is the user who authenticated?
10. When Should You Use API Keys or OAuth?
Let's consider a few practical scenarios.
| Scenario | Suitable Approach |
|---|---|
| Accessing a weather API from a backend | API Key |
| Identifying an application consuming a public API | API Key |
| Allowing users to connect their cloud storage accounts | OAuth 2.0 |
| Allowing a third-party application to access user resources | OAuth 2.0 |
| Backend service communicating with another backend service | Client Credentials Grant |
| Implementing Sign in with Google | OpenID Connect |
These are common patterns rather than strict rules. The API provider's security model and documentation determine the actual integration.
11. Security Best Practices
Using API keys and OAuth doesn't automatically make an application secure.
Developers still need to follow important security practices.
API Key Security
- Never expose secret API keys in public repositories.
- Store sensitive keys in environment variables or a secure secrets manager.
- Restrict API keys by API, origin, IP address, or other available controls.
- Rotate keys periodically and immediately after suspected compromise.
- Apply usage quotas and rate limits.
- Avoid sending API keys through URLs when a secure header-based method is supported.
OAuth Security
- Use Authorization Code Flow with PKCE for appropriate clients.
- Validate the
stateparameter. - Register exact redirect URIs and validate them.
- Use HTTPS for authorization and token communication.
- Request only necessary scopes.
- Protect access tokens and refresh tokens.
- Store client secrets securely in confidential clients.
- Validate tokens and relevant claims at the resource server.
- Implement refresh-token rotation and revocation where supported.
- Never assume that a token is trustworthy just because it is a JWT.
Conclusion
We've explored how applications communicate securely with external services and how authorization can be delegated without sharing user passwords.
Here's what we learned:
- API Keys: Identify applications or projects accessing an API.
- Access Tokens: Represent granted access to protected resources.
- OAuth 2.0: Allows applications to obtain limited authorization.
- Scopes: Define the permissions an application requests.
- Authorization Code Flow: Allows applications to obtain tokens through a user-approved authorization process.
- PKCE: Protects authorization code exchanges, especially for public clients.
- OpenID Connect: Adds a standard identity layer on top of OAuth 2.0.
These concepts are essential when building applications that integrate with external services, third-party APIs, and identity providers.
Understanding them also gives us a foundation for working with distributed systems and modern application architectures.
What's Next?
We've now explored the fundamentals of authentication and authorization, including:
- Password hashing and secure credential storage.
- Sessions and cookies.
- JWTs and token-based authentication.
- API keys and access tokens.
- OAuth 2.0 and OpenID Connect.
- Authorization flows, scopes, and security practices.
Together, these concepts help us understand how applications identify users, manage access, and communicate securely with external services.
But securing an application is only one part of backend development.
Another equally important responsibility is ensuring that the data entering our application is correct, consistent, and usable.
In our next article, we'll explore Server-Side Validation and Data Transformation.
We'll understand how backend applications validate incoming data and transform it into the structure required by business logic and database operations.


