In his post on The State of the Implicit Flow in OAuth2, Brook Allen mentions several reasons why OIDC/OAuth2 implicit flow is no longer a recommended approach to protect a public application and discusses using Oauth2 authorization code grant with Proof key for code exchange (PKCS) if the client and the resource server run on different domains, or simply using cookie based authentication with same-site cookie policy if the client and resource server run on a same domain. This post is my notes on what I have learned after reading Brook Allen’s post and also the related documents from Internet Engineering Task Force about the security risks of using OAuth2 implicit flow.
One of the reasons the implicit flow is less secure than the authorization flow is the lack of client authentication. Unlike a confidential client, a public client such as a javascript based application which runs in a browser is not able to secure any secret. As a result, it does not make sense to require the public client to authenticate because the client’s credentials are visible by inspecting the source codes in the browser. Without requiring the client to authenticate, any application can pretend to be the client if it knows the client’s id. To prevent a malicious application from obtaining tokens using id of the real client, an authorization server must only deliver the tokens to the trusted, registered uris via url redirect. Otherwise, the system is vulnerable to phishing attacks because of acting as open redirectors, per RFC 6749 section 10.15. Indeed, azure ad and azure adb2c verify the redirect urls and throw errors if the url in the request does not match exactly with one of the uris defined in the app registration.
However, having to deliver the tokens via url redirects open the door to other types of attack because of exposing tokens in the urls. For instance, if the tokens are in the query parts of the url, then nothing prevents the browser from unintentionally saving the tokens in history. In addition, nothing prevents the full url which includes the tokens from going out to other servers.
You may wonder why doesn’t the authorization server return the tokens via a HTTP response instead? That is because when the OAuth2 authors came up with the implicit flow, it was not possible for a client application to submit a request in codes to a server if the server is on a different domain because of same-site origin policy restriction. Nowadays, with Cross-origin resource sharing policy (CORS), the client application can submit the request in codes to the server if the server supports and enables CORS. Indeed, the IEFT publishes a revised document on OAuth2 current best practices which states clients should not use the implicit flow anymore.
Previously it was recommended that browser-based applications use the OAuth 2.0 Implicit flow. That approach has several drawbacks, including the fact that access tokens are returned in the front- channel via the fragment part of the redirect URI, and as such are vulnerable to a variety of attacks where the access token can be intercepted or stolen.
For instance, a naive implementation of an authorization server may allow an attacker to use the server as an open redirector to treat the user into authenticating against the authorization server and have the server sends the access token to an endpoint which the attacker controls. Similarly, your application can be an open redirector if you are not careful. For more info and examples on attacks via open redirector, checkout the section Insufficient Redirect URI Validation of OAuth 2.0 Security Best Current Practice. Also read OAuth 2.0 Threat Model and Security Considerations, and OAuth 2.0 for Browser-Based Apps to understand the risks associated with using the implicit flow and the approaches to mitigate them.
As suggested in Brook Allen’s post and also in the OAuth 2.0 for Browser-Based Apps, the more secure flow for protecting a client application is the code flow with Proof Key for Code Exchange (PKCE).
browser-based apps can perform the OAuth 2.0 authorization code flow and make a POST request to the token endpoint to exchange an authorization code for an access token, just like other OAuth clients. This ensures that access tokens are not sent via the less secure front-channel, and are only returned over an HTTPS connection initiated from the application. Combined with PKCE, this enables the authorization server to ensure that authorization codes are useless even if intercepted in transport.
Section 4 of OAuth 2.0 for Browser-Based Apps spec
Alternatively, if the client application and the API which it talks to are on a same domain, it may be better to just use session authentication.
Session authentication has the benefit of having fewer moving parts and fewer attack vectors. OAuth and OpenID Connect were created primarily for third-party or federated access to APIs, so may not be the best solution in a same-domain scenario.
Section 6.1 of OAuth 2.0 for Browser-Based Apps spec
The state of the implicit flow in OAuth2
OAuth 2.0 for Browser-Based Apps Proof Key for Code Exchange by OAuth Public Clients Open Redirectors what are the security risks of implicit flow Protecting Mobile Apps with PKCE
Using MSAL angular to authenticate a user against azure ADB2C via authorization code flow with Proof Key for Code Exchange.
Azure AD authentication in angular using MSAL angular v2 library
Common frameworks, libraries and design patterns I use
Authenticate against azure ad using certificate in a client credentials flow
Migrating from Microsoft.AspNetCore.Authentication.AzureAD to Microsoft Identity Web authentication library to integrate with Azure AD.
Integrate Azure AD B2C reset password user flow in angular using oidc-client-js.
Integrate Azure AD B2C profile editing user flow in angular using oidc-client-js.
Using OAuth2 Client Credentials grant type in Azure ADB2C