user3061146
user3061146

Reputation: 1

Azure app Service authentication token not refreshing after calling .\auth\refresh endpoint

I have an angular app hosted in one azure windows app service and a asp.net core api hosted in another, both are protected using the app service authentication feature. I am using an app service plan which currently has two servers. I am using the token store and it is using the file system to store the tokens.

When the angular app calls the api it needs to pass an access token in the authorization header. The access token is retrieved by performing a GET on the \.auth\me endpoint and sending the AppServiceAuthSession cookie as the credential. The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the \.auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the \.auth\me to get the refreshed token.

All this works well when running on one server but when the app service plan is scaled to 2 or more servers the call to \.auth\refresh succeeds but the subsequent call to the .auth\me endpoint gets a token which has already expired. I can tell the token has expired by checking the internal exp claim and also the call to the api fails with a 401 Unauthorized when it would normally succeed.

If I scale back to one server the problem goes away and if I enable ARR affinity the problem goes away but I don't want to have to enable affinity just to resolve this.

Upvotes: 0

Views: 2956

Answers (2)

James Evans
James Evans

Reputation: 11

Bit late here but might be useful to someone! Also this information is based on a some correspondence with Microsoft support in 2021 but it is possible things might have changed or been fixed.

I think the issue is how the token store is configured. When running on more than one server in an app service plan you need to configure the token store to use blob storage rather than use the file storage.

Configure the tokenStore to use blog storage using appsettingsV2 configuration in the arm template.

"login": {
    "tokenStore": {
        "enabled": true,
        "tokenRefreshExtensionHours": 72,
        "azureBlobStorage": {
            "sasUrlSettingName": "WEBSITE_AUTH_TOKEN_CONTAINER_SASURL"
        }
    }
}

set the url to the blob storage using this environment variable WEBSITE_AUTH_TOKEN_CONTAINER_SASURL

https://learn.microsoft.com/en-us/archive/blogs/jpsanders/azure-app-service-authentication-using-a-blob-storage-for-token-cache

Alternatively set the environment variable to a key vault reference which then points to the blob storage url

{
    "name": "WEBSITE_AUTH_TOKEN_CONTAINER_SASURL",
    "value": "[concat('@Microsoft.KeyVault(SecretUri=https://',parameters('KVName'),'.vault.azure.net/secrets/', 'APPTokenStoreSAS',')')]"
},

Other useful links

Restarting Azure App Service on Linux with Azure Active Directory authentication resets /.auth/me/

https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet

Upvotes: 1

anon
anon

Reputation:

also the call to the api fails with a 401 Unauthorized when it would normally succeed.

You shouldn't be calling /.auth/refresh on the API app. You must call /.auth/refresh on the WEB app because the auth token is produced on the WEB app.

Alternative to the bearer token is the AppServiceAuthSession cookie from your client request to /.auth/refresh.

The returned token is then cached in session storage and used for subsequent requests until the token expires. When the token expires I call the .auth\refresh endpoint (and send the AppServiceAuthSession cookie) and then call the .auth\me to get the refreshed token.

We have a user-facing web application that calls an API backend, which in turn queries the MSFT graph, just to be sure we have the context correctly captured. At each phase, we anticipate a distinct AAD registration:

  • Web app registration (with permission to API app)
  • API app registration (with permission to Microsoft Graph)

Our initial assumption is that the user hasn't explicitly given their agreement to using the Microsoft Graph because this looked to be tied to adding the graph permission. Since the user isn't involved if you're merely updating the existing token, there isn't a chance for that consent to be obtained. The user would need to re-login with an explicit consent prompt, which could be done by calling /.auth/login/aad?prompt=consent. But we wouldn't anticipate that to cause the expiration problem. I'm speculating here, but it's possible that a cached, outdated value is being utilised in place of the permission error. There would need to be input from Connor.

The logs from the actual Authentication / Authorization layer itself, particularly if there is an error message, are another piece of information that would be useful to have. To obtain these logs:

  • From your app in the portal (the API app is what I'm most interested in): “Platform features” > “Diagnostic logs”. Change “Application Logging (Filesystem)” to “On” and set the level to “verbose.” Hit Save.
  • Back to “Platform features” > “Log stream” and keep that window open as you reproduce the issue (in this case, the call to the API app)

Actually, the issue is that AAD bearer tokens are incompatible with the /.auth/refresh API. Instead, you must use a session token (_x-zumo-auth_ header) or a session cookie (_AppServiceAuthSession_ cookie), which are the results of a login activity. For more info - see here #refreshing-the-access-tokens

Upvotes: 0

Related Questions