11using System . ComponentModel . DataAnnotations ;
22using System . Diagnostics ;
3+ using System . Security . Cryptography ;
34using Microsoft . AspNetCore . Authentication ;
4- using Microsoft . AspNetCore . Authentication . OpenIdConnect ;
55using Microsoft . AspNetCore . DataProtection ;
6+ using Microsoft . Extensions . DependencyInjection ;
67using Microsoft . Extensions . Logging ;
78using Microsoft . Extensions . Options ;
9+ using Microsoft . IdentityModel . Tokens ;
10+ using OpenIddict . Client ;
11+ using OpenIddict . Client . AspNetCore ;
812using OrchardCore . Environment . Shell ;
913using OrchardCore . OpenId . Services ;
1014using OrchardCore . OpenId . Settings ;
@@ -13,21 +17,25 @@ namespace OrchardCore.OpenId.Configuration;
1317
1418public sealed class OpenIdClientConfiguration :
1519 IConfigureOptions < AuthenticationOptions > ,
16- IConfigureNamedOptions < OpenIdConnectOptions >
20+ IConfigureOptions < OpenIddictClientOptions > ,
21+ IConfigureNamedOptions < OpenIddictClientAspNetCoreOptions >
1722{
1823 private readonly IOpenIdClientService _clientService ;
1924 private readonly IDataProtectionProvider _dataProtectionProvider ;
25+ private readonly IServiceProvider _serviceProvider ;
2026 private readonly ShellSettings _shellSettings ;
2127 private readonly ILogger _logger ;
2228
2329 public OpenIdClientConfiguration (
2430 IOpenIdClientService clientService ,
2531 IDataProtectionProvider dataProtectionProvider ,
32+ IServiceProvider serviceProvider ,
2633 ShellSettings shellSettings ,
2734 ILogger < OpenIdClientConfiguration > logger )
2835 {
2936 _clientService = clientService ;
3037 _dataProtectionProvider = dataProtectionProvider ;
38+ _serviceProvider = serviceProvider ;
3139 _shellSettings = shellSettings ;
3240 _logger = logger ;
3341 }
@@ -40,42 +48,53 @@ public void Configure(AuthenticationOptions options)
4048 return ;
4149 }
4250
43- // Register the OpenID Connect client handler in the authentication handlers collection.
44- options . AddScheme < OpenIdConnectHandler > ( OpenIdConnectDefaults . AuthenticationScheme , settings . DisplayName ) ;
45- }
51+ options . AddScheme < OpenIddictClientAspNetCoreHandler > (
52+ OpenIddictClientAspNetCoreDefaults . AuthenticationScheme , displayName : null ) ;
4653
47- public void Configure ( string name , OpenIdConnectOptions options )
48- {
49- // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module.
50- if ( ! string . Equals ( name , OpenIdConnectDefaults . AuthenticationScheme , StringComparison . Ordinal ) )
54+ foreach ( var scheme in _serviceProvider . GetRequiredService < IOptionsMonitor < OpenIddictClientAspNetCoreOptions > > ( )
55+ . CurrentValue . ForwardedAuthenticationSchemes )
5156 {
52- return ;
57+ options . AddScheme < OpenIddictClientAspNetCoreForwarder > ( scheme . Name , scheme . DisplayName ) ;
5358 }
59+ }
5460
61+ public void Configure ( OpenIddictClientOptions options )
62+ {
5563 var settings = GetClientSettingsAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
5664 if ( settings == null )
5765 {
5866 return ;
5967 }
6068
61- options . Authority = settings . Authority . AbsoluteUri ;
62- options . ClientId = settings . ClientId ;
63- options . SignedOutRedirectUri = settings . SignedOutRedirectUri ?? options . SignedOutRedirectUri ;
64- options . SignedOutCallbackPath = settings . SignedOutCallbackPath ?? options . SignedOutCallbackPath ;
65- options . RequireHttpsMetadata = string . Equals ( settings . Authority . Scheme , Uri . UriSchemeHttps , StringComparison . OrdinalIgnoreCase ) ;
66- options . GetClaimsFromUserInfoEndpoint = true ;
67- options . ResponseMode = settings . ResponseMode ;
68- options . ResponseType = settings . ResponseType ;
69- options . SaveTokens = settings . StoreExternalTokens ;
69+ // Note: the provider name, redirect URI and post-logout redirect URI use the same default
70+ // values as the Microsoft ASP.NET Core OpenID Connect handler, for compatibility reasons.
71+ var registration = new OpenIddictClientRegistration
72+ {
73+ Issuer = settings . Authority ,
74+ ClientId = settings . ClientId ,
75+ RedirectUri = new Uri ( settings . CallbackPath ?? "signin-oidc" , UriKind . RelativeOrAbsolute ) ,
76+ PostLogoutRedirectUri = new Uri ( settings . SignedOutCallbackPath ?? "signout-callback-oidc" , UriKind . RelativeOrAbsolute ) ,
77+ ProviderName = "OpenIdConnect" ,
78+ ProviderDisplayName = settings . DisplayName ,
79+ Properties =
80+ {
81+ [ nameof ( OpenIdClientSettings ) ] = settings
82+ }
83+ } ;
84+
85+ if ( ! string . IsNullOrEmpty ( settings . ResponseMode ) )
86+ {
87+ registration . ResponseModes . Add ( settings . ResponseMode ) ;
88+ }
7089
71- options . CallbackPath = settings . CallbackPath ?? options . CallbackPath ;
90+ if ( ! string . IsNullOrEmpty ( settings . ResponseType ) )
91+ {
92+ registration . ResponseTypes . Add ( settings . ResponseType ) ;
93+ }
7294
7395 if ( settings . Scopes != null )
7496 {
75- foreach ( var scope in settings . Scopes )
76- {
77- options . Scope . Add ( scope ) ;
78- }
97+ registration . Scopes . UnionWith ( settings . Scopes ) ;
7998 }
8099
81100 if ( ! string . IsNullOrEmpty ( settings . ClientSecret ) )
@@ -84,30 +103,46 @@ public void Configure(string name, OpenIdConnectOptions options)
84103
85104 try
86105 {
87- options . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
106+ registration . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
88107 }
89108 catch
90109 {
91110 _logger . LogError ( "The client secret could not be decrypted. It may have been encrypted using a different key." ) ;
92111 }
93112 }
94113
95- if ( settings . Parameters != null && settings . Parameters . Length > 0 )
96- {
97- var parameters = settings . Parameters ;
98- options . Events . OnRedirectToIdentityProvider = ( context ) =>
99- {
100- foreach ( var parameter in parameters )
101- {
102- context . ProtocolMessage . SetParameter ( parameter . Name , parameter . Value ) ;
103- }
114+ options . Registrations . Add ( registration ) ;
104115
105- return Task . CompletedTask ;
106- } ;
107- }
116+ // Note: claims are mapped by CallbackController, so the built-in mapping feature is unnecessary.
117+ options . DisableWebServicesFederationClaimMapping = true ;
118+
119+ // TODO: use proper encryption/signing credentials, similar to what's used for the server feature.
120+ options . EncryptionCredentials . Add ( new EncryptingCredentials ( new SymmetricSecurityKey (
121+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . Aes256KW , SecurityAlgorithms . Aes256CbcHmacSha512 ) ) ;
122+
123+ options . SigningCredentials . Add ( new SigningCredentials ( new SymmetricSecurityKey (
124+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . HmacSha256 ) ) ;
125+ }
126+
127+ public void Configure ( string name , OpenIddictClientAspNetCoreOptions options )
128+ {
129+ // Note: the OpenID module handles the redirection requests in its dedicated
130+ // ASP.NET Core MVC controller, which requires enabling the pass-through mode.
131+ options . EnableRedirectionEndpointPassthrough = true ;
132+ options . EnablePostLogoutRedirectionEndpointPassthrough = true ;
133+
134+ // Note: error pass-through is enabled to allow the actions of the MVC callback controller
135+ // to handle the errors returned by the interactive endpoints without relying on the generic
136+ // status code pages middleware to rewrite the response later in the request processing.
137+ options . EnableErrorPassthrough = true ;
138+
139+ // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module.
140+ // To make configuration easier and avoid having to configure it in two different features,
141+ // the transport security requirement enforced by OpenIddict by default is always turned off.
142+ options . DisableTransportSecurityRequirement = true ;
108143 }
109144
110- public void Configure ( OpenIdConnectOptions options ) => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
145+ public void Configure ( OpenIddictClientAspNetCoreOptions options ) => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
111146
112147 private async Task < OpenIdClientSettings > GetClientSettingsAsync ( )
113148 {
0 commit comments