Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guidance Needed: Implementing Two Different OAuth Flows in One Application #1437

Open
newway-anshul opened this issue Sep 12, 2024 · 1 comment

Comments

@newway-anshul
Copy link

Hi there,

I'm working on an Angular application that requires two different OAuth flows:

  1. A standard login flow for user authentication
  2. An OAuth flow for connecting to Atlassian Jira

Currently, I'm using the angular-oauth2-oidc library for both flows, but I'm encountering issues when trying to manage two different configurations. Here's a brief overview of my setup:

  • For the main user authentication, I'm using the standard OAuthService configuration provided at the root level.
  • For the Jira integration, I initially tried to create a separate instance of OAuthService with a different configuration.

I've tried a few approaches:

  1. Using two different OAuthService instances with separate configs:
  2. Attempting to reconfigure the main OAuthService instance when switching between flows.

Both approaches have led to conflicts and unexpected behavior, particularly with token storage and management.

Questions:

  1. What's the recommended way to handle multiple OAuth flows in a single application using angular-oauth2-oidc?
  2. Is it possible to use a single OAuthService instance but switch configurations dynamically?
  3. If separate instances are needed, how can I ensure they don't interfere with each other?
  4. Are there any examples or documentation specifically addressing this use case?

Any guidance or best practices for managing this scenario would be greatly appreciated.
Thank you for your time and assistance!

@Serge-Libotte
Copy link

Serge-Libotte commented Sep 13, 2024

Hi,

I was looking into a similar issue today and I did some analysis.
Also I posted the question on Stackoverflow: https://stackoverflow.com/questions/78977600/oidc-oauth-api-requests-using-different-public-clientid-in-an-angular-app

Some APIs are registered on our API Gateway with different OAUTH clientId from two different identity providers. Our need is then to be able to call APIs by specifying the IDP and the clientId.

The issue, in short, is that OAuthService is really meant to be used as a singleton in a page, meaning that only one IDP and one clientId can be used by the page.

Storage access
First root cause, as you mentioned, is the way it uses Storage. Indeed, the keys are static. In the code of the lib you find stuff like this._storage.setItem('refresh_token', refreshToken)
I wonder what happens when you jump from one front-end to another when the two are using different APIs registered with different client-id. All instances of OAuthService will overwrite the same keys from the Storage.
To solve this quickly we have to implement a custom OAuthStorage that wrap a sessionStorage and use keys with a pattern like prefix:IDP:client-id (ex: angular-oauth2-oidc:https://myidp.com/auth/realms/abc/:api-public-client-id) Under such key we would store a Json object containing the requested keys ({refresh_token:"xyz",...})
Note that although having only one Storage key per instance may sound elegant it can lead to concurrency issues. One way to deal with it is to use Mutex or use "Web Locks API" (https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API). This would involve to change the interface of OAuthStorage with async accessors (IMO, consumers should be aware of the async behavior). A first step could be to prefix all keys that are currently used. Then we may rationalize the use of the keys and reduce them if possible.
iFrame identifier
Then comes the issue of the iFrame identifier for the silent refresh. Like for the rest of the code it assumes there is a unique instance of the class running in the page. First it destroys (potentially) and create an iFrame with id = this.silentRefreshIFrameName (default is 'angular-oauth-oidc-silent-refresh-iframe'). A new refresh for another API will destroy the existing iFrame created from a concurrent refresh.
Luckily this is a public property of the class so it’s possible to calculate a unique value per API definition.
postMessage
Another problem is how messages are treated when acquiring token (from iFrame or popup). Before creating an iFrame a message listener is added… ok, but what if two iFrames are doing the silent refresh in parallel? As you guess, when first iFrame posts a message it will be captured by all listeners and all will enter the tryLogin() method. One may succeed but all others will fail.
The way to solve that is to use the OAUTH state parameter:
#The “state” parameter is sent during the initial Authorization Request and sent back from the Authorization Server to the Client along with the Code (that can be later exchanged to a token). The Client should use the content of this parameter to make sure the Code it received matches the Authorization Request it sent.
The state will be sent back as a query parameter. Great, the class has a state field. It’s then possible to set it to a value that would be recognized by the correct OAuthService instance. Only the emitter instance would treat the received message.

Am I wrong or missing something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants