Closed Coruscate5 closed 8 months ago
After several days of trying different methods - the current workaround is to have ITokenAcquisition request a new user token on the controller using the App Proxy scope in a way I've never seen before:
GUID/user_impersonation
The expected Scopes all fail, e.g. api://
and https://
, because apparently what Azure STS wants is the aud
to be set to the AppID directly of the App Proxy App Registration. I'm unsure how this could have been figured out without extreme trial and error.
In any other configuration, the Proxy does not accept the token and redirects to the sign-in page
We're glad you eventually found a workaround @Coruscate5.
We simply would like to use tokens from WASM to call additional Azure resources from Blazor.Server.
This is likely why you were running into issues. The guide at https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-microsoft-entra-id?view=aspnetcore-7.0 demonstrates how to set up the client app and server app with different registrations and Client IDs. The WASM app is a Single-page application (SPA) and the guide tells you to register it as such. This causes the token to have a shorter lifetime than a token that is only accessible on the server.
Cross-site scripting (XSS) attacks or compromised JS packages can steal the refresh token and use it remotely until it expires or is revoked. Application developers are responsible for reducing their application's risk to cross-site scripting. In order to minimize the risk of stolen refresh tokens, SPAs are issued tokens valid for 24 hours only. After 24 hours, the app must acquire a new authorization code via a top-level frame visit to the login page.
Security implications of refresh tokens in the browser | Microsoft Learn
DO NOT send access tokens that were issued to the middle tier to any other party. Access tokens issued to the middle tier are intended for use only by that middle tier.
Security risks of relaying access tokens from a middle-tier resource to a client (instead of the client getting the access tokens themselves) include:
Increased risk of token interception over compromised SSL/TLS channels. Inability to satisfy token binding and Conditional Access scenarios requiring claim step-up (for example, MFA, Sign-in Frequency). Incompatibility with admin-configured device-based policies (for example, MDM, location-based policies).
I attempted to resolve this through MSFT support to no avail - the core issue here is Proxy configuration, not the OBO flow (after investigation). You can reproduce this by creating any App Proxy and attempting to retrieve a valid token using the App URI that is automatically created (https://somesite/user_impersonation) and use it for a proxy call - it will always redirect programmatically back to the sign-in page (even using ConfidentialClient flow).
If using ConfidentialClient, the only scope that ends up working is GUID\.default
(since the .default scope modifier is required for confidential flows)
The problem appears to be with STS acceptance of App Proxy's URI as a Scope. It does not accept the custom auto-created URI, but instead works using this undocumented fix.
Likely this is either a bug in the Azure App Proxy, or something that skipped documentation - the Support Team ended up wanting me to create a full sample of the reproduction which I couldn't commit to (and since this is Azure Proxy, that wouldn't even work on a remote build). Is there a more proper place to put this? I don't think Azure STS/App Proxy has a dedicated git repo.
Is there an existing issue for this?
Describe the bug
I have created a sample application as described here: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-microsoft-entra-id?view=aspnetcore-7.0
The secured Hosted WebAPI now needs to use additional scopes to call OBO or consented permissions that are delegated on WebAPI2 (Azure Application Proxy, https://myproxy/user_impersonation), also in Azure.
The problem: The received token on the Hosted API always contains the wrong audience - we cannot use acquireTokenSilent on the token sent by WASM because it requires an additional consent for the additional scope with a different audience (WebAPI2).
This pattern is confirmed to work in Mobile applications (Xamarin) with custom Redirect URIs configured on the proxy. We have tried alternatives, client confidential applications, to authenticate on the back-end directly to the proxy, but the Entra pre-auth fails because it expects user interaction (MsalUiRequired).
We can see that the audience in the controller (WeatherForecastController), when secured by [Authorize], contains only the JWT scope and audience for the Server's App Registration, without the additional scopes required for Bearer authentication to the proxy object. Attempting to secure a new controller with authorization tied to the secondary WebAPI2 audience fails, likely because the server application is bound to a single ID:
There appears to be little/no documentation on this pattern, though I'd imagine it should be a common use case (Azure webapp calls for on-prem data from application proxy).
Expected Behavior
Hosted WebAPI should be able to receive the secondary token and retrieve additional API resource scopes silently. From what I understand, updates were made to WASM MSAL to obtain multiple tokens in sequence, but the binding context on front-end/back-end for Blazor under the Authorized component context only ever contains the DefaultScope.
Steps To Reproduce
Exceptions (if any)
No response
.NET Version
8.0.0
Anything else?
We could in theory call the Application Proxy directly from WASM, though of course CORS fails and this seems like a bizarre pattern. We simply would like to use tokens from WASM to call additional Azure resources from Blazor.Server.
Blazor WASM attempts to get multiple resource consent by doing the following - I can't tell if the additional scope is actually being acquired though: