Skip to content

Commit 2192884

Browse files
sfmskywalkerCopilotCopilot
authored
Refactor Authentication Framework and implement OpenID Connect (#723)
* Refactor OpenID Connect PKCE service implementation to consolidate logic and improve session storage management. * Add support for client secrets and enhance OpenID Connect PKCE flow with session state management. * Add `UseOpenIdConnect` extension method to simplify OIDC configuration for BlazorServer and BlazorWasm modules * Add `Elsa.Studio.Authentication.OpenIdConnect` module to provide OpenID Connect-based authentication for Elsa Studio * Switch to OpenID Connect for authentication, update configuration, and add OIDC script reference. * Remove default value for `UsePkce` in OpenIdConnectConfiguration to align with existing conventions. * Remove unused `wwwroot` folder reference from `Elsa.Studio.Login.csproj`. * Add foundational types for OpenID Connect PKCE flow authentication * 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. * Add standalone OpenID Connect authentication module with multi-provider 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]> * Refactor authentication modules: replace `IScopedAccessTokenProvider` with streamlined scope-aware token handling, introduce generic component providers, and enhance JWT accessor abstractions. * Refactor authentication modules: replace `GenericComponentProvider` with `UnauthorizedComponentProvider`, streamline component references, and mark `LoginFeature` as obsolete. * Update authentication architecture documentation to reflect actual codebase (#724) * Initial plan * Update authentication architecture documentation with accurate module information Co-authored-by: sfmskywalker <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sfmskywalker <[email protected]> * Simplify token access with explicit methods instead of string-based lookups (#725) * Initial plan * Simplify token access architecture - replace string-based token names with explicit methods Co-authored-by: sfmskywalker <[email protected]> * Update documentation to reflect simplified token access architecture Co-authored-by: sfmskywalker <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sfmskywalker <[email protected]> Co-authored-by: Sipke Schoorstra <[email protected]> * Remove `IScopedAccessTokenProvider`, related properties, and simplify OIDC scope configuration * Refactor authentication modules: remove obsolete abstractions, streamline configuration setup, and introduce provider-specific HTTP connection options. * Remove obsolete unauthorized component providers and migrate to generic `UnauthorizedComponentProvider`. Streamline default provider registrations. * Refactor authentication modules: replace `ITokenRefreshCoordinator` with `ISingleFlightCoordinator`, remove obsolete abstractions, and streamline service registrations across modules. * Refactor authentication modules: replace `IOidcTokenAccessor` with `ITokenProvider`, simplify service registrations, and enhance Blazor WASM and Server OIDC token handling. * Remove obsolete persisted OIDC refresh services and replace with `AuthCookieEvents` for token refresh logic. Simplify Blazor Server implementation by leveraging `CookieAuthenticationEvents.OnValidatePrincipal`. * Remove `IScopedTokenCache` and related implementations. Simplify Blazor Server OIDC token handling by replacing scoped token cache with in-memory dictionary and updating `ServerTokenProvider` logic. Add Polly-based retry policy for token refresh HTTP client. * Update package versions: upgrade `Elsa.Api.Client` to `3.6.0-rc2`, replace `Http.Resilience` with `Http.Polly`, and realign framework-specific dependencies. * Apply file-scoped namespace declarations across the solution to align with modern C# practices. * Update `AUTHENTICATION_ARCHITECTURE.md` to reflect updated Blazor Server and WASM authentication workflows, replace obsolete components, and document new token refresh flow. * Update default authentication provider to `ElsaAuth` and adjust related documentation and configuration. Refactor `ITokenProvider` interface and improve token handling description. * Remove `AddAuthenticationInfrastructure` and related references. Clean up obsolete extensions and update service registrations in authentication modules. Adjust documentation to reflect changes. * Refactor authentication modules: consolidate duplicate JWT parsers into a single implementation, remove legacy services and interfaces, and streamline service registrations. * Consolidate authentication service registrations: simplify `AddElsaAuth` and `AddElsaAuthUI` extensions, remove redundant `Blazored.LocalStorage` and JWT parser registrations, and streamline usage across Blazor Server and WASM. * Update `AddOpenIdConnectAuth` naming and replace `ITimeZoneProvider` implementation with `LocalTimeZoneProvider`. Clean up redundant service and variable usages in Blazor Server and WASM hosts. * Update authentication provider to `ElsaAuth` in configurations and remove redundant comments and unused application URLs. * Remove `LoginState` component and associated `LoginFeature` logic from `ElsaAuth` authentication modules. * Add README files for `Elsa.Studio.Authentication` modules: core, Blazor Server, Blazor WebAssembly, and OpenID Connect modules. Document architecture, setup instructions, and troubleshooting details. * Refactor `IAuthenticationProvider` to `ITokenProvider` across `ElsaAuth` modules and rename `JwtAuthenticationProvider` to `JwtTokenProvider` for improved clarity and consistency. * Update references from `IAuthenticationProvider` to `ITokenProvider` in README and architecture docs for consistency * Update localization module to use nullable result in `blazorCulture.get` and switch default authentication to `OpenIdConnect` * Remove `AZURE_AD_BLAZOR_WASM_AUTH_PLAN.md` as it is no longer relevant to the updated architecture and authentication strategy. * Rename `ElsaAuth` modules and references to `ElsaIdentity` for alignment with updated authentication strategy. Remove deprecated components and update relevant configurations, service registrations, and documentation accordingly. * Remove redundant null assignment to `Authorization` header in `ElsaIdentityAuthenticatingApiHttpMessageHandler`. * Simplify `AccessTokenProvider` lambda expression in `ElsaAuthHttpConnectionOptionsConfigurator`. * Refactor `ServerTokenProvider` and `TokenRefreshService` constructors to use primary constructor syntax for improved readability and reduced boilerplate. Adjust usages to align with updated structure. * Remove redundant `<remarks>` section from `AddOidcAuthentication` method documentation. * Remove `AppBaseUrl` property and related logic from OIDC options and configuration as it is no longer required. * Add support for legacy Elsa Login authentication module and update project references * Refactor `WorkflowInstanceObserverFactory` to enable asynchronous configuration of `HttpConnectionOptions` during SignalR connection setup. * Fix authentication bugs and code quality issues from PR #723 review (#726) * Initial plan * Address code review feedback: fix authentication condition, improve documentation, and simplify nested if statements Co-authored-by: sfmskywalker <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sfmskywalker <[email protected]> Co-authored-by: Sipke Schoorstra <[email protected]> * Add configurable `NameClaimType` and `RoleClaimType` to OIDC options and update token validation parameters accordingly --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: sfmskywalker <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent 3eb007f commit 2192884

File tree

123 files changed

+5988
-904
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

123 files changed

+5988
-904
lines changed

Directory.Packages.props

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
55
</PropertyGroup>
66
<ItemGroup Label="Elsa">
7-
<PackageVersion Include="Elsa.Api.Client" Version="3.6.0-preview.4162" />
7+
<PackageVersion Include="Elsa.Api.Client" Version="3.6.0-rc2" />
88
</ItemGroup>
99
<ItemGroup>
1010
<PackageVersion Include="Blazored.FluentValidation" Version="2.2.0" />
@@ -26,6 +26,7 @@
2626
</ItemGroup>
2727
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
2828
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.22" />
29+
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.22" />
2930
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="8.0.22" />
3031
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.22" />
3132
<PackageVersion Include="Microsoft.AspNetCore.Components.CustomElements" Version="8.0.22" />
@@ -34,13 +35,15 @@
3435
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.22" />
3536
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.22" PrivateAssets="all" />
3637
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.22" />
37-
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.22" />
38-
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.22" />
3938
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.11" />
4039
<PackageVersion Include="Microsoft.JSInterop" Version="8.0.22" />
40+
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.22" />
41+
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.22" />
42+
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.11" />
4143
</ItemGroup>
4244
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
4345
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.11" />
46+
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.11" />
4447
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.11" />
4548
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.11" />
4649
<PackageVersion Include="Microsoft.AspNetCore.Components.CustomElements" Version="9.0.11" />
@@ -49,13 +52,15 @@
4952
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.11" />
5053
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.11" PrivateAssets="all" />
5154
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.11" />
52-
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.11" />
5355
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="9.0.11" />
56+
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.11" />
5457
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.11" />
5558
<PackageVersion Include="Microsoft.JSInterop" Version="9.0.11" />
59+
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.11" />
5660
</ItemGroup>
5761
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
5862
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.1" />
63+
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.1" />
5964
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="10.0.1" />
6065
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.1" />
6166
<PackageVersion Include="Microsoft.AspNetCore.Components.CustomElements" Version="10.0.1" />
@@ -68,5 +73,6 @@
6873
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="10.0.1" />
6974
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.1" />
7075
<PackageVersion Include="Microsoft.JSInterop" Version="10.0.1" />
76+
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="10.0.1" />
7177
</ItemGroup>
7278
</Project>

Elsa.Studio.sln

Lines changed: 381 additions & 16 deletions
Large diffs are not rendered by default.

doc/AUTHENTICATION_ARCHITECTURE.md

Lines changed: 428 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace Elsa.Studio.Contracts;
4+
5+
/// <summary>
6+
/// Provides API clients to the backend for anonymous (non-authenticated) calls.
7+
/// </summary>
8+
/// <remarks>
9+
/// This provider is intended for endpoints like <c>/identity/login</c> where attaching an access token is not required
10+
/// and can even be harmful (e.g., stale tokens, circular dependencies during sign-in).
11+
/// </remarks>
12+
public interface IAnonymousBackendApiClientProvider
13+
{
14+
/// <summary>
15+
/// Gets the URL to the backend.
16+
/// </summary>
17+
Uri Url { get; }
18+
19+
/// <summary>
20+
/// Gets an API client that does not attach access tokens.
21+
/// </summary>
22+
/// <typeparam name="T">The API client type.</typeparam>
23+
ValueTask<T> GetApiAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(CancellationToken cancellationToken = default) where T : class;
24+
}

src/framework/Elsa.Studio.Core/Contracts/IBackendApiClientProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
13
namespace Elsa.Studio.Contracts;
24

35
/// <summary>
@@ -15,5 +17,5 @@ public interface IBackendApiClientProvider
1517
/// </summary>
1618
/// <typeparam name="T">The API client type.</typeparam>
1719
/// <returns>The API client.</returns>
18-
ValueTask<T> GetApiAsync<T>(CancellationToken cancellationToken = default) where T : class;
20+
ValueTask<T> GetApiAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(CancellationToken cancellationToken = default) where T : class;
1921
}
Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
1-
namespace Elsa.Studio.Contracts
1+
using Elsa.Studio.Models;
2+
3+
namespace Elsa.Studio.Contracts;
4+
5+
/// <summary>
6+
/// Defines a contract for visualizing objects into Visualized representations.
7+
/// </summary>
8+
public interface IContentVisualizer
29
{
310
/// <summary>
4-
/// Defines a contract for visualizing objects into Visualized representations.
11+
/// Gets the name of the content visualizer.
512
/// </summary>
6-
public interface IContentVisualizer
7-
{
8-
/// <summary>
9-
/// Gets the name of the content visualizer.
10-
/// </summary>
11-
string Name { get; }
13+
string Name { get; }
1214

13-
/// <summary>
14-
/// Returns the syntax used by this visualizer.
15-
/// </summary>
16-
string Syntax { get; }
15+
/// <summary>
16+
/// Returns the syntax used by this visualizer.
17+
/// </summary>
18+
string Syntax { get; }
1719

18-
/// <summary>
19-
/// Determines whether the content can be visualized with this visualizer.
20-
/// </summary>
21-
/// <param name="input">The object to evaluate for visualizing capability. Cannot be <see langword="null"/>.</param>
22-
/// <returns><see langword="true"/> if the input can be visualized; otherwise, <see langword="false"/>.</returns>
23-
bool CanVisualize(object input);
20+
/// <summary>
21+
/// Determines whether the content can be visualized with this visualizer.
22+
/// </summary>
23+
/// <param name="input">The object to evaluate for visualizing capability. Cannot be <see langword="null"/>.</param>
24+
/// <returns><see langword="true"/> if the input can be visualized; otherwise, <see langword="false"/>.</returns>
25+
bool CanVisualize(object input);
2426

25-
/// <summary>
26-
/// Returns a prettified text version of the input.
27-
/// </summary>
28-
string? ToPretty(object input);
27+
/// <summary>
28+
/// Returns a prettified text version of the input.
29+
/// </summary>
30+
string? ToPretty(object input);
2931

30-
/// <summary>
31-
/// Returns tabular representation of the input.
32-
/// Returns <see langword="null"/> if not supported.
33-
/// </summary>
34-
TabulatedContentVisualizer? ToTable(object input);
35-
}
32+
/// <summary>
33+
/// Returns tabular representation of the input.
34+
/// Returns <see langword="null"/> if not supported.
35+
/// </summary>
36+
TabulatedContentVisualizer? ToTable(object input);
3637
}
Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
namespace Elsa.Studio.Contracts
1+
namespace Elsa.Studio.Contracts;
2+
3+
/// <summary>
4+
/// Provides content visualizers.
5+
/// </summary>
6+
public interface IContentVisualizerProvider
27
{
38
/// <summary>
4-
/// Provides content visualizers.
9+
/// Attempts to resolve the best matching visualizer for the input.
510
/// </summary>
6-
public interface IContentVisualizerProvider
7-
{
8-
/// <summary>
9-
/// Attempts to resolve the best matching visualizer for the input.
10-
/// </summary>
11-
/// <param name="content">The content to check visualizers against.</param>
12-
/// <returns></returns>
13-
IContentVisualizer MatchOrDefault(object content);
11+
/// <param name="content">The content to check visualizers against.</param>
12+
/// <returns></returns>
13+
IContentVisualizer MatchOrDefault(object content);
1414

15-
/// <summary>
16-
/// Returns all content visualizers.
17-
/// </summary>
18-
/// <returns></returns>
19-
IEnumerable<IContentVisualizer> GetAll();
20-
}
15+
/// <summary>
16+
/// Returns all content visualizers.
17+
/// </summary>
18+
/// <returns></returns>
19+
IEnumerable<IContentVisualizer> GetAll();
2120
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Elsa.Studio.Contracts;
2+
3+
/// <summary>
4+
/// Coordinates operations to prevent multiple concurrent executions.
5+
/// </summary>
6+
public interface ISingleFlightCoordinator
7+
{
8+
/// <summary>
9+
/// Ensures only one operation runs at a time for the current scope.
10+
/// </summary>
11+
Task<T> RunAsync<T>(Func<CancellationToken, Task<T>> action, CancellationToken cancellationToken);
12+
}

src/framework/Elsa.Studio.Core/Contracts/IUIFieldExtensionHandler.cs

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,50 @@
22
using Elsa.Studio.Models;
33
using Microsoft.AspNetCore.Components;
44

5-
namespace Elsa.Studio.Contracts
5+
namespace Elsa.Studio.Contracts;
6+
7+
/// <summary>
8+
/// Provides a contract for UI field extension handlers.
9+
/// </summary>
10+
public interface IUIFieldExtensionHandler
611
{
712
/// <summary>
8-
/// Provides a contract for UI field extension handlers.
9-
/// </summary>
10-
public interface IUIFieldExtensionHandler
11-
{
12-
/// <summary>
13-
/// The order in which the extensions should be displayed.
14-
/// </summary>
15-
int DisplayOrder { get; set; }
16-
17-
/// <summary>
18-
/// If set to true and no restricting types are set, either with <c>UIHintComponent</c>, <c>ActivityTypes</c> or <c>Syntaxes</c>, this extension will be rendered for in all field types.
19-
/// </summary>
20-
bool IncludeForAll { get; set; }
21-
22-
/// <summary>
23-
/// The position to render the extension within the field.
24-
/// </summary>
25-
FieldExtensionPosition Position { get; set; }
26-
27-
/// <summary>
28-
/// The UIHint component this extension should be rendered for.
29-
/// </summary>
30-
string UIHintComponent { get; set; }
31-
32-
/// <summary>
33-
/// The activities this extension should be rendered for.
34-
/// </summary>
35-
List<string> ActivityTypes { get; set; }
36-
37-
/// <summary>
38-
/// The syntaxes this extension should be rendered for.
39-
/// </summary>
40-
List<string> Syntaxes { get; set; }
41-
42-
/// <summary>
43-
/// Returns true if the handler extension the specified or is empty.
44-
/// </summary>
45-
bool GetExtensionForInputComponent(string componentName);
46-
47-
/// <summary>
48-
/// Returns a <see cref="RenderFragment"/> of the added extension.
49-
/// </summary>
50-
RenderFragment DisplayExtension(DisplayInputEditorContext context);
51-
}
13+
/// The order in which the extensions should be displayed.
14+
/// </summary>
15+
int DisplayOrder { get; set; }
16+
17+
/// <summary>
18+
/// If set to true and no restricting types are set, either with <c>UIHintComponent</c>, <c>ActivityTypes</c> or <c>Syntaxes</c>, this extension will be rendered for in all field types.
19+
/// </summary>
20+
bool IncludeForAll { get; set; }
21+
22+
/// <summary>
23+
/// The position to render the extension within the field.
24+
/// </summary>
25+
FieldExtensionPosition Position { get; set; }
26+
27+
/// <summary>
28+
/// The UIHint component this extension should be rendered for.
29+
/// </summary>
30+
string UIHintComponent { get; set; }
31+
32+
/// <summary>
33+
/// The activities this extension should be rendered for.
34+
/// </summary>
35+
List<string> ActivityTypes { get; set; }
36+
37+
/// <summary>
38+
/// The syntaxes this extension should be rendered for.
39+
/// </summary>
40+
List<string> Syntaxes { get; set; }
41+
42+
/// <summary>
43+
/// Returns true if the handler extension the specified or is empty.
44+
/// </summary>
45+
bool GetExtensionForInputComponent(string componentName);
46+
47+
/// <summary>
48+
/// Returns a <see cref="RenderFragment"/> of the added extension.
49+
/// </summary>
50+
RenderFragment DisplayExtension(DisplayInputEditorContext context);
5251
}

src/framework/Elsa.Studio.Core/Extensions/ServiceCollectionExtensions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Elsa.Api.Client.Extensions;
22
using Elsa.Studio.Contracts;
3-
using Elsa.Studio.Core.Services;
43
using Elsa.Studio.Localization;
54
using Elsa.Studio.Models;
65
using Elsa.Studio.Services;
@@ -41,13 +40,17 @@ public static IServiceCollection AddCoreInternal(this IServiceCollection service
4140

4241
// Content visualizers
4342
services.AddContentVisualizer<JsonContentVisualizer>();
44-
43+
4544
// Mediator.
4645
services.AddScoped<IMediator, DefaultMediator>();
4746

4847
//Localization
4948
services.AddSingleton<ILocalizationProvider, DefaultLocalizationProvider>();
5049
services.AddSingleton<ILocalizer, DefaultLocalizer>();
50+
51+
// Single-flight coordinator for preventing concurrent operations.
52+
services.TryAddScoped<ISingleFlightCoordinator, SingleFlightCoordinator>();
53+
5154
return services;
5255
}
5356

@@ -60,6 +63,7 @@ public static IServiceCollection AddRemoteBackend(this IServiceCollection servic
6063
services.AddDefaultApiClients(config?.ConfigureHttpClientBuilder);
6164
services.TryAddScoped<IRemoteBackendAccessor, DefaultRemoteBackendAccessor>();
6265
services.TryAddScoped<IBackendApiClientProvider, DefaultBackendApiClientProvider>();
66+
services.TryAddScoped<IAnonymousBackendApiClientProvider, DefaultAnonymousBackendApiClientProvider>();
6367
return services;
6468
}
6569

0 commit comments

Comments
 (0)