A guide to creating a solution that avoids security pitfalls, scales well to many components, is easily extendable and requires only simple code.
Organizations providing digital services most commonly secure their applications and APIs using OAuth 2.0 and OpenID Connect. One benefit of this approach is externalizing difficult security operations, such as user credential management, from applications.
Often, developers are first introduced to OAuth when integrating social logins. This typically involves plugging a library into the app, then writing a few lines of code to redirect to a provider such as Google or Facebook, after which tokens are returned to the application:
This seems like a far more attractive option when compared to older website architectures, where apps had to store user passwords and implement password recovery or password policy features. Yet a simple user login is only a small part of the end-to-end security life cycle for an application.
There are several architecture and security pitfalls when using social logins. Therefore, in this article, I will point out the most common issues. I will then show how to implement a social login solution in the best way possible. The end result will be a solution that scales well to many components, is easily extendable and requires only simple code.
Designing API Credentials
After authenticating a user, the next objective is to create a secure session with a backend. These days, the frontend usually calls backend APIs, so an API message credential is required. When developers are new to OAuth, they naturally expect to use one of the tokens received from the social provider.
The tokens received will be an ID token, an access token and optionally a refresh token. The OpenID Connect standard mandates that the ID token is always in the JSON web token (JWT) format. The access and refresh tokens are not commonly JWTs, though. They are designed to enable access to the user’s resources from the social provider, such as Facebook posts.
Therefore, if a developer tries to use the standard OAuth 2.0 behavior of sending access tokens to APIs, it might not be possible to secure the request. Instead, an inexperienced developer might attempt to resolve this by sending the ID token to APIs. Since social providers offer endpoints to validate the ID token, the following flow can be implemented successfully if the API uses a security library with support for validating JWTs:
Yet, an ID token should not be used like this. In OpenID Connect, the ID token represents proof of the authentication event and informs the client application how and when authentication occurred. It should be stored by the client and not sent to any remote endpoints. It is not designed for authorization in APIs.
The missing ingredient here is that access tokens used to secure APIs must be issued by the same organization that provides the APIs. This enables the user identities, scopes and claims, and token lifetimes to be controlled. The API can then authorize requests for data correctly. Foreign tokens issued by another organization, including social providers, should never be used to secure your APIs.
Custom Token Issuing
Once this is understood, the next implementation step might be to validate the ID token as proof, then issue custom tokens in the backend, which are then returned to the OAuth client. This is closer to how standard OAuth and OpenID Connect work. A homegrown implementation might be named a token service, as depicted below. Its role would be to issue access tokens to clients, which can then be sent to the organization’s APIs.
The overall shape of the security solution is now on a better track. Yet there are a number of limitations compared to a full OAuth solution. Firstly, every time a new authentication method, such as a new social provider, is integrated, the application and the token service must be changed and must handle any security nuances. This adds considerable complexity as the architecture grows and is unlikely to cope well with aspects such as multifactor authentication (MFA).
Secondly, security best practices are unlikely to be followed, leading to weaknesses. A quick social login implementation may use a public client that receives tokens without an OAuth client credential and reveals them to the browser. This is not in line with the latest recommendations from OAuth for browser-based apps.
On the API side of the architecture, multiple token types should be used. JWT access tokens are designed only to be used within the backend environment. Internet clients should instead receive confidential, opaque access tokens as a privacy best practice.
Another difficulty is that each social provider will issue a different value for a user’s identity in their token’s subject claim. If a user authenticates in multiple ways, there is a risk that this can lead to duplicate identities in business data. Most organizations will struggle to manage these API behaviors correctly.
The Authorization Server
The original OAuth 2.0 specification introduces the core security component in this architecture, the authorization server. Modern implementations support many additional security standards, including OpenID Connect. When using an authorization server, the application components no longer integrate with social login providers directly.
Instead, each application implements a code flow and only ever interacts with the authorization server. This mechanism supports any possible type of authentication, including MFA and completely bespoke methods. After authentication, account linking can be used to ensure a consistent identity in access tokens received by APIs. How tokens are issued provides control over token formats, claims and lifetimes. There is also a built-in token-signing key management and renewal solution:
Social providers enable a user-friendly way to manage logins for many types of applications. Each user signs in with a familiar credential they will not forget, which can seamlessly onboard users to your digital services. Yet there are suboptimal ways to implement social logins. The process may appear simple at first sight, but it can quickly lead to complexity and blocking issues.
The best approach when designing such solutions is to think backward from what your APIs need to correctly secure data access. Avoid using the social provider’s ID token as an API credential.
Even more importantly, avoid using foreign access tokens to protect your own data. Instead, issue access tokens whose formats, claims and lifetimes you can control. Following security best practices for both APIs and clients is also important.
Lastly, consider the viewpoints of code simplicity, future security capabilities and architectural scalability. This will lead to integrating social logins using an authorization server. You must then ensure that the provider you choose supports your desired behaviors. See the following links for further related information: