The first “A” in the Jamstack stands for “APIs” and is a key contributor to what makes working with static sites so powerful. APIs give developers the freedom to offload complexity and provide avenues for including dynamic functionality to an otherwise static site. Often, accessing an API requires validating the authenticity of a request. This frequently manifests in the form of authentication (auth) and can be done either client side or server side depending on the service used and the task being accomplished.
Given the vast spectrum of protocols available, APIs differ in their individual auth implementations. These auth protocols and implementation intricacies add an additional challenge when integrating APIs into a Jamstack site. Thankfully, there is a method to this madness. Every protocol can be mapped to a specific use case and implementing auth is a matter of understanding this.
To illustrate this best, let’s dive into the various protocols and the scenarios that they’re best suited for.
OAuth 2.0 is the general standard by which authentication today follows. OAuth is a fairly flexible authorization framework that constitutes a series of grants defining the relationship between a client and an API endpoint. In an OAuth flow, a client application requests an access token from an authorization endpoint and uses that to sign a request to an API endpoint.
There are four main grant types — authorization code, implicit flow, resource owner credential, and client credentials. We’ll look at each one individually.
Of all OAuth grant types, the Authorization Code Grant is likely the most common one. Primarily used to obtain an access token to authorize API requests after a user explicitly grants permission, this grant flow follows a two-step process.
- First, the user is directed to a consent screen aka the authorization server where they grant the service restricted access to their personal account and data.
- Once permission has been granted, the next step is to retrieve an access token from the authentication server which can then be used to authenticate the request to the API endpoint.
Compared to other grant types, the Authorization Code Grant has an extra layer of security with the added step of asking a user for explicit authorization. This multi-step code exchange means that the access token is never exposed and is always sent via a secure backchannel between an application and auth server. In this way, attackers can’t easily steal an access token by intercepting a request. Google-owned services, like Gmail and Google Calendar, utilize this authorization code flow to access personal content from a user’s account. If you’d like to dig into this workflow more, check out this blog post to learn more.
The Implicit Grant is akin to the Authorization Code Grant with a noticeable difference: instead of having a user grant permission to retrieve an authorization code that is then exchanged for an access token, an access token is returned immediately via the the fragment (hash) part of the redirect URL (a.k.a. the front channel).
With the reduced step of an authorization code, the Implicit Grant flow carries the risk of exposing tokens. The token, by virtue of being embedded directly into the URL (and logged to the browser history), is easily accessible if the redirect is ever intercepted.
Despite its vulnerabilities, the Implicit Grant can be useful for user-agent-based clients like Single Page Applications. Since both application code and storage is easily accessed in client-side rendered applications, there is no safe way to keep client secrets secure. The implicit flow is the logical workaround to this by providing applications a quick and easy way to authenticate a user on the client side. It is also a valid means to navigate CORS issues, especially when using a third-party auth server that doesn’t support cross-origin requests. Because of the inherent risks of exposed tokens with this approach, it’s important to note that access tokens in Implicit Flow tend to be short-lived and refresh tokens are never issued. As a result, this flow may require logging in for every request to a privileged resource.
In the case of the Resource Owner Credential Grant, resource owners send their username and password credentials to the auth server, which then sends back an access token with an optional refresh token. Since resource owner credentials are visible in the auth exchange between client application and authorization server, a trust relationship must exist between resource owner and client application. Though evidently less secure than other grant types, the Resource Owner Credential grant yields an excellent user experience for first party clients. This grant flow is most suitable in cases where the application is highly privileged or when working within a device’s operating system. This authorization flow is often used when other flows are not viable.
The Client Credentials Grant type is used primarily when clients need to obtain an access token outside of the context of a user. This is suitable for machine to machine authentication when a user’s explicit permission cannot be guaranteed for every access to a protected resource. CLIs, and services running in the back end are instances when this grant type comes in handy. Instead of relying on user login, a Client ID and Secret are passed along to obtain a token which can then be used to authenticate an API request.
Typically, in the Client Credential grant, a service account is established through which the application operates and makes API calls. This way, users are not directly involved and applications can still continue to authenticate requests. This workflow is fairly common in situations where applications want access to their own data, e.g. Analytics, rather than to specific user data.
With its reliance on third party services for complex functionality, a well-architected authentication solution is crucial to maintain the security of Jamstack sites. APIs, being the predominant way to exchange data in the Jamstack, are a big part of that. We looked at four different methods for authenticating API requests, each with its benefits and impacts on user experience.
We mentioned at the start that these four are the main forms of authentication that are used to request data from an API. There are plenty of other types as well, which are nicely outlined on oauth.net. The website as a whole is an excellent deep-dive on not only the auth types available, but the OAuth framework as a whole.
Do you prefer one method over another? Do you have an example in use you can point to? Share in the comments!