Auth & identity
December 26, 2023
Author: Isaac Ejeh
Today, we’re experiencing the exciting possibilities of being interconnected more than ever. Our devices and networks communicate with each other every second, generating billions of requests and exchanging large gigabytes of data — all without human intervention.
Machine-to-machine (M2M) communication is transforming how modern applications and devices deliver real-time product experiences. From the millions of IoT devices now connected to the internet to the search engines that process billions of search queries every day, computer networks are connecting humans and machines in unimaginable ways.
As such, our traditional methods of protecting machines and service accounts using usernames and passwords may not be the most secure way to govern how autonomous systems communicate, especially at scale.
To address this concern, machine-to-machine (M2M) applications now adopt the Client Credentials Flow, which is defined in the OAuth 2.0 RFC 6749 authorization protocol. If you aren’t familiar with the OAuth 2.0 standard, do not worry, we’ll go over this protocol as simply as possible later in this article.
For now, let’s assume we have two backend services that are trying to communicate using the client credentials flow. One service, the Client, needs to access information from the other service, the Resource.
This approach enables the Client to use its unique Client ID and Client Secret to authenticate its identity with an OAuth 2.0 Authorization server, such as Stytch. If the credentials are valid and the Resource grants consent, the Authorization server will issue an Access Token to the Client. This access token then serves as the authorization grant that gives the Client access to specific protected resources or services provided by the Resource.
In this article, we’ll explore what machine-to-machine authentication and authorization is, how it works, discuss M2M implementation best practices, and where all of this might fit within your application’s security stack.
Machine-to-machine (M2M) communication is the autonomous exchange of data between machines, devices, or networks without the need for human involvement.
When it comes to M2M communication, the term “machine” refers to any autonomous system or agent that can send or receive information within a network. This encompasses physical servers, computers, IoT devices, as well as workloads like your automation scripts, Docker containers, K8s clusters, and mobile/web applications.
Before machines can securely exchange information, they must first verify their identities. This is where machine-to-machine (M2M) authentication and authorization come into play.
Developers often use the terms “M2M authentication” and “M2M authorization” interchangeably. But really, do these terms mean the same thing?
Let’s assume you are at the airport, preparing to board a flight. Before you can board the plane (authorization), you must first prove your identity as a human by presenting your passport and booking details (authentication). Once your identity has been verified, you will be issued a boarding pass (authorization) that grants you access to your assigned seat (resources) based on your booking information. Your boarding pass (authorization) determines whether you will fly comfortably in business class or economy.
The same idea applies to machines. Authentication verifies the identity of a machine, while authorization determines what the authenticated machine is allowed to do or access. Before authorization can happen, authentication must have taken place.
Machine-to-machine (M2M) authentication is the process of verifying the identity of a machine, whether it’s a hardware device or a software program. When a machine attempts to communicate securely with another machine, the Client must first authenticate its identity credentials before it can access the other machine which is the Resource.
Machine-to-machine (M2M) authorization is the process of providing authenticated machines with secure access to specific resources using cryptographically signed JSON web tokens (JWTs). These access tokens contain information such as the machine’s identity, the expiration date of the token, and the list of resources that the machine is authorized to access. With M2M authorization, machines can communicate autonomously, execute core business functions, and access protected resources at a granular level, all without human users.
Software machines such as APIs, servers, and other workloads rely on client credentials and JWT-based access tokens to securely exchange data and functionality, without exposing key resources to potential bad actors. This is based on the Client Credentials grant type as defined in the OAuth 2.0 protocol.
Hardware machines, such as sensors, smart wearables, and other physical devices, use various M2M authentication and authorization methods to prove their identity before connecting to other devices or networks. These methods include shared secrets, digital certificates, cryptographic identifiers, and established protocols such as MQTT (Message Queuing Telemetry Transport), CoAP (Constrained Application Protocol), OPC UA (OPC Unified Architecture), and RADIUS (Remote Authentication Dial-In User Service).
OAuth 2.0 is the industry-standard framework for authorization and access control in machine-to-machine (M2M) communication. The OAuth 2.0 protocol defines the steps that a machine (the Client) must follow to securely access protected resources or functionality from another machine (the Resource), using the Client Credentials grant flow.
The OAuth 2.0 Client Credentials grant type is ideal for securing autonomous interactions in machine-to-machine (M2M) applications, such as APIs, backend services, servers, background processes, or command line interfaces (CLIs) that don’t require human users.
In addition to the client credential grant type, the OAuth 2.0 authorization framework supports several other grant types to address different authorization use cases. For example, the authorization code grant type is used when applications require end users to authenticate their identities with the authorization server. The device code grant type is used to authorize devices that lack a browser or have limited input capabilities, such as smart TVs or IoT (Internet of Things) devices. Other grant types are also available.
The Client Credentials grant flow, as defined in section 4.4 of the OAuth 2.0 RFC 6749 standard, details the steps that client applications must take to access protected resources.
Client applications must exchange their client credentials, such as a client ID and client secret, for an OAuth token that authorizes the client to access some specified resource. The client application must authenticate its credentials with an OAuth 2.0 authorization server, which can be powered by Stytch or another identity provider that supports the OAuth 2.0 standard.
When a Client application requests access to a protected resource, it must include the scope of resources that it requires from the Resource in the POST request that it sends to the Authorization server. A scope can be a string (claim) or a list of strings or claims that defines the functionality or resources that a client needs access to.
When it comes to securing machine-to-machine (M2M) APIs, JSON Web Tokens (JWTs) are generally considered to be more secure and standardized than API keys. However, the best option for your specific use case will depend on several factors, including the type of application you are building, your security requirements, API architecture, and risk tolerance.
Before now, API keys were the standard way to manage access control between internal and/or external APIs across different companies communicating. However, as our security needs have evolved with the internet, JWTs in OAuth 2.0 are now considered a more secure and developer-friendly way to manage APIs.
If your API only needs to grant basic read access to non-sensitive information and doesn’t require granular permissions and access control, API keys are easier to implement and manage than JWTs.
However, if your API serves multiple clients, such as an external-facing API or in a microservices architecture, OAuth 2.0 JWTs are a more secure and scalable alternative to API keys. This is because JWTs are signed, self-contained tokens that can provide granular levels of permissions for each tenant without the need for multiple API calls.
JSON Web Tokens (JWTs) are a URL-safe means of sharing information as JSON objects, as defined by the OAuth 2.0 RFC 7519 standard.
JWTs are self-contained tokens structured in three parts: the header, the payload, and the signature. The header defines the type of token (JWT) and the hashing algorithm used to sign it, the payload holds the claims (issuer, audience, expiration, etc.), and the signature verifies the token’s integrity.
When a client API sends a request to a server API, it includes the JWT that it must have obtained from the OAuth 2.0 authorization server in the authorization header of its API request. The server API then validates the JWT’s signature and uses the specified claims in the JWT’s payload to either authorize or deny the request.
Securing APIs using JWTs is very different from using API keys or traditional service accounts. While service accounts and API keys are easier to set up for static access control, they lack the dynamic authorization and security capabilities of JWTs. This is why JWTs are becoming the most popular choice for protecting APIs in modern architectures.
API keys are a string of alphanumeric characters that are issued to client APIs. They serve as the client’s unique identifier during authentication with the server API that issued the key.
When a client API makes a request to a server API, the client must include its API key in the header, query parameter, or body of that API request. The most secure way to do this is to include the API key in the HTTP header.
Upon receiving the request, the server API gateway will verify the identity of the client API and grant the request if the API key is valid. Unlike JWTs, API keys can’t hold any information or claims other than the string that uniquely identifies the client. You may need to make additional authorization requests to a centralized server to determine the specific scopes of access granted to the client, depending on how the API has been implemented.
On the other hand, API keys are not cryptographically signed like JWTs, so their security relies on how well they are managed to prevent leakage or unauthorized access. Another concerning security risk with API keys is their long lifespan. If an API key is compromised, it can be used to access sensitive resources and perform unauthorized actions for a long time before you realize the breach and revoke the key.
Identifying a compromised API key can be challenging, especially if the API is not used frequently or for activities that raise immediate red flags. Attackers can often operate stealthily, making it harder to detect their unauthorized access.
These days, most SaaS tech companies and applications are either built on modular monoliths or microservices or are in the process of migrating to one of these architectures.
In all of these architectures, when two backend services communicate, the client service must be authenticated before it can access any resource from the other service. This is important because each service is responsible for a specific business domain and may be accessed by multiple other services, either synchronously or asynchronously.
Imagine you are a hungry DoorDash customer who has just placed an order for lunch from your favorite restaurant and is patiently waiting for the driver to deliver. If DoorDash runs on a microservices architecture, which backend services do you think would need to securely communicate to ensure your order arrives at your doorstep as quickly as possible?
When you place an order, the order management service would need to securely communicate with the user management service, the inventory management service, the payment management service, the driver management service, the notification service, and most likely other services we aren’t considering here. To secure communication between all these backend services at DoorDash, each service must be authenticated and authorized using the OAuth 2.0 client credentials grant flow.
In contrast to microservices architectures which run on multiple servers, modular monolithic architectures typically run on a single server. However, you can still set up an OAuth 2.0 authorization middleware between your other backend services or modules to secure how they communicate and access resources.
In software automation, cronjobs and background processes are essential tools for automating pre-defined tasks and processes. Cronjobs are scheduled tasks that are executed at specific intervals, initiated by the system’s cron daemon in Unix-like operating systems. Background processes, on the other hand, are tasks that run continuously in the background, often initiated by the system’s boot sequence or by specific system events.
These scripts and commands, often referred to as daemons, can perform background tasks that range from managing network requests and system resources to scheduled maintenance activities like database backups and even data synchronization between servers.
Daemons run autonomously and interface with various system resources without human supervision, making them vulnerable to attack if not properly secured. Daemons may need to access system memory or communicate with other daemons and programs just to perform their tasks. As a result, M2M authentication, encryption protocols, and other authorized communication channels can be used to ensure daemons only have access to the system resources they require.
M2M and IoT devices leverage several technologies to securely exchange information, including sensors, wired or wireless networks, communication and encryption protocols, and gateway servers. This creates an interconnected network of smart devices that can make autonomous decisions without human input.
Most M2M and IoT devices communicate with each other and the internet using wireless network channels, such as Wi-Fi, Bluetooth, RFID, BLE, NFC, and secure M2M communication protocols like MQTT, CoAP, OPC UA, and RADIUS.
Let’s take your living room, for example. Imagine you own a smart thermostat that can communicate with your smart air conditioner. The thermostat continuously monitors the temperature in your room using built-in sensors, while the air conditioner is a self-regulating device that can receive commands from the thermostat.
If your room temperature goes above or below a limit you set — for instance, if it gets too hot — the thermostat can instruct the smart air conditioner to start cooling. Once the room cools to your desired temperature, the thermostat can send another signal to the air conditioner to stop cooling.
In this example of M2M communication, the smart thermostat and the smart air conditioner communicate over secure network channels and protocols to maintain your desired room temperature without requiring any input from you for each adjustment.
The best way to secure protected resources from unauthorized access is to encrypt all communications between your servers, APIs, and intermediary services, and implement the OAuth 2.0 authorization protocol. As you already know, the OAuth 2.0 standard is the most comprehensive industry-wide authorization framework that defines how machines can securely communicate without compromising the confidentiality and integrity of the data being shared.
You can encrypt your communication channels using different industry-standard protocols such as transport layer security (TLS) and secure sockets layer (SSL) which are cryptographic protocols that encrypt data sent over a network, rendering it unreadable to any attacker. If you require both parties to verify each other’s identities (mutual two-way authentication), you can always use an authorized communication channel like mutual TLS (mTLS).
Using the same client credentials across multiple services can be a potential security risk. If an attacker compromises one service, they may be able to use the same shared credentials to access other services.
To mitigate this risk, it’s best to create and use unique client IDs and client secrets for each service. In this way, if one service is compromised, the damage will be localized to that service alone. You can use a secure centralized vault that is only accessible by administrators to maintain a comprehensive list of all your active M2M clients, along with their credentials. You can always update this list with the latest information about existing or new client identities.
It’s important to have a credential rotation policy that governs how you rotate client credentials. This will help to reduce your exposure window in the event of a potential breach or unauthorized access. However, the intervals at which you rotate different secrets should be determined by the security policies of your company or industry, as well as the importance of the resource or functionality being accessed.
In addition to rotating credentials, you can use short-lived access tokens and refresh tokens to control the validity of your tokens as needed. You can also implement logging and monitoring tools to actively track API calls, and analyze system logs and audit trails to detect suspicious activity and anomalous access patterns.
When granting access to critical services, you should always adhere to the principle of least privilege. This means that you should only grant each service or entity the necessary permissions it needs to perform its specific tasks. This approach helps mitigate the potential risks associated with over-privileged services, ensuring that the damage is minimized if a security breach occurs.
When granting access to third-party APIs and external systems, you should only grant the minimum level of access necessary to execute their tasks. After the task or collaboration is complete, always revoke their credentials and access tokens.
Whether you need to authenticate between APIs, servers, or services, Stytch has got you covered. We provide easy-to-implement M2M authorization solutions for both startups and enterprises that need to protect their businesses from unauthorized access and attacks.
Stytch’s M2M auth product is built on the OAuth 2.0 open protocol, which is the industry standard for securing access to APIs and other machines. We provide several REST endpoints that you can use to create secure M2M applications, request and authenticate JWT access tokens, and define permissions with granular scopes.
Stytch uses the RS256 hashing algorithm to sign all M2M access tokens (JWTs) using your Stytch project’s JWKS (JSON Web Key Set). And you can validate these access tokens using any of Stytch’s backend SDKs, or locally using any library that supports the JWT protocol.
We also provide endpoints that let you update your M2M clients, rotate your client secrets at intervals, specify the validity period of any authorization token, and even delete M2M clients if necessary. These endpoints give you the flexibility and control you need to effectively manage your M2M clients.
And what’s more, if you already have pre-existing M2M clients with other identity providers, such as Auth0, you can import them into Stytch without having to reassign credentials and scopes.