"Single Sign-On" is an authentication mechanism that enables users to log in and access multiple applications and systems without having to log in multiple times. In this blog, we will cover the following topics:
- Single Sign-On and its advantages
- SSO protocols: SAML vs. OpenID Connect
- Password anti-pattern problem
- OAuth- What is OAuth, Real world examples of OAuth, key terminologies, different grant-types
- OpenID Connect
- Identity token
- Enterprise case study on SSO implementation using OIDC, Azure Active Directory, and MSAL
If you are already aware of all these terminologies, then please skip the first few sections and directly check the last 'case study' section where you can read about how SSO was implemented for one of our enterprise client applications. We used MSAL (Microsoft Authentication Library) which is a standard authentication library by Microsoft for identity and access management.
Single Sign-On and its Advantages
Single Sign-On works on the principle of Login once, access multiple applications. A classic example of Single Sign-On in enterprises is how employees can access Microsoft Suite Applications (Outlook, OneDrive, Word, SharePoint, Teams, etc.) with a single login on the browser.
On the front channel, what users observe is, "I need to login to any one application, and then the browser automatically logs me in to other applications. I don't need to re-login (for some time) to access other applications of the same suite." This is what happens behind the scenes:
- The user enters credentials to access any one application of the whole suite.
- The browser sends an authentication request (credentials) to the server/service provider.
- The service provider redirects the browser to the authorization server.
- The authorization server/ identity provider validates the credentials and then creates 'something' which is sent back to the browser.
- This 'something' is basically the user's identity information and could be in any form— identity token, JWT, auth code, signed certificate, or XML assertion; depending on the protocol or process used for SSO.
- The browser stores this identity information on the client side—local storage, cookies, session storage, or certificate registry.
- An important thing to note here is that this identity information (in any form) has validity. Depending on the authorization server, it could be in milliseconds, seconds, or minutes. If it is short-lived, then the authorization server will provide a backup mechanism to increase the duration (~ refresh tokens).
- If a user tries to access any application of the same suite, behind the scenes, the browser sends this identity information to the authorization server and gets it validated.
- This automatically logs in the user and gives them a seamless workflow experience.
Note: The above flow is very high-level and generic. Steps might vary based on the SSO protocol used.
SSO has many advantages. A few of them are listed below:
Purple boxes in the diagram show SSO advantages from the user's perspective, while the blue boxes show the system/ enterprise's view. It makes the end user's life easy by providing a better workflow, avoiding password fatigue, and negating the need to remember multiple passwords. On the other hand, the enterprise IT/ Admin team can do identity and access management from a central system. They can enable MFA (multi-factor authentication) to increase the security of all associated applications.
Web SSO Protocols
Once authentication is successful, clients (or browsers) fetch users' identity in the form of tokens, certificates, or codes, and then they store this 'information' on the client side in cookies, local storage, or session storage. Later, when users try to access other applications, behind-the-scenes browsers send these tokens/certificates on users' behalf, giving end users a better user experience and seamless workflow.
There are various ways or protocols where the client applications get authenticated and store identity information. SAML (Security Assertion Markup language) and OIDC (OpenID Connect) are the two most popular SSO protocols for web-based applications.
SAML is an XML-based traditional approach for implementing Single Sign-On in enterprises. Client applications get SAML assertion (xml document) from the identity provider which they further use to communicate with the service provider. OIDC OpenID Connect uses JWTs, and information is exchanged in JSON format between the client, resource server, and authorization server. The below picture will give you a high-level comparison between these two protocols:
OpenID Connect is a modern approach for implementing Single Sign-On in enterprise web applications. It is an identity layer built on top of the OAuth 2.0 protocol. To understand the working and behind-the-scenes of OIDC, we should first understand the flow of OAuth- OIDC's underlying protocol. But even before we talk about OAuth, we need to know why OAuth was introduced and what problem it solved.
Password Anti Pattern Problem
Let's go a few years back and see how things were back then.
Case 1: Facebook (now Meta) - Gmail Case Study
A social networking startup 'Facebook' asked its users to enter their mail (Gmail/ Yahoo/ MSDN etc.) credentials on the Facebook website. All Facebook wanted was to log into users' mail accounts to check which of the users' connections were already using Facebook. Based on this analysis, it would provide recommendations to the users so they could expand their networks.
Facebook wanted to access Google's user base. They wanted their users' Gmail credentials for 'limited' access like name, email, etc. When the user enters their Gmail credentials on the Facebook website, they have no control over the scope of access. Also, there was no way for them to revoke the permissions later.
Case 2: LinkedIn to Automatically Post Tweets
To implement this functionality, LinkedIn would need access to your Twitter (now X) account. A simple solution would be to give your Twitter credentials to LinkedIn so that every time you tweet, LinkedIn accesses and posts the same content.
This pattern where an application expects a user to enter credentials of another application so that it can access users' data hosted/owned by that application is called the Password Anti Pattern. This pattern had multiple problems:
- Lack of trust as users were required to trust you with their credentials.
- No way to revoke access permissions later.
- No control over the level and duration of access.
- Lack of a standard solution as each organization was coming up with its own authorization system to solve this.
It is in this context that a standard solution like OAuth emerges as a clear winner.
OAuth is a standard authorization protocol or framework that allows applications to get limited access to users' data without exposing their sensitive personal information. OAuth 2.0 standard was introduced to solve the 'password anti-pattern' problem. If you notice all bold keywords in the OAuth definition, you will see how it solves all the problems mentioned above.
Let's see how we can solve the Facebook-Gmail case using OAuth. Instead of entering the Gmail credentials on the Facebook website itself, the user would need to click the 'Sign in with Google' button. This would open a popup and redirect the user to the Google login page. Users will be comfortable providing their Gmail credentials on the Google Identity Server. Once Google validates users' identity, it hands over an access token. The client application (in this case, Facebook) can then use this access token to call Google Contacts API and fetch the required data (in this case, users' friend list).
OAuth Authorization Code Flow in Facebook - Gmail Case
OAuth-Real World Example
The hotel check-in experience is the classic real-world analogy to understand OAuth. We cannot directly access our hotel room. First, we need to establish our identity in the reception. After that, the receptionist hands us a key card to access our room. Here, the hotel room is the bundle of resources/amenities that you want to access, the receptionist is the authorization server, and the key card is the access token. The important thing is that key cards don't contain any identity information. It's solely tied to the room, not to the users.
OAuth- Real World Example
Key Terminologies in OAuth
- Client Application: The application that wants to access protected resources hosted and owned by the resource server. In our above case, Facebook is the client application.
- Resource Server: The entity that owns/holds the protected resources. In our case, the Google Contacts API servers have the protected data (users' friends list)
- Authorization Server (AS): This is the main engine of OAuth. An entity that handles the authorization part, creates/validates access tokens.
- Client ID & Secret: This is the unique identifier for the client application. It is granted by the AS server when the client application registers itself on the AS server.
- Access Token: The AS server grants an access token to the client application. This has all the claims information.
- Scopes: Claims defined inside the access token define the level of permissions the token bearer will have on the resource server.
OAuth Grant Types
OAuth provides different flows/ grant types. We can choose the appropriate grant type based on our use case/ need. Here are a few popular ones:
- Authorization Code Flow
- Implicit Flow
- Client Credentials Flow
- Authorization Code Flow with PKCE
All these OAuth flows differ based on how and when the access token is generated and shared with the client applications. We are not going to dive deep into these flows' functionalities. As part of Encora Dev Week 2022, I have done a detailed tech talk on OAuth and its key terminologies. Also covered are the various types of flows and which flow to use when; the recording is available on the Encora YouTube channel here.
For now, the diagram below can help you choose an appropriate grant type based on your needs. The first question to ask is whether resource owners are involved or not. If not, it is a machine-to-machine interaction. In such cases, we must implement the Client Credentials OAuth. This could be an API calling another API, a web app calling a daemon process, etc. If the resource owner is involved, then we need to check if it's a public or confidential client.
Public clients are the applications that cannot keep a 'secret value' safe, whereas confidential clients can. Applications running on browsers (SPA) or mobile devices are public clients. Applications like Web apps, APIs, etc. that run on the server side and can securely connect with the AS server using back-channel communication are confidential clients. Authorization code flow is the best choice for confidential clients as it's the most secure. For public clients, you can either go for Implicit or Auth flow with PKCE. Implicit is not recommended anymore because it's less secure than PKCE.
Choosing OAuth Grant Type
OIDC- Open ID Connect
Now that we know what OAuth is, let us see what OpenID Connect is. Authentication verifies the user's identity, while authorization defines the level of access the user will have in the system. OAuth was all about authorization; it was not intended for authentication/ identity verification, but organizations started adding their own flavor on top of OAuth for authentication. This is when OIDC (Open ID Connect) was introduced.
OIDC is an identity layer built on top of OAuth. It provides you with everything OAuth has, along with the identity token and user info endpoint. OIDC and OAuth 2.0 together provide a complete package/framework for authentication and authorization in web applications.
The user enters the client application URL in the browser and clicks on 'Sign in with XXX' link. This is when the OAuth flow gets triggered, and the browser then redirects the user to the identity server/ AS server. The browser opens a popup, and this is how the pop URL looks like:
Sample OIDC Request
Let's break down this OIDC request:
- Line 1—authorization server/ IdP/ Tenant URL: Notice the 'OAuth2/authorize' API endpoint at the end.
- Line 2 — response_type: Specifies what your client application expects in return from the authorization server. It could be 'id_token', 'code' etc.
- Line 3— scope: Defines all the claims the client application expects inside the response token.
- Line 4— client_id: Unique identifier the client gets after registering on IdP.
- Line 5— redirect_uri: The authorization server should know where to redirect the user/ browser later.
- Line 6— state: Arbitrary number generated by the client application and passed to the authorization server. The server returns the same value inside the token. The client then matches both values to prevent CSRF attacks.
- Line 7— nonce: Arbitrary cryptographic hashed string value generated by the client app and passed to the authorization server. The server sends the same value inside the token to prevent replay attacks.
- Lines 8, 9, and 11 are all optional parameters.
- Line 10— login_hint: Optional parameter used as a hint by the authorization server to identify/tag the appropriate identity provider in case the authorization server has multiple IdPs.
- Line 12— response_mode: The client application specifies in which format they want the response back. Query params, jwt, fragment, or form post.
Once the AS server gets this request from the client application, it validates the provided credentials. After successful validation, it generates identity tokens in response to the above OIDC request. Let's see what these identity tokens are.
In the case of OAuth, the authorization server gives you an access token that allows the client application to access protected resources/data on the resource server on behalf of the authenticated user. This token is short-lived and has no information about users' identities; it just contains permissions and claims information about protected resources.
In OIDC, the authorization server gives you an identity token. The identity token is a JWT. The header portion of the identity token contains metadata like the token type and the hashing algorithm used for creating it. The footer has the hashed value of 'Header. Payload', acting as the digital signature for establishing the token's integrity. The payload has a lot of information.
Some aspects of the payload information are listed below:
- sub- user's unique identifier
- iss- issuer of the token, authorization server/ identity provider's unique identifier
- aud- audience of the token, client application ID
- iat- timestamp of when the token was created
- exp- expiration timestamp of the token
- nbf- not valid before, meaning the token is valid only after this timestamp
- nonce- Number used only once, for which the authorization server needs to pass the same value it received in the request
- state- arbitrary number used to prevent CSRF attacks; the authorization server needs to pass the exact value received in the request
- name- name of the authenticated user
- preferred_username- alias or secondary identity information for authenticated users, like email.
OIDC is a RESTful API-centric framework where client applications can invoke below three API endpoints.
- AS Server URL/authorize: Used in OAuth flow. AS Server returns a temporary auth code. The client application then makes a POST call (passes Client ID, Secret, and auth code) to get an access token in return from the AS server.
- AS Server URL/token: Client applications get an identity token from the AS.
- AS Server URL/user info: The ID token may not give you all the information about authenticated users. At a minimum, it will contain a unique identifier. The client application can always make another GET REST API call (user info endpoint) to the AS server and fetch more information about users.
These are in no order, as the flow/ sequence of these endpoints will depend on the underlying OAuth grant type used. OIDC's identity token is the perfect choice for implementing Single Sign-On in enterprise applications. Browsers store this identity token on the client side and use it behind the scenes to authenticate end users and give them a seamless user experience.
All the popular identity providers, like Microsoft Azure Active Directory, Okta, Ping Identity, etc., support both SSO protocols- SAML and OIDC. In this blog's next and last section, we will see a case study where we implemented SSO using Azure AD as an identity provider and OIDC SSO protocol.
Enterprise Case Study on Implementing SSO using OIDC, Azure AD, and MSAL
Encora designed and implemented an Azure cloud-based scheduler for automating work scheduling for a client in the utility operations space. During our discovery phase, we gathered that this scheduler web application would be used only by the client's employees (in-house app). They use Azure Active Directory as an identity provider for managing all their employees centrally. We decided to implement the SSO in our scheduler application. Since the client's Azure AD already had our whole user base, we did not find writing a separate custom authentication/authorization module imperative. Also, an SSO implementation provides multiple benefits, as we saw previously.
The next task was to choose a suitable SSO protocol between OIDC and SAML. Our web application is built in Angular + ASP .NET core tech stack and deployed on the Azure cloud. Microsoft recommends using OIDC for implementing SSO in cloud applications and web APIs 1,2. Microsoft also recommends using MSAL (Microsoft Authentication Library) as it provides all security best practices for implementing OIDC in web applications. 1
Solution: SSO using Azure AD was implemented as an identity provider, and OIDC as the SSO protocol. Since we were using an SPA (Angular app), we implemented the authorization code flow with the PKCE OAuth flow here.
SSO Solution Using OIDC and Azure AD
This is how it looks to end users:
- Client employee/ app user comes to our UI/ Angular application.
- No registration on our application is required—they only need to click the 'Login with Microsoft' link.
- Enter MS credentials and access the scheduler application.
Behind the Scenes
- Scheduler UI written in Angular acts as a client application.
- Scheduler .NET API is the resource server.
- The client's Azure Active Directory is the identity provider/ authorization server.
- We used Microsoft-recommended MSAL in our Angular code.
- We used 'Microsoft.AspNetCore.Authentication.OpenIdConnect & Microsoft.Identity.Web' NuGet packages in our .NET API code.
- MSAL, on the Angular side, initiates an OIDC flow (Auth code flow with PKCE) with the client's Azure AD.
- Azure AD hands over an identity token to the Angular app/ browser.
- This token has a logged-in user's identity information + claims/ scopes needed to access the .NET API.
- With this token, the Angular application can easily interact with the .NET API.
The picture below gives you a high-level idea about all changes/ configurations:
SSO- Code and Configuration Changes
The above solution using OIDC, MSAL, and Azure AD helped both the client and Encora in the following ways:
- No Custom Code: With the 'Single Sign-On' implementation in our scheduler application, Encora does not have to code for a separate custom authentication module.
- Ease of Access: Client employees could easily access our built application with their existing employee MS credentials without registering on the application.
- Data Security: The security of users' data was delegated to the client, and Encora focused on the application's core functionality, which resulted in the quick delivery of the product.
- User Management: No overhead managing users on the Encora side. The client could control centrally using their Azure AD.
- API Security: Encora could secure web API as all .NET API endpoints were Azure AD token protected. The Angular app generates the identity token using MSAL & Azure AD, which helps access the secured web API endpoints.
- Ease of Integration: The client recently asked us to check the feasibility of integrating our application with another vendor. Encora did a small POC, testing API endpoint accessibility using an Azure AD token generated with the 'Client Credentials OAuth flow.' The POC was successful as our secured APIs only care for a valid Azure AD token, regardless of which OAuth flow created it. So, we exposed/integrated our application with this vendor with minimal code changes. The client was pleased about this 'quick' integration because of the scalable/ futuristic technology.
Here, you can find another Azure case study on how we used the Azure logic app and service bus to integrate this scheduler application with the client's single source of truth.
'Access Denied' Troubleshooting: In our early development phase, we got an 'access denied' error message from Azure Active Directory. The Angular application was getting 401 from .NET API while calling API endpoints. This was mainly because of an invalid access token from AAD. We implemented the following fixes:
- Azure AD .NET API changes: Added a scope under the 'Expose an API' tab of .NET API. We made sure to link this added scope with the Angular application ID.
- Azure AD Angular changes: Ensured we added Angular URLs of all environments (including localhost) as redirect URLs on the Angular app registration section. Ensured we used the same redirect URL in the Angular app MSAL code.
Angular Version Upgrade: We upgraded our application— Angular version 8—to the latest application, Angular 14. Angular 14 doesn't support MSAL 1.0, so we had to upgrade from MSAL 1.0 to MSAL 2.0. This MSAL upgrade involved some code changes on our side.3
Changing OAuth Flow: In continuation to point number 1, initially, we implemented OIDC SSO using 'Implicit OAuth Flow' in our Angular application. Later, Microsoft recommends using 'Authorization Code Flow with Proof Key for Code Exchange' (PKCE) for SPAs. Also, MSAL 2.0 does not support implicit flow. 4,5 We had to change our OAuth flow to Authorization PKCE flow, which is more secure than implicit flow because the 'dynamic secret' (code verifier/challenge) offers protection against authorization code interception/injection attacks.
Full Access with AD Identity Token: Signed-in users could access our 'full' application. We had a flat system without any authorization in place. Later, we created different Azure AD groups and implemented role/group-based access in the scheduler application, ensuring the 'right' people have 'right' access. We were able to implement this within a week.
Limited Access on Azure AD: The Azure Active Directory is the enterprise-level identity and access management portal holding all enterprise users' and workload identities. Besides this, our client had a 'single tenant' Azure AD. They were cautious about giving access to Encora resources. Eventually, we decided to do a few crucial POCs on the Encora Azure AD tenant.
Single Sign-On is an authentication mechanism that enables users to access multiple applications and systems across multiple systems. SSO has multiple advantages like seamless workflow, centrally controlled user management, enhanced security, etc. There are multiple ways to implement SSO in web applications. SAML and OIDC are the two most popular ones. SAML xml-based protocol is a conventional way whereas the OIDC is JWT and token-based protocol which is very popular because of its strong ecosystem.
OIDC is an identity layer built on top of OAuth. OAuth is a standard delegated authorization protocol that solves password anti-pattern problems. It helps applications obtain data from third-party applications without giving away users' identity information. OAuth has multiple grant types based on how and when the access token is generated and shared with the client applications. OAuth is focused on authorization which gives us only access tokens containing claims/scopes within them, without users' identities. On the other hand, OIDC offers an identity layer on top of OAuth. OIDC gives us identity tokens too as JWTs containing users' identity information.
- (2023, June 29). Authentication vs. Authorization. Microsoft Learn. Retrieved September 15, 2023, from https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-vs-authorization
- (2023, February 5). Microsoft identity platform best practices and recommendations. Microsoft Learn. Retrieved September 15, 2023, from https://learn.microsoft.com/en-us/azure/active-directory/develop/identity-platform-integration-checklist
- (2023, November 8). Microsoft identity platform app types and authentication flows. Microsoft Learn. Retrieved September 15, 2023, from https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-flows-app-scenarios#scenarios-and-supported-authentication-flows
- (2023, February 1). Single-page application sign-in using the OAuth 2.0 implicit flow in Azure Active Directory B2C. Microsoft Learn. Retrieved September 15, 2023, from https://learn.microsoft.com/en-us/azure/active-directory-b2c/implicit-flow-single-page-application
- (2023, August 11). Microsoft identity platform and OAuth 2.0 implicit grant flow. Microsoft Learn. Retrieved September 15, 2023, from https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow
Aditya Chouhan has 13 years of IT industry experience in web development and design. He has expertise in Microsoft technology stack, Azure Cloud, and Identity & Access Management. Currently, he is working as an Engineering Manager at Encora. He is also one of the Innovation leaders of Encora in Cloud Services. He holds a master's degree in management of information systems from the University of Nebraska, Omaha, and a bachelor's in computer science from Indore, India. As part of his job at Encora, Aditya drives the delivery of multiple business initiatives and manages all aspects of projects such as planning, requirements, risk management, communication, and implementation. He loves to speak and write about technology.
Fast-growing tech companies partner with Encora to outsource product development and drive growth. Contact us to learn more about our software engineering capabilities.