为AWS Cognito OIDC的.NET Framework应用添加PKCE支持
Great question! The good news is that you don't need to manually implement PKCE by handling code verifiers/challenges yourself—the official Microsoft.Owin.Security.OpenIdConnect middleware (version 4.2.2 and later) includes built-in PKCE support, just like your .NET Core app. Here's how to enable it with minimal code changes for both your .NET Framework MVC and Web Forms apps:
Step 1: Upgrade the OIDC Middleware NuGet Package
First, make sure you're using a version of Microsoft.Owin.Security.OpenIdConnect that supports PKCE. Upgrade the package in both your MVC and Web Forms projects to 4.2.2 or newer (this was the first version to add the PKCE toggle).
Step 2: Ensure You're Using the Authorization Code Flow
PKCE only works with the Authorization Code flow, so verify your Cognito.ResponseType app setting is set to code (not id_token or id_token token, which are implicit flow types). AWS Cognito requires this for PKCE to function.
Step 3: Enable PKCE in the OIDC Configuration
Modify your OpenIdConnectAuthenticationOptions in both apps to add the UsePkce = true property. This tells the middleware to automatically handle all PKCE logic: generating the code verifier, creating the SHA256 challenge, appending the required parameters to the authorization request, and sending the verifier with the token request.
For Your .NET Framework Web Forms App:
Update the ConfigureIdentityProviders method's OIDC options:
app.UseCustomOidcAuthentication( new OpenIdConnectAuthenticationOptions { ClientId = ConfigurationManager.AppSettings["Cognito.ClientId"], ResponseType = ConfigurationManager.AppSettings["Cognito.ResponseType"], Authority = ConfigurationManager.AppSettings["Cognito.Authority"], MetadataAddress = ConfigurationManager.AppSettings["Cognito.MetadataAddress"], ClientSecret = ConfigurationManager.AppSettings["Cognito.ClientSecret"], RedirectUri = ConfigurationManager.AppSettings["Cognito.RedirectUri"], SaveTokens = saveTokens, UsePkce = true, // <-- Add this line to enable built-in PKCE TokenValidationParameters = new TokenValidationParameters { NameClaimType = ConfigurationManager.AppSettings["Cognito.TokenValidationParameters.NameClaimType"], RoleClaimType = ConfigurationManager.AppSettings["Cognito.TokenValidationParameters.RoleClaimType"], ValidateIssuer = validateIssuer }, Notifications = new OpenIdConnectAuthenticationNotifications { // Your existing notification logic remains unchanged RedirectToIdentityProvider = async (context) => { var removePortOnRedirectIdentifierValue = ConfigurationManager.AppSettings["Cognito.RemovePortOnRedirectToIdentityProvider"]; var convertResult = bool.TryParse(removePortOnRedirectIdentifierValue, out var removePortOnRedirectIdentifier); if (removePortOnRedirectIdentifier && convertResult) { var builder = new UriBuilder(context.ProtocolMessage.RedirectUri) { Scheme = "https", Port = -1 }; context.ProtocolMessage.RedirectUri = builder.ToString(); } } }, Scope = ConfigurationManager.AppSettings["Cognito.Scope"], SignInAsAuthenticationType = signInAsType } );
For Your .NET Framework MVC App:
Do the same in its ConfigureIdentityProviders method:
app.UseCustomOidcAuthentication( new OpenIdConnectAuthenticationOptions { ClientId = ConfigurationManager.AppSettings["Cognito.ClientId"], ResponseType = ConfigurationManager.AppSettings["Cognito.ResponseType"], Authority = ConfigurationManager.AppSettings["Cognito.Authority"], MetadataAddress = ConfigurationManager.AppSettings["Cognito.MetadataAddress"], ClientSecret = ConfigurationManager.AppSettings["Cognito.ClientSecret"], RedirectUri = ConfigurationManager.AppSettings["Cognito.RedirectUri"], SaveTokens = saveTokens, UsePkce = true, // <-- Add this line to enable built-in PKCE Notifications = new OpenIdConnectAuthenticationNotifications { // Your existing notification logic remains unchanged SecurityTokenValidated = context => { var redirectUri = "/account/openidlogincallback"; if (!string.IsNullOrEmpty(context.AuthenticationTicket.Properties.RedirectUri)) { redirectUri += $"?returnUrl={context.AuthenticationTicket.Properties.RedirectUri}"; } context.AuthenticationTicket.Properties.RedirectUri = redirectUri; return Task.FromResult(0); }, RedirectToIdentityProvider = context => { var removePortOnRedirectIdentifierValue = ConfigurationManager.AppSettings["Cognito.RemovePortOnRedirectToIdentityProvider"]; var convertResult = bool.TryParse(removePortOnRedirectIdentifierValue, out var removePortOnRedirectIdentifier); if (removePortOnRedirectIdentifier && convertResult) { var builder = new UriBuilder(context.ProtocolMessage.RedirectUri) { Scheme = "https", Port = -1 }; context.ProtocolMessage.RedirectUri = builder.ToString(); } return Task.FromResult(0); } }, TokenValidationParameters = new TokenValidationParameters { NameClaimType = ConfigurationManager.AppSettings["Cognito.TokenValidationParameters.NameClaimType"], RoleClaimType = ConfigurationManager.AppSettings["Cognito.TokenValidationParameters.RoleClaimType"], ValidateIssuer = validateIssuer }, Scope = ConfigurationManager.AppSettings["Cognito.Scope"], SignInAsAuthenticationType = signInAsType } );
Step 4: Verify Cognito Client Configuration
Double-check your AWS Cognito app client settings:
- Ensure Authorization code grant is enabled under "Allowed OAuth Flows"
- PKCE is supported by default in Cognito for authorization code flows, so no extra configuration is needed there
Important Notes
- If your
UseCustomOidcAuthenticationis a wrapper around the official middleware, make sure it passes through theUsePkceproperty to the underlyingOpenIdConnectAuthenticationOptions. If it's a fully custom implementation, you may need to adjust it to respect this setting, but most custom wrappers just forward properties. - You don't need to modify your
RedirectToIdentityProvidernotification—all PKCE parameter handling is done automatically by the middleware whenUsePkceis enabled.
That's it! This gives you the same "set-it-and-forget-it" PKCE support as your .NET Core app, no manual parameter splicing required.
内容的提问来源于stack exchange,提问作者Dan7el

