AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.64k stars 2.65k forks source link

OBO flow not using refresh tokens #5330

Open aleknik opened 1 year ago

aleknik commented 1 year ago

Core Library

MSAL Node (@azure/msal-node)

Core Library Version

1.14.2

Wrapper Library

Not Applicable

Wrapper Library Version

None

Public or Confidential Client?

Confidential

Description

When acquiring tokens using obo flow, they are not refreshed before expiry (offline_access scope is included). Since MSAL is not exposing refresh tokens it should handle refresh on its own. What happens is that when the oboAssertion expires we are not able to get the token anymore.

Based on the documentation I was under the impressions that all refresh tokens are handled by MSAL library.

Error Message

Failed to execute exchange request: StatusCodeError: 400 - "{\"error\":\"invalid_grant\",\"error_description\":\"AADSTS500133: Assertion is not within its valid time range. Ensure that the access token is not expired before using it for user assertion, or request a new token. Current time: 2022-10-20T21:20:43.1843767Z, expiry time of assertion 2022-10-20T21:20:43.0000000Z.\r\nTrace ID: 0c81f3ab-9374-4d33-aac8-584f1a4b5000\r\nCorrelation ID: 442b6065-3c34-483f-98a9-e8ed6b993a24\r\nTimestamp: 2022-10-20 21:20:43Z\",\"error_codes\":[500133],\"timestamp\":\"2022-10-20 21:20:43Z\",\"trace_id\":\"0c81f3ab-9374-4d33-aac8-584f1a4b5000\",\"correlation_id\":\"442b6065-3c34-483f-98a9-e8ed6b993a24\",\"error_uri\":\"https://login.microsoftonline.com/error?code=500133\"}"

Msal Logs

No response

MSAL Configuration

const config: Configuration = {
                auth: {
                    clientId: clientId,
                    clientCertificate: {
                        privateKey: cert.privateRSAKey,
                        thumbprint: Buffer.from(thumbprint, 'base64').toString('hex'),
                        x5c: cert.publicCER.asPEM()
                    }
                },
                system: {
                    networkClient: new CustomHttpsNetworkModule()
                }
            };

Relevant Code Snippets

const response = await msalClient.acquireTokenOnBehalfOf({
            scopes: request.scopes,
            authority: `https://${request.authorityHostname}/${request.tenantId}`,
            oboAssertion: request.oboAssertion
        });

Reproduction Steps

  1. Request token using obo flow
  2. When obo assertion expired request the token again
  3. Error: Assertion is not within its valid time range.

Expected Behavior

MSAL should refresh the obo token proactively using the refresh token and update the cache.

Design

Add LongRunningOBO APIs just like https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/on-behalf-of and https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/2733

CC @pmaytak and @trwalke for breakdown of tasks - please add more if needed.

bmahall commented 1 year ago

@aleknik Thanks for bringing this to our attention, we currently do not support OBO tokens refreshed with long lived RT, i.e user will have to send a new assertion if the current access token expires. This is to prevent security issues when the assertion changes over time, however, longlived RT helps retaining the session.

I can get back to you on whether we intend to support this scenario in near future.

aleknik commented 1 year ago

Our scenario requires us to refresh the token for the lifetime of the session even though the assertion is expired, so this would help us to leverage the MSAL library for all scenarios (We are currently using a custom implementation of OBO and refresh flow for this scenario). Is it possible to get the refresh token in the msal library response and call the refresh method as a workaround?

bmahall commented 1 year ago

@aleknik We do not intentionally expose the refresh token in the response and that is unlikely to change. To make sure I understand, are you trying to get a new access token even though the assertion is expired?

bgavrilMS commented 1 year ago

CC @jmprieur for long lived OBO request.

aleknik commented 1 year ago

@bmahall We are a server-side service that gets the assertion from the client side and uses it to do OBO flow. We want to be able to do obo flow even after the assertions is expired.

ghost commented 1 year ago

@aleknik This issue has been automatically marked as stale because it is marked as requiring author feedback but has not had any activity for 5 days. If your issue has been resolved please let us know by closing the issue. If your issue has not been resolved please leave a comment to keep this open. It will be closed automatically in 7 days if it remains stale.

aleknik commented 1 year ago

Hey folks, any updates on this?

bgavrilMS commented 1 year ago

Hi @aleknik - not yet, we're working on bugs at the moment.

Next we will probably pick this up or making the token cache more performant. I have a preference for this scenario, because the token cache issues can be worked around.

SlyryD commented 11 months ago

Any update here? Thanks!

SlyryD commented 11 months ago

@bgavrilMS friendly ping

bgavrilMS commented 11 months ago

Hi @SlyryD, we do not have bandwidth to work on this feature request in the near term.

What is your OBO scenario here? Are you using a long running process which needs to maintain access to some data for a user well after their access token expires?

SlyryD commented 11 months ago

Hi @SlyryD, we do not have bandwidth to work on this feature request in the near term.

What is your OBO scenario here? Are you using a long running process which needs to maintain access to some data for a user well after their access token expires?

Hi @bgavrilMS. Correct, there are client apps sending expired access tokens to our service (probably due to clock skew between the client and server), so we handle renewing those tokens via OBO (using the refresh_token for subsequent requests when the token is close to expiry, according to the clock on our server).

bgavrilMS commented 11 months ago

Well, if a client sends expired access token to your web api, MSAL won't be able to get a refresh token anyway, because the OBO flow will fail.

The correct way to handle this scenario is for your web api to reply with 401 and optionally with WWW-Authenticate header with a claims value (the claims is read from the OBO error).

SlyryD commented 10 months ago

@bgavrilMS, my mistake. Let me explain some more.

We own a stateful service where the client has a long-lived WebSocket connection to our service. The issue is not with the first token the client sends us -- OBO succeeds and we can call downstream services with the resulting access token. But the client doesn't send us a new token until after the first token is expired, which means we have downtime where we can't call downstream services. With the refresh_token, we can make sure that doesn't happen, by using it to get another token when we get into that situation and need to call downstream services.

bgavrilMS commented 10 months ago

We will take a look at this in CY24Q1. Today, OBO call gets an RT but ignores it.

bgavrilMS commented 10 months ago

To implementer: see the .NET implementation of Long Running OBO for details.

SlyryD commented 10 months ago

Turning off offline_access should probably be an option, especially if msal-node doesn't use the refresh_token. Apparently it can speed up the request by 30% (that's just hearsay though :P).

SlyryD commented 6 months ago

@bgavrilMS any updates here? Should I open a PR?