OAuth2, Saml, OpenID Connect, SSO, Grant flow, everything you need to know
When you create a web application or a SaaS service, you want to restrict access to registered users only. You need to authenticate your users; in other words, you want to know who is using the application: authentication, and you need to know whether they have the right to do the requests: authorization. Authorization means deciding which resources a certain user should be able to access, and what they should be allowed to do with those resources.
Before going further into the implementation in a ReactJS or VueJS SPA (Single Page Application) apps, we will go through protocols that are involved nowadays in authentication and authorization.
local user authentication: you have your own user pool. You create a sign-up form to ask for login (email) and a password, and you store those credentials in your database (passwords must not be stored in plain text, you will have to hash it using for instance bcrypt).
Local authentication can be bad for business: people find sign up and account creation tedious, and users may abandon apps or shopping carts. For enterprises with many apps, the maintenance of separate user databases can quickly become an administrative and security nightmare.
The other option is to delegate user authentication and permission to a third party service that provides Identity Provider (IdP), for instance, Google, Facebook, Amazon. If those third party services are shared among different applications, you can allow a user to enter this username and password once and get access to multiple applications. This idea is called single sign-on (SSO).
We will focus on the IdP option!
There are two dominant open web standards for online identity:
OpenID Connect, published in 2014, is not the first standard for IdP, but definitely the best in terms of usability and simplicity, having learned the lessons from past efforts(SAML and oldest version of OpenID).
SAML is an open standard, based on XML-based protocol messages that provides both authentication and authorization. SAML defines a principal, which is the end-user trying to access a resource. There is a service provider (SP), which is the webserver that the principal is trying to access, and there is an identity provider (IdP), which is the server that holds the principal’s identities and credentials. The service provider requests and obtains an authentication assertion from the identity provider. On the basis of this assertion, the service provider can decide whether to perform the service for the connected principal. SAML does not specify the method of authentication at the identity provider.
SAML is/was very common in corporate environments as it is the oldest standard. It was originally developed in 2001, and its latest major update was in 2005.
Basically if you are new in IdP and you don't have to manage legacy stuff, you can ignore SAML.
OpenID Connect is close to Google’s authentication API. OpenID Connect standardizes the flow for person authentication using OAuth2. Previously we had too many proprietary API’s that did the same thing. OpenID Connect represents years of work to align consumer IDPs (like Microsoft, Google, Yahoo…) and other industry participants on a single profile of OAuth 2.0 for authentication. OpenID Connect (OIDC) was created in early 2014, and it is promoted by the non-profit OpenID Foundation.
We need to define first what is OAuth 2.0 before digging into OpenId connect.
OAuth 2 defines the flows to authorize access to a resource, whereas OIDC defines and normalizes the content of the messages involved in those flows. It also uses the messages to carry out the identity of the user.
OAuth 2.0 is an authorization framework, not an authentication protocol. OAuth2 is generic so that it could be applied to many authorization requirements (API access management, IOT services, ...).
OAuth2 provides secure delegated access, meaning that an application, called a client, can take actions or access resources on a resource server ** on behalf of a user, without the user sharing their credentials with the application**.
For instance, you can grant access to your twitter account to an application without reveling your twitter password.
OAuth2 does this by allowing tokens to be issued by an identity provider to these third-party applications, with the approval of the user. The client then uses the token to access the resource server on behalf of the user. We talk about access token. Those tokens typically have a limited lifetime.
OAuth 2.0 supports different flows also called grants.
Grants are ways of retrieving an Access Token.
Deciding which one is suited for your use case depends mostly on your client's type, the trust for the client (web app, mobile/native app, server), or the experience you want your users to have.
OAuth 2.0 defines 4 major grant flows to delegate access on behalf of a user:
And two additional flows for specific use cases:
I recommend to understand at least the 2 followings:
Authorization Code is used when you can protect/secure a secret. For instance, you have a backend server (like in a regular PHP web-app) that is hosted in a secured place.
The secret must NOT be shipped into the application source code or binary that is deployed on the user browser, mobile, or desktop: security through obscurity is not valid at all.
The Implicit grant type is a simplified flow that can be used by public clients, where the access token is returned immediately without an extra authorization code exchange step.
Implicit grant is used when you can't protect a secret: SPA application, Mobile application, etc...
We will discuss those two flows in the security chapter.
Back to OIDC: OAuth2 is the basis for OpenID Connect, which provides OpenID (authentication) on top of OAuth2 (authorization) for a complete security solution.
OpenID Connect is a profile of OAuth 2.0 that defines workflows for authentication. The big difference between OpenID Connect and OAuth2 is the id_token. There is no id_token defined in the OAuth2 protocol because the id_token is specific to authentication.
OIDC uses simple JSON Web Tokens (JWT) for the token format, which you can be obtained using flows conforming to the OAuth 2.0 specifications.
OIDC purpose is to give you one login for multiple app/websites. Each time you need to log in to a website using OIDC, you are redirected to your OpenID site where you log in, and then taken back to the website.
For example, once you successfully authenticate with Google and authorize a client app to access your information, Google will send back to the client app information about the user and the authentication performed. This information is returned in a JWT format. The client app will receive an Access Token and, if requested, an ID Token that contains the user identity in a JWT payload.
The benefits of JWT are consequent:
OpenID Connect defines
In your SPA, only 2 flows can be used:
The implicit grant is generally not recommended. The flow was written in the time where browser javascript was unable to perform cross-domain requests. The implicit grant relies only on GET requests and browser redirection with the usage of the URL fragment (https://YOUR_DOMAIN/#access_token) It is now discouraged from using it in the RFC.
The implicit grant has the following flaws:
We will go deeper into security flaws in the next section.
Today, Cross-Origin Resource Sharing (CORS) is universally adopted by browsers, removing the need for this compromise. CORS provides a way for JavaScript to make requests to servers on a different domain as long as the destination allows it. This opens up the possibility of using the Authorization Code flow in JavaScript on browser-side with the PKCE extension instead.
Here is an implementation proposal to do an authorization code grant in a Single Page Application. The JWT is stored in a cookie as recommended in my previous article.
The main advantages are:
The authorization flow can now be done on the browser only thanks to the PKCE extension. It is then recommended if you can access a backend server without a valid token.
In addition to what has been discussed in my previous article, the authentication flows increase the attack surface. I will try to list the potential attack and the corresponding mitigation. Full list available here.
Access token credentials (as well as any confidential access token attributes) MUST be kept confidential in transit and storage, and only shared among the authorization server, the resource servers the access token is valid for, and the client to whom the access token is issued. Access token credentials MUST only be transmitted using TLS (httpS).
The signature and the expiration date of every token should always be verified. The OIDC discovery documents contain the signature algorithm and the public key to use to verify the signature.
The goal for the attacker is to steal the token and reuse it to impersonate you. It can be done thanks to
Mitigation
An attacker who owns a site A, can try to reuse user token on another site B that use the same IdP. For instance, the attacker has a web site A that uses Facebook Connect to authenticate and grant access to his website. The attacker can try to get the user tokens (from the server that he owns) and reuse them on a targeted website B that also use Facebook Connect and has weak audience verification.
In technical words, a JWT token issued by a public IdP for a site A could be reused for a site B that uses the same IdP and has weak issuer and/or audience verification.
Mitigation
The JWT payload defined in OIDC has 2 important text fields:
The issuer contains the URL of the IdP used to generate the token. It should be compared to a white list.
The audience is the destination of the token. It should be compared to a white list.
If the issuer or audience doesn't match the white list, an unauthorized exception should be throw.
The goal here for an attacker is to send an email to a victim with a specific link.
Let say you own a website A that uses authentication code grant flow to authenticate your users. Your website is a SaaS service that bills monthly.
An attacker can try the following:
the server will exchange the given code for an access token. The access token will contain the attacker's identity.
The same attack can be performed with both Authorization code or access-token if the implicit grant flow is used.
Phishing and token replay can be used at the same time; for instance, a malicious website can use an iFrame to change the currently connected user silently.
The attacker may take advantage of an open URI redirection configuration on the IdP to redirect the user browser into a malicious website that can steal the user access token.
Mitigation
This article contains a lot of information about concepts and security concerns for your SPA. I recommend the Authentication code grant flow with a server or in the browser only with PKCE. You need to understand the core concept to make sure you don't leak the token, and you secure your app properly against CSRF and phishing, especially when you use a public/shared identity provider IdP (CSP, white-list issuer, white-list audience, no pattern in redirection URI, random state to control the flow initiator).
Feel free to discuss on twitter.
In case you missed the first part:
👉 Don't forget to follow me on Twitter to be notified when new posts are available!
Follow @jcbaey