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 . OpenId . Services ;
1317using OrchardCore . OpenId . Settings ;
@@ -16,21 +20,25 @@ namespace OrchardCore.OpenId.Configuration;
1620
1721public sealed class OpenIdClientConfiguration :
1822 IConfigureOptions < AuthenticationOptions > ,
19- IConfigureNamedOptions < OpenIdConnectOptions >
23+ IConfigureOptions < OpenIddictClientOptions > ,
24+ IConfigureNamedOptions < OpenIddictClientAspNetCoreOptions >
2025{
2126 private readonly IOpenIdClientService _clientService ;
2227 private readonly IDataProtectionProvider _dataProtectionProvider ;
28+ private readonly IServiceProvider _serviceProvider ;
2329 private readonly ShellSettings _shellSettings ;
2430 private readonly ILogger _logger ;
2531
2632 public OpenIdClientConfiguration (
2733 IOpenIdClientService clientService ,
2834 IDataProtectionProvider dataProtectionProvider ,
35+ IServiceProvider serviceProvider ,
2936 ShellSettings shellSettings ,
3037 ILogger < OpenIdClientConfiguration > logger )
3138 {
3239 _clientService = clientService ;
3340 _dataProtectionProvider = dataProtectionProvider ;
41+ _serviceProvider = serviceProvider ;
3442 _shellSettings = shellSettings ;
3543 _logger = logger ;
3644 }
@@ -43,42 +51,53 @@ public void Configure(AuthenticationOptions options)
4351 return ;
4452 }
4553
46- // Register the OpenID Connect client handler in the authentication handlers collection.
47- options . AddScheme < OpenIdConnectHandler > ( OpenIdConnectDefaults . AuthenticationScheme , settings . DisplayName ) ;
48- }
54+ options . AddScheme < OpenIddictClientAspNetCoreHandler > (
55+ OpenIddictClientAspNetCoreDefaults . AuthenticationScheme , displayName : null ) ;
4956
50- public void Configure ( string name , OpenIdConnectOptions options )
51- {
52- // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module.
53- if ( ! string . Equals ( name , OpenIdConnectDefaults . AuthenticationScheme , StringComparison . Ordinal ) )
57+ foreach ( var scheme in _serviceProvider . GetRequiredService < IOptionsMonitor < OpenIddictClientAspNetCoreOptions > > ( )
58+ . CurrentValue . ForwardedAuthenticationSchemes )
5459 {
55- return ;
60+ options . AddScheme < OpenIddictClientAspNetCoreForwarder > ( scheme . Name , scheme . DisplayName ) ;
5661 }
62+ }
5763
64+ public void Configure ( OpenIddictClientOptions options )
65+ {
5866 var settings = GetClientSettingsAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
5967 if ( settings == null )
6068 {
6169 return ;
6270 }
6371
64- options . Authority = settings . Authority . AbsoluteUri ;
65- options . ClientId = settings . ClientId ;
66- options . SignedOutRedirectUri = settings . SignedOutRedirectUri ?? options . SignedOutRedirectUri ;
67- options . SignedOutCallbackPath = settings . SignedOutCallbackPath ?? options . SignedOutCallbackPath ;
68- options . RequireHttpsMetadata = string . Equals ( settings . Authority . Scheme , Uri . UriSchemeHttps , StringComparison . OrdinalIgnoreCase ) ;
69- options . GetClaimsFromUserInfoEndpoint = true ;
70- options . ResponseMode = settings . ResponseMode ;
71- options . ResponseType = settings . ResponseType ;
72- options . SaveTokens = settings . StoreExternalTokens ;
72+ // Note: the provider name, redirect URI and post-logout redirect URI use the same default
73+ // values as the Microsoft ASP.NET Core OpenID Connect handler, for compatibility reasons.
74+ var registration = new OpenIddictClientRegistration
75+ {
76+ Issuer = settings . Authority ,
77+ ClientId = settings . ClientId ,
78+ RedirectUri = new Uri ( settings . CallbackPath ?? "signin-oidc" , UriKind . RelativeOrAbsolute ) ,
79+ PostLogoutRedirectUri = new Uri ( settings . SignedOutCallbackPath ?? "signout-callback-oidc" , UriKind . RelativeOrAbsolute ) ,
80+ ProviderName = "OpenIdConnect" ,
81+ ProviderDisplayName = settings . DisplayName ,
82+ Properties =
83+ {
84+ [ nameof ( OpenIdClientSettings ) ] = settings
85+ }
86+ } ;
87+
88+ if ( ! string . IsNullOrEmpty ( settings . ResponseMode ) )
89+ {
90+ registration . ResponseModes . Add ( settings . ResponseMode ) ;
91+ }
7392
74- options . CallbackPath = settings . CallbackPath ?? options . CallbackPath ;
93+ if ( ! string . IsNullOrEmpty ( settings . ResponseType ) )
94+ {
95+ registration . ResponseTypes . Add ( settings . ResponseType ) ;
96+ }
7597
7698 if ( settings . Scopes != null )
7799 {
78- foreach ( var scope in settings . Scopes )
79- {
80- options . Scope . Add ( scope ) ;
81- }
100+ registration . Scopes . UnionWith ( settings . Scopes ) ;
82101 }
83102
84103 if ( ! string . IsNullOrEmpty ( settings . ClientSecret ) )
@@ -87,30 +106,46 @@ public void Configure(string name, OpenIdConnectOptions options)
87106
88107 try
89108 {
90- options . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
109+ registration . ClientSecret = protector . Unprotect ( settings . ClientSecret ) ;
91110 }
92111 catch
93112 {
94113 _logger . LogError ( "The client secret could not be decrypted. It may have been encrypted using a different key." ) ;
95114 }
96115 }
97116
98- if ( settings . Parameters != null && settings . Parameters . Length > 0 )
99- {
100- var parameters = settings . Parameters ;
101- options . Events . OnRedirectToIdentityProvider = ( context ) =>
102- {
103- foreach ( var parameter in parameters )
104- {
105- context . ProtocolMessage . SetParameter ( parameter . Name , parameter . Value ) ;
106- }
117+ options . Registrations . Add ( registration ) ;
107118
108- return Task . CompletedTask ;
109- } ;
110- }
119+ // Note: claims are mapped by CallbackController, so the built-in mapping feature is unnecessary.
120+ options . DisableWebServicesFederationClaimMapping = true ;
121+
122+ // TODO: use proper encryption/signing credentials, similar to what's used for the server feature.
123+ options . EncryptionCredentials . Add ( new EncryptingCredentials ( new SymmetricSecurityKey (
124+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . Aes256KW , SecurityAlgorithms . Aes256CbcHmacSha512 ) ) ;
125+
126+ options . SigningCredentials . Add ( new SigningCredentials ( new SymmetricSecurityKey (
127+ RandomNumberGenerator . GetBytes ( 256 / 8 ) ) , SecurityAlgorithms . HmacSha256 ) ) ;
128+ }
129+
130+ public void Configure ( string name , OpenIddictClientAspNetCoreOptions options )
131+ {
132+ // Note: the OpenID module handles the redirection requests in its dedicated
133+ // ASP.NET Core MVC controller, which requires enabling the pass-through mode.
134+ options . EnableRedirectionEndpointPassthrough = true ;
135+ options . EnablePostLogoutRedirectionEndpointPassthrough = true ;
136+
137+ // Note: error pass-through is enabled to allow the actions of the MVC callback controller
138+ // to handle the errors returned by the interactive endpoints without relying on the generic
139+ // status code pages middleware to rewrite the response later in the request processing.
140+ options . EnableErrorPassthrough = true ;
141+
142+ // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module.
143+ // To make configuration easier and avoid having to configure it in two different features,
144+ // the transport security requirement enforced by OpenIddict by default is always turned off.
145+ options . DisableTransportSecurityRequirement = true ;
111146 }
112147
113- public void Configure ( OpenIdConnectOptions options ) => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
148+ public void Configure ( OpenIddictClientAspNetCoreOptions options ) => Debug . Fail ( "This infrastructure method shouldn't be called." ) ;
114149
115150 private async Task < OpenIdClientSettings > GetClientSettingsAsync ( )
116151 {
0 commit comments