Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 9, 2026

Azure AD requires the scope parameter in token endpoint requests, but Blazor WASM's IAccessTokenProvider.RequestAccessToken() parameterless overload doesn't send it. This caused authentication to succeed but fail authorization, redirecting users to /authentication/login-failed.

Changes

  • WasmOidcTokenAccessor: Request tokens with explicit resource scopes via RequestAccessToken(new AccessTokenRequestOptions { Scopes = [...] }). Filters standard OIDC scopes (openid, profile, email, offline_access) and passes only API/resource scopes.

  • ServiceCollectionExtensions: Register OidcOptions as singleton for DI injection.

  • README.md: Add Azure AD configuration guide covering single-resource scope limitation, app registration setup, and troubleshooting for AADSTS errors.

Example

// Before: No scopes passed to token endpoint
var tokenResult = await _tokenProvider.RequestAccessToken();

// After: API scopes explicitly passed
var resourceScopes = _options.Scopes
    ?.Where(s => !standardScopes.Contains(s))
    .ToArray() ?? Array.Empty<string>();

var tokenResult = await _tokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions { Scopes = resourceScopes });

Azure AD now receives the scope parameter during token exchange, fixing authentication for SignalR connections and API calls.

Original prompt

There seems to be something fundamentally wrong with the Blazor WASM implementartion of the OpenIDConnect library. When I try to login, everything seems to work but I end up being redirected to the https://localhost:7052/authentication/login-failed page with the following error in the console:

info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.

The auth-interop.js was used for troubleshooting, which produces console output like this:


[auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
content.js:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
MudBlazor.min.js:1 Debugging hotkey: Shift+Cmd+D (when application has focus)
content.js:50 [auth-interop] Intercepted XHR token request to: https://login.microsoftonline.com/f35bcd45-7991-4e24-84f1-e964394501ad/oauth2/v2.0/token
content.js:53 [auth-interop] Original body length: 2488
content.js:58 [auth-interop] Added scope to token request
content.js:59 [auth-interop] Updated body length: 2597
content.js:70 [auth-interop] Captured ID token from response
content.js:30 [auth-interop] Intercepting userinfo request - will return empty response
6login-failed:1 Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was receivedUnderstand this error
Navigated to https://localhost:7052/authentication/login-failed
VM261:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM261:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
VM248:1 Debugging hotkey: Shift+Alt+D (when application has focus)
VM283:8 [Violation] 'setTimeout' handler took 407ms
VM305:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM305:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
Navigated to https://localhost:7052/
VM368:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM368:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
VM372:1 Debugging hotkey: Shift+Alt+D (when application has focus)
VM405:8 [Violation] 'setTimeout' handler took 412ms
VM439:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM439:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
VM404:3 info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
Navigated to https://localhost:7052/authentication/login
VM494:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM494:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
VM480:1 Debugging hotkey: Shift+Alt+D (when application has focus)
VM523:8 [Violation] 'setTimeout' handler took 408ms
VM550:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM550:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
VM582:9 [auth-interop] Will use scopes (single resource): openid profile offline_access api://dda3270c-997e-413a-9175-36b70134547c/elsa-server-api
VM582:121 [auth-interop] Azure AD compatibility patches initialized (scope + userinfo intercept)
Navigated to https://login.microsoftonline.com/f35bcd45-7991-4e24-84f1-e964394501ad/oauth2/v2.0/authorize?client_id=0078a6b1-54d9-438b-9223-e26f07191949&redirect_uri=https%3A%2F%2Flocalhost%3A7052%2Fauthentication%2Flogin-callback&response_type=code&scope=openid%20profile%20offline_access%20api%3A%2F%2Fdda3270c-997e-413a-9175-36b70134547c%2Felsa-server-api&state=e0b23bc42fa14946bab6256a65b8cb69&code_challenge=th1NwqZM4XtI_1O39A-eLEcsYchUzJJsB8OlPXDyWxA&code_challenge_method=S256&response_mode=query
Navigated to https://localhost:7052/authentication/login-callback?code=1.AXoARc1b85F5JE6E8elkOUUBrbGmeADZVItDkiPibwcZGUl6AAB6AA.BQABBAIAAAADAOz_BQD0_0V2b1N0c0FydGlmYWN0cwIAAAAAAHUryNZPafThvrNIkCpaxcP1t0Uf5iHsC0VWXNDSu4ERMU05_B3zmfMHbFjLBhpgoh2H9_IjKQmVHMID_wf5luD3m9DtCW-7hIh_t0675JSdDX7i3yJ_K_XeT1LQUvpg2zF1D7ghpmhEeZve0vH69ppkYNMeVhMsg7JO58HwOaNOgUKIhIuvNoaCKspqr_yw1fM0FPLfBZ_awP967z-Or_OFuZb4SXsFf1Ly2KLgvkAjwKjzEUGpp63OJVXxrp8-plHdHF-zkfhgoyxHfEcaIJJpTMqytg6WMFZ...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits January 9, 2026 18:48
…en requests

- Updated WasmOidcTokenAccessor to request access tokens with explicit resource scopes
- Filter out standard OIDC scopes (openid, profile, email, offline_access) and pass only API scopes
- Register OidcOptions in DI container so WasmOidcTokenAccessor can access configured scopes
- This ensures Azure AD receives scope parameter during token exchange, fixing AADSTS errors

Co-authored-by: sfmskywalker <[email protected]>
- Document Azure AD app registration setup and requirements
- Explain single-resource scope limitation (no mixing Graph + custom API scopes)
- Add troubleshooting guide for common Azure AD errors (AADSTS28000, AADSTS28003)
- Update example to use AddElsaOidcAuthentication instead of AddOidcAuthentication
- Document that standard OIDC scopes are automatically filtered

Co-authored-by: sfmskywalker <[email protected]>
@sfmskywalker sfmskywalker marked this pull request as ready for review January 9, 2026 18:51
Copilot AI review requested due to automatic review settings January 9, 2026 18:51
- Register OidcOptions as singleton instance instead of using Configure<T>
- Remove IOptions<T> dependency from WasmOidcTokenAccessor
- Add null check for Scopes array to prevent NullReferenceException
- Simplify DI registration pattern

Co-authored-by: sfmskywalker <[email protected]>
Copilot AI changed the title [WIP] Fix OpenIDConnect login redirect issue in Blazor WASM Fix Azure AD authentication in Blazor WASM by passing explicit API scopes during token exchange Jan 9, 2026
Copilot AI requested a review from sfmskywalker January 9, 2026 18:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Azure AD authentication issues in Blazor WebAssembly by ensuring that API scopes are explicitly passed during token exchange requests. Azure AD requires the scope parameter in token endpoint requests, which wasn't being sent when using the parameterless RequestAccessToken() method.

Key Changes:

  • Modified token accessor to filter standard OIDC scopes and request tokens with explicit resource scopes for Azure AD compatibility
  • Registered OidcOptions in the DI container to make configured scopes accessible to the token accessor service
  • Added comprehensive Azure AD/Microsoft Entra ID documentation with setup instructions, troubleshooting guide, and configuration examples

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/modules/Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm/Services/WasmOidcTokenAccessor.cs Enhanced to filter standard OIDC scopes and request access tokens with explicit resource scopes, ensuring Azure AD receives the required scope parameter during token exchange
src/modules/Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm/Extensions/ServiceCollectionExtensions.cs Added registration of OidcOptions in the DI container so the token accessor can access configured scopes
src/modules/Elsa.Studio.Authentication.OpenIdConnect/README.md Added Azure AD-specific configuration section with app registration setup, scope configuration, troubleshooting guide, and updated examples to use AddElsaOidcAuthentication
Comments suppressed due to low confidence (1)

src/modules/Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm/Extensions/ServiceCollectionExtensions.cs:48

  • The options registration pattern is redundant. Instead of creating an OidcOptions instance, configuring it, and then manually copying each property into services.Configure, you can directly register the configured instance. Consider using: services.Configure<OidcOptions>(configure); or services.AddSingleton(Options.Create(options));. The current approach requires maintaining property-by-property copying which is error-prone if new properties are added to OidcOptions.
        services.AddSingleton(options);

        // Register the token accessor
        services.AddScoped<IOidcTokenAccessor, WasmOidcTokenAccessor>();
        services.AddScoped<IAuthenticationProvider, OidcAuthProvider>();
        services.AddScoped<IFeature, OpenIdConnectBlazorWasmFeature>();

        // Configure WASM authentication using the built-in framework.
        // Note: Entra ID requires absolute redirect URIs.
        services.AddOidcAuthentication(wasmOptions =>
        {
            wasmOptions.ProviderOptions.Authority = options.Authority;
            wasmOptions.ProviderOptions.ClientId = options.ClientId;
            wasmOptions.ProviderOptions.ResponseType = options.ResponseType;

sfmskywalker and others added 2 commits January 9, 2026 19:58
…m/Services/WasmOidcTokenAccessor.cs

Co-authored-by: Copilot <[email protected]>
…m/Services/WasmOidcTokenAccessor.cs

Co-authored-by: Copilot <[email protected]>
@sfmskywalker sfmskywalker merged commit 12dcd61 into copilot/implement-elsastudio-authentication-oidc Jan 9, 2026
2 checks passed
@sfmskywalker sfmskywalker deleted the copilot/fix-openidconnect-login-issue branch January 9, 2026 18:59
sfmskywalker added a commit that referenced this pull request Jan 9, 2026
…er abstractions (#721)

* Initial plan

* Phase 1-3 complete: Core OIDC module with Server and WASM implementations

Co-authored-by: sfmskywalker <[email protected]>

* Add shared authentication abstractions for multi-provider support

Co-authored-by: sfmskywalker <[email protected]>

* Add comprehensive authentication architecture documentation

Co-authored-by: sfmskywalker <[email protected]>

* Final: Add implementation summary for PR review

Co-authored-by: sfmskywalker <[email protected]>

* Add authentication infrastructure and modules for Elsa Studio, including ElsaAuth and OpenID Connect integration.

* Update project references to include new Elsa Studio Authentication modules.

* Update `RedirectToLoginUnauthorizedComponentProvider` to support fallback to default Unauthorized component when `IAuthorizationService` is unavailable.

* Mark `Elsa.Studio.Login` APIs as obsolete and migrate authentication to `Elsa.Studio.Authentication.ElsaAuth`. Simplify dependencies and refactor JWT parsing for BlazorServer and BlazorWasm modules.

* Switch to OpenID Connect authentication, remove legacy login module, and update service registration methods.

* Replace `Login` module with OpenID Connect, update authentication pipeline, and revise default `GetClaimsFromUserInfoEndpoint`.

* Replace `BearerTokenHttpMessageHandler` with `AuthenticatingApiHttpMessageHandler` and remove obsolete references

* Organize solution structure by adding new folders: authentication, localization, workflows, deprecated, samples, and dashboard. Remove obsolete project references.

* Refactor authentication: replace legacy services, update OIDC implementation, and restructure PKCE flow

* Add `Elsa.Studio.Authentication.ElsaAuth.UI` module to provide Elsa Identity authentication with a login UI and unauthorized redirect behavior.

* Migrate `AUTHENTICATION_ARCHITECTURE.md` to `doc/` folder, update project references, and refine namespace imports for authentication module.

* Introduce `IAnonymousBackendApiClientProvider` and refactor API client creation to support non-authenticated backend calls.

* Add token refresh mechanism for OpenID Connect and Elsa Identity authentication modules. Introduce token refresh coordinators, configuration providers, and support for silent token refresh. Update related services and integrate advanced options for customization.

* Add persisted token refresh for OpenID Connect in Blazor Server: implement browser-side pings, background services, and configurable strategies.

* Remove persisted token refresh strategy and related services from OpenID Connect configuration.

* Refactor OIDC configuration for Blazor WebAssembly: add Azure AD compatibility patches, improve URI handling, and modularize features.

* Fix Azure AD authentication in Blazor WASM by passing explicit API scopes during token exchange (#722)

* Initial plan

* Fix Azure AD authentication by passing explicit API scopes during token requests

- Updated WasmOidcTokenAccessor to request access tokens with explicit resource scopes
- Filter out standard OIDC scopes (openid, profile, email, offline_access) and pass only API scopes
- Register OidcOptions in DI container so WasmOidcTokenAccessor can access configured scopes
- This ensures Azure AD receives scope parameter during token exchange, fixing AADSTS errors

Co-authored-by: sfmskywalker <[email protected]>

* Add Azure AD configuration documentation for Blazor WASM

- Document Azure AD app registration setup and requirements
- Explain single-resource scope limitation (no mixing Graph + custom API scopes)
- Add troubleshooting guide for common Azure AD errors (AADSTS28000, AADSTS28003)
- Update example to use AddElsaOidcAuthentication instead of AddOidcAuthentication
- Document that standard OIDC scopes are automatically filtered

Co-authored-by: sfmskywalker <[email protected]>

* Address code review feedback

- Register OidcOptions as singleton instance instead of using Configure<T>
- Remove IOptions<T> dependency from WasmOidcTokenAccessor
- Add null check for Scopes array to prevent NullReferenceException
- Simplify DI registration pattern

Co-authored-by: sfmskywalker <[email protected]>

* Update src/modules/Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm/Services/WasmOidcTokenAccessor.cs

Co-authored-by: Copilot <[email protected]>

* Update src/modules/Elsa.Studio.Authentication.OpenIdConnect.BlazorWasm/Services/WasmOidcTokenAccessor.cs

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: sfmskywalker <[email protected]>
Co-authored-by: Sipke Schoorstra <[email protected]>
Co-authored-by: Copilot <[email protected]>

* Remove obsolete Azure AD compatibility patches and cleanup related JavaScript and Razor components.

* Refactor OpenID Connect callback path handling: use null-coalescing assignments and remove default path values from `OidcOptions`.

* Add token purposes and scoped token caching for enhanced authentication configuration

* Add scoped access token capabilities and token-purpose configuration

Introduce `IScopedAccessTokenProvider`, `IOidcTokenAccessorWithScopes`, and associated models to enable scope-aware token acquisition based on token purposes. Update handlers to support backend API scopes and implement scoped token caching for multi-audience token scenarios.

* Refactor authentication modules: simplify scoped token handling, update OIDC providers, and enhance incremental consent support.

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: sfmskywalker <[email protected]>
Co-authored-by: Sipke Schoorstra <[email protected]>
Co-authored-by: Sipke Schoorstra <[email protected]>
Co-authored-by: Copilot <[email protected]>
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

Successfully merging this pull request may close these issues.

2 participants