Table of Contents
Generally speaking, the term authentication refers to any process of verification that someone, be it a human being or an automated system, is who (or what) it claims to be. This is also true within the context of the World Wide Web (WWW), where that same word is mostly used to denote any technique used by a website or service to collect a set of login info from a user agent, typically a web browser, and authenticate them using a membership and/or identity service.
Authentication should never be confused with Authorization, as it is a different process and is in charge of a very different task: to give a quick definition, we could say that the purpose of authorization is to confirm that the requesting user is allowed to have access to the action they want to perform. In other words, while authentication is about who he is, authorization is about what he's allowed to do.
To better understand the distance between these two apparently similar concepts, we could think of two real-world scenarios:
- A free, yet registered account trying to gain access to a paid or premium only service or feature: this is a common example of authenticated, yet not authorized access; we know who he is, yet he's not allowed to go there.
- An anonymous user trying to gain access to a publicly available page or file: this is an example of non-authenticated, yet authorized access; we don't know who he is, yet he can access public resources just like everyone else.
To Auth, or not to Auth
As a matter of fact, implementing authentication and/or authorization logic isn't mandatory for most web-based applications or services: there are a number of websites that still don't do that, mostly because they serve contents that can be accessed by anyone at any time. This used to be pretty common among most corporate, marketing, and informative websites until some years ago: that was before their owners learned how important it is to build a network of registered users and how much these loyal contacts are worth nowadays.
We don't need to be experienced developers to acknowledge how much the World Wide Web has changed in the last few years: each and every website, regardless of its purpose, nowadays has an increasing and more or less legitimate interest in tracking their users, giving them the chance to customize their navigation experience, interacting with their social networks, collecting e-mail addresses, and so on. None of the preceding could be done without an authentication mechanism of some sort.
There are billions of websites and services that require authentication to work properly, as most of their content and/or intents depend upon the actions of registered users: forums, blogs, shopping carts, subscription-based services, and even collaborative tools such as wikis (including ours).
Long story short, the answer is yes: as long as we want to have users performing CRUD operations within our client app, there is no doubt we should implement some kind of authentication and authorization procedure. If we're aiming for a production-ready SPA with , we definitely want to know who our users are in terms of name and e-mail address. It is the only way to determine who will be able to view, add, update, or delete our valued quizzes, not to mention perform administrative-level tasks, keep track of our users, and so on.
Since the origin of the World Wide Web, the vast majority of authentication techniques rely upon HTTP/HTTPS implementation standards, and all of them work more or less in the following way:
- A non-authenticated user-agent asks for a content that cannot be accessed without some kind of permissions.
- The web application returns an authentication request, usually in form of an HTML page containing an empty web form to complete.
- The user-agent fills up the web form with their credentials, usually a username and a password, and then sends it back with a POST command, which is most likely issued by a click on a Submit button.
- The web application receives the POST data and calls the aforementioned server-side implementation that will try to authenticate the user with the given input and return an appropriate result.
- If the result is successful, the web application will authenticate the user and store the relevant data somewhere, depending on the chosen authentication method: sessions/cookies, tokens, signatures, and so on (we'll talk about it later on). Conversely, the result will be presented to the user as a readable outcome inside an error page, possibly asking them to try again, contact an administrator, or something else.
This is still the most common approach nowadays. Almost all websites we can think of are using it, albeit with a number of big or small differences regarding security layers, state management, JWT or other RESTful tokens, basic or digest access, single sign-on properties, and more.
Being forced to have a potentially different username and password for each website visit can be frustrating, other than requiring the users to develop custom password storage techniques that might lead to security risks. In order to overcome this issue a wide amount of IT developers started to look around for an alternative way to authenticate users that could replace the standard authentication technique based upon usernames & passwords with an authentication protocol based upon trusted third-party providers.
The rise and fall of OpenID
The first successful attempt to do that was the first release of OpenID, an open and decentralized authentication protocol promoted by the non-profit OpenID Foundation. Available since 2005, it was quickly and enthusiastically adopted by some big players such as Google and StackOverflow, who originally based their authentication providers upon it.
Here's how it worked in few words:
- Whenever our application receives an OpenID authentication request, it opens a transparent connection interface through the requesting user and a trusted, third-party authentication provider (for example, the Google Identity Provider): the interface can be a popup, an AJAX, populated modal windows or an API call, depending on the implementation.
- The user sends his username and password to the aforementioned third-party provider, who performs the authentication accordingly and communicates the result to our application by redirecting the user back to where he came, together with a security token that can be used to retrieve the authentication result.
- Our application consumes the token to check the authentication result, authenticating the user in case of success or sending an error response in case of failure.
Despite the great enthusiasm between 2005 and 2009, with a good amount of relevant companies publicly declaring their support for OpenID and even joining the foundation - including PayPal and Facebook - the original protocol didn't live up to the great expectations: legal controversies, security issues and, most importantly, the massive popularity surge of the social networks with their improper - yet working - OAuth-based social logins within the 2009-2012 period basically killed it.
In a desperate attempt to keep their flag alive after the takeover of the OAuth/OAuth2 social logins, the OpenID foundation released in february 2014 the "third generation" of the OpenID technology, which was called OpenID Connect.
Despite the name, the new installment has little or nothing to do with their ancestors: it's merely an authentication layer built upon the OAuth2 authorization protocol. In other words, it's little more than a standardized interface to help developers using OAuth2 as an authentication framework in a "less improper way": which is kind of funny, considering that OAuth2 played a major role in taking out OpenID 2.0 in the first place.
The choice to move to OpenID Connect was quite sad in 2014 and it still is as of today: however, after more than three years, we can definitely say that - despite its undeniable limitations - OpenID Connect can still provide a useful, standardized way to obtain user identity; it allows developers to request and receive info about authenticated users and sessions using a convenient, RESTful based JSON interface; it features an extensible specification wich also supports some promising optional features such as encryption of identity data, auto-discovery of OpenID providers, and even session management. In short, it's still useful enough to be used instead of relying to pure OAuth2.
For additional info about OpenID, we strongly suggest to read the following specifications from the OpenID Foundation official website:
OpenID 2.0 to OpenID Connect migration guide
In most standard implementations, including those featured by ASP.NET, the authorization phase kicks in right after the authentication, and it's mostly based on permissions or roles: any authenticated user might have their own set of permissions and/or belong to one or more roles, and thus be granted access to a specific set of resources. These role-based checks are usually set by the developer in a declarative fashion within the application source code and/or configuration files.
Authorization, like we said, shouldn't be confused with authentication, despite the fact it could be easily exploited to perform an implicit authentication as well, especially when it's delegated to a third-party actor.
The best known third-party authorization protocol nowadays is the 2.0 release of OAuth, also known as OAuth2, which supersedes the former release (OAuth1 or simply OAuth) originally developed by Blaine Cook and Chris Messina in 2006.
We already talked a lot about it for good reasons: OAuth 2 has quickly become the industry-standard protocol for authorization and is currently used by a gigantic amount of community-based websites and social networks, including Google, Facebook and Twitter. It basically works like this:
- Whenever an existing user requests a set of permissions to our application via OAuth, we open a transparent connection interface between them and a third-party authorization provider that is trusted by our application (for example, Facebook).
- The provider acknowledges the user and, if they have the proper rights, responds entrusting them with a temporary, specific access key.
- The user presents the access key to our application and will be granted access.
OAuth2 trust level
We can clearly see how easy it is to exploit this authorization logic for authentication purposes as well; after all, if Facebook says I can do something, shouldn't it also imply that I am who I claim to be? Isn't that enough?
The short answer is no. It might be the case for Facebook, because their OAuth 2 implementation implies that the subscriber receiving the authorization must have authenticated himself to Facebook first; however, this assurance is not written anywhere: considering how many websites are using it for authentication purposes we can assume that Facebook won't likely change their actual behaviour, yet we have no guarantees about it.
Theoretically speaking, these websites could split their authorization system from their authentication protocol at any time, thus leading our application's authentication logic to an unrecoverable state of inconsistency. More generally, we can say that presuming something from something else is almost always a bad practice unless that assumption lies upon very solid, well-documented and (most importantly) highly guaranteed grounds.
Proprietary vs Third-Party
Theoretically speaking, it's possible to entirely delegate the authentication and/or authorization tasks to existing external, third-party providers such as those we mentioned before: there are a lot of web and mobile applications that proudly follow this route nowadays. There are a number of undeniable advantages in using such an approach, including the following:
- No user-specific DB tables/data models, just some provider-based identifiers to use here and there as reference keys.
- Immediate registration, since there's no need to fill in a registration form and wait for a confirmation e-mail: no username, no password. This will be appreciated by most users and probably increase our conversion rates as well.
- Little or no privacy issues, as there's no personal or sensitive data on the application server.
- No need to handle usernames and passwords and implement automatic recovery processes.
- Fewer security-related issues such as form-based hacking attempts or brute force login attempts.
Of course, there are also some downsides:
- There won't be an actual user base so it would be hard to get an overview of active users, get their e-mail address, do statistics, and so on.
- The login phase might be resource-intensive, since it will always require an external, back and forth secure connection with a third-party server.
- All users will need to have (or open) an account with the chosen third-party provider(s) in order to log in.
- All users will need to trust our application because the third-party provider will ask them to authorize it for accessing their data.
- We will have to register our application with the provider in order to be able to perform a number of required or optional tasks, such as receive our public and secret keys, authorize one or more URI initiators, and choose the information we want to collect.
Taking all these pros and cons into account, we could say that relying on third-party providers might be a great time-saving choice for small-scale apps, including ours: however, building our own account management system seems to be the only way to overcome the aforementioned governance and control-based flaws undeniably brought by that approach.
As always, it's mostly a matter of what we actually need (or want to) achieve as product owners, project managers or full-stack software developers.