AzureAD / MSAL.PS

MIT License
163 stars 29 forks source link

AADSTS50196: The server terminated an operation because it encountered a client request loop #10

Closed DarkLite1 closed 4 years ago

DarkLite1 commented 4 years ago

First of all thank you for this wonderful wrapper around msal. It really helped to simplify our code. We use the following code to retrieve a valid access token:

$msalParams = @{
    ClientId              = $azureClientId
    TenantId              = $azureTenantId
    IntegratedWindowsAuth = $true
    Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
}
$token = Get-MsalToken @msalParams

This code is invoked multiple times by different PowerShell scripts. We assume it would retrieve a valid token from the cache and if there's no valid token it will try to acquire a new token silently. However, after multiple invocations the following error is thrown:

AADSTS50196: The server terminated an operation because it encountered a client request loop. Please contact your app vendor. Trace ID: 46e48f1f-ecfc-4b82-90a1-10b591f2a500 Correlation ID: c52d4881-e115-4a02-be39-9e46686e47fc Timestamp: 2020-06-17 07:52:06Z

Looking further into this, this seems to be a reported issue at msal. A remark from @jasonnutter:

Correct, if you get this error, it is because your requests are being throttled, which is due to making too many request in a short period of time. This can be mitigated by reducing the number of tokens you are requesting, and/or ensuring that you are retrieving tokens from the cache.

Is it possible that the CmdLet Get-MsalToken is not looking into the cache but requesting a token from Azure each time on invocation?

To reproduce this issue:


(0..50) | ForEach-Object {
    $msalParams = @{
        ClientId              = $azureClientId
        TenantId              = $azureTenantId
        IntegratedWindowsAuth = $true
        Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
    }
    $token = Get-MsalToken @msalParams
}
DarkLite1 commented 4 years ago

I found the -Silent switch which can be used to accommodate the desired behavior. The only issue is it cannot be combined with the the switch -IntegratedWindowsAuth. Ideally one would like to do this:

(0..50) | ForEach-Object {
    $msalParams = @{
        ClientId              = $azureClientId
        TenantId              = $azureTenantId
        IntegratedWindowsAuth = $true
        Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
    }
    $token = Get-MsalToken @msalParams -Silent
}

Because that is not possible we now require two steps to get the same result:

  1. Acquire a token with -IntegratedWindowsAuth:

    $msalParams = @{
    ClientId              = $azureClientId
    TenantId              = $azureTenantId
    IntegratedWindowsAuth = $true
    Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
    }
    $token = Get-MsalToken @msalParams 
  2. Refresh the token:

    $msalParams = @{
    ClientId              = $azureClientId
    TenantId              = $azureTenantId
    Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
    }
    $token = Get-MsalToken @msalParams -Silent

Would it be possible to combine both steps?

jazuntee commented 4 years ago

Do you have requirement to only allow IntegratedWindowsAuth? Do you get the desired behavior when you remove the IntegratedWindowsAuth parameter? By default, the Get-MsalToken command will attempt a silent auth first, then IWA, and finally if all else fails it will do interactive in a web browser.

(0..50) | ForEach-Object {
    $msalParams = @{
        ClientId              = $azureClientId
        TenantId              = $azureTenantId
        Scopes                = "https://outlook.office.com/EWS.AccessAsUser.All"
    }
    $token = Get-MsalToken @msalParams
}
DarkLite1 commented 4 years ago

If I remember correctly I really need the switch IntegratedWindowsAuth = $true because in the Azure App Registration I have the following :

image

jazuntee commented 4 years ago

That switch determines what client application types the AAD service principal will accept which is either Public or Confidential. https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-client-applications

The Get-MsalToken command will always use public client application object in MSAL when ClientSecret and ClientCertificate parameters are omitted.

DarkLite1 commented 4 years ago

In any case, I can tell you with certainty that when I did my test I really needed to add the -silent switch to have the token refreshed silently. If this was omitted it would not renew the token. Might have been my setup in Azure or something else, I just wanted you to know.

jazuntee commented 4 years ago

Just to clarify, MSAL will continue to return the same Access Token from the token cache until it expires or you specify the -ForceRefresh parameter.

I just tested without -IntegratedWindowsAuth or -Silent and it all appears to work silently as expected. Maybe this got fixed somewhere along the way but let me know if you are still unable to get it working.

## The same command does Integrated Windows Auth the first time and Silent for all subsequent call.
Get-MsalToken -ClientId $ClientId -TenantId mytenant.onmicrosoft.com