22using System . ComponentModel . DataAnnotations ;
33using System . Diagnostics ;
44using System . Linq ;
5+ using System . Security . Cryptography ;
56using System . Threading . Tasks ;
67using Microsoft . AspNetCore . Authentication ;
7- using Microsoft . AspNetCore . Authentication . OpenIdConnect ;
88using Microsoft . AspNetCore . DataProtection ;
9+ using Microsoft . Extensions . DependencyInjection ;
910using Microsoft . Extensions . Logging ;
1011using Microsoft . Extensions . Options ;
12+ using Microsoft . IdentityModel . Tokens ;
13+ using OpenIddict . Client ;
14+ using OpenIddict . Client . AspNetCore ;
1115using OrchardCore . Environment . Shell ;
1216using OrchardCore . Modules ;
1317using OrchardCore . OpenId . Services ;
1620namespace OrchardCore . OpenId . Configuration
1721{
1822 [ Feature ( OpenIdConstants . Features . Client ) ]
19- public class OpenIdClientConfiguration :
20- IConfigureOptions < AuthenticationOptions > ,
21- IConfigureNamedOptions < OpenIdConnectOptions >
23+ public class OpenIdClientConfiguration : IConfigureOptions < AuthenticationOptions > ,
24+ IConfigureOptions < OpenIddictClientOptions > ,
25+ IConfigureNamedOptions < OpenIddictClientAspNetCoreOptions >
2226 {
2327 private readonly IOpenIdClientService _clientService ;
2428 private readonly IDataProtectionProvider _dataProtectionProvider ;
29+ private readonly IServiceProvider _serviceProvider ;
2530 private readonly ShellSettings _shellSettings ;
2631 private readonly ILogger _logger ;
2732
2833 public OpenIdClientConfiguration (
2934 IOpenIdClientService clientService ,
3035 IDataProtectionProvider dataProtectionProvider ,
36+ IServiceProvider serviceProvider ,
3137 ShellSettings shellSettings ,
3238 ILogger < OpenIdClientConfiguration > logger )
3339 {
3440 _clientService = clientService ;
3541 _dataProtectionProvider = dataProtectionProvider ;
42+ _serviceProvider = serviceProvider ;
3643 _shellSettings = shellSettings ;
3744 _logger = logger ;
3845 }
@@ -45,42 +52,53 @@ public void Configure(AuthenticationOptions options)
4552 return ;
4653 }
4754
48- // Register the OpenID Connect client handler in the authentication handlers collection.
49- options . AddScheme < OpenIdConnectHandler > ( OpenIdConnectDefaults . AuthenticationScheme , settings . DisplayName ) ;
50- }
55+ options . AddScheme < OpenIddictClientAspNetCoreHandler > (
56+ OpenIddictClientAspNetCoreDefaults . AuthenticationScheme , displayName : null ) ;
5157
52- public void Configure ( string name , OpenIdConnectOptions options )
53- {
54- // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module.
55- if ( ! string . Equals ( name , OpenIdConnectDefaults . AuthenticationScheme , StringComparison . Ordinal ) )
58+ foreach ( var scheme in _serviceProvider . GetRequiredService < IOptionsMonitor < OpenIddictClientAspNetCoreOptions > > ( )
59+ . CurrentValue . ForwardedAuthenticationSchemes )
5660 {
57- return ;
61+ options . AddScheme < OpenIddictClientAspNetCoreForwarder > ( scheme . Name , scheme . DisplayName ) ;
5862 }
63+ }
5964
65+ public void Configure ( OpenIddictClientOptions options )
66+ {
6067 var settings = GetClientSettingsAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
6168 if ( settings == null )
6269 {
6370 return ;
6471 }
6572
66- options . Authority = settings . Authority . AbsoluteUri ;
67- options . ClientId = settings . ClientId ;
68- options . SignedOutRedirectUri = settings . SignedOutRedirectUri ?? options . SignedOutRedirectUri ;
69- options . SignedOutCallbackPath = settings . SignedOutCallbackPath ?? options . SignedOutCallbackPath ;
70- options . RequireHttpsMetadata = string . Equals ( settings . Authority . Scheme , Uri . UriSchemeHttps , StringComparison . OrdinalIgnoreCase ) ;
71- options . GetClaimsFromUserInfoEndpoint = true ;
72- options . ResponseMode = settings . ResponseMode ;
73- options . ResponseType = settings . ResponseType ;
74- options . SaveTokens = settings . StoreExternalTokens ;
73+ // Note: the provider name, redirect URI and post-logout redirect URI use the same default
74+ // values as the Microsoft ASP.NET Core OpenID Connect handler, for compatibility reasons.
75+ var registration = new OpenIddictClientRegistration
76+ {
77+ Issuer = settings . Authority ,
78+ ClientId = settings . ClientId ,
79+ RedirectUri = new Uri ( settings . CallbackPath ?? "signin-oidc" , UriKind . RelativeOrAbsolute ) ,
80+ PostLogoutRedirectUri = new Uri ( settings . SignedOutCallbackPath ?? "signout-callback-oidc" , UriKind . RelativeOrAbsolute ) ,
81+ ProviderName = "OpenIdConnect" ,
82+ ProviderDisplayName = settings . DisplayName ,
83+ Properties =
84+ {
85+ [ nameof ( OpenIdClientSettings ) ] = settings
86+ }
87+ } ;
88+
89+ if ( ! String . IsNullOrEmpty ( settings . ResponseMode ) )
90+ {
91+ registration . ResponseModes . Add ( settings . ResponseMode ) ;
92+ }
7593
76- options . CallbackPath = settings . CallbackPath ?? options . CallbackPath ;
94+ if ( ! String . IsNullOrEmpty ( settings . ResponseType ) )
95+ {
96+ registration . ResponseTypes . Add ( settings . ResponseType ) ;
97+ }
7798
7899 if ( settings . Scopes != null )
79100 {
80- foreach ( var scope in settings . Scopes )
81- {
82- options . Scope . Add ( scope ) ;
83- }
101+ registration . Scopes . UnionWith ( settings . Scopes ) ;
84102 }
85103
86104 if ( ! string . IsNullOrEmpty ( settings . ClientSecret ) )
@@ -89,30 +107,47 @@ public void Configure(string name, OpenIdConnectOptions options)
89107
90108 try
91109 {
92- options . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
110+ registration . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
93111 }
94112 catch
95113 {
96114 _logger . LogError ( "The client secret could not be decrypted. It may have been encrypted using a different key." ) ;
97115 }
98116 }
99117
100- if ( settings . Parameters != null && settings . Parameters . Length > 0 )
101- {
102- var parameters = settings . Parameters ;
103- options . Events . OnRedirectToIdentityProvider = ( context ) =>
104- {
105- foreach ( var parameter in parameters )
106- {
107- context . ProtocolMessage . SetParameter ( parameter . Name , parameter . Value ) ;
108- }
118+ options . Registrations . Add ( registration ) ;
109119
110- return Task . CompletedTask ;
111- } ;
112- }
120+ // Note: claims are mapped by CallbackController, so the built-in mapping feature is unnecessary.
121+ options . DisableWebServicesFederationClaimMapping = true ;
122+
123+ // TODO: use proper encryption/signing credentials, similar to what's used for the server feature.
124+ options . EncryptionCredentials . Add ( new EncryptingCredentials ( new SymmetricSecurityKey (
125+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . Aes256KW , SecurityAlgorithms . Aes256CbcHmacSha512 ) ) ;
126+
127+ options . SigningCredentials . Add ( new SigningCredentials ( new SymmetricSecurityKey (
128+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . HmacSha256 ) ) ;
129+ }
130+
131+ public void Configure ( string name , OpenIddictClientAspNetCoreOptions options )
132+ {
133+ // Note: the OpenID module handles the redirection requests in its dedicated
134+ // ASP.NET Core MVC controller, which requires enabling the pass-through mode.
135+ options . EnableRedirectionEndpointPassthrough = true ;
136+ options . EnablePostLogoutRedirectionEndpointPassthrough = true ;
137+
138+ // Note: error pass-through is enabled to allow the actions of the MVC callback controller
139+ // to handle the errors returned by the interactive endpoints without relying on the generic
140+ // status code pages middleware to rewrite the response later in the request processing.
141+ options . EnableErrorPassthrough = true ;
142+
143+ // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module.
144+ // To make configuration easier and avoid having to configure it in two different features,
145+ // the transport security requirement enforced by OpenIddict by default is always turned off.
146+ options . DisableTransportSecurityRequirement = true ;
113147 }
114148
115- public void Configure ( OpenIdConnectOptions options ) => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
149+ public void Configure ( OpenIddictClientAspNetCoreOptions options )
150+ => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
116151
117152 private async Task < OpenIdClientSettings > GetClientSettingsAsync ( )
118153 {
0 commit comments