Created
May 22, 2024 10:41
-
-
Save dgt0011/b9cf97cd12a8ccd7aa1609fdd7484fe5 to your computer and use it in GitHub Desktop.
Modified version of the BlazorWebAppOidc.Program.cs file to support using AWS Cognito as the OIDC provider for a ASP.Net Core 8 Blazor sample project from https://learn.microsoft.com/en-us/aspnet/core/blazor/security/blazor-web-app-with-oidc?view=aspnetcore-8.0&pivots=without-bff-pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Authentication.Cookies; | |
using Microsoft.AspNetCore.Components.Authorization; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.IdentityModel.JsonWebTokens; | |
using Microsoft.IdentityModel.Protocols.OpenIdConnect; | |
using BlazorWebAppOidc; | |
using BlazorWebAppOidc.Client.Weather; | |
using BlazorWebAppOidc.Components; | |
using BlazorWebAppOidc.Weather; | |
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |
using Microsoft.Extensions.Options; | |
using Microsoft.Extensions.DependencyInjection; | |
const string AWS_OIDC_SCHEME = "CognitoOidc"; | |
//reference: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/blazor-web-app-with-oidc?view=aspnetcore-8.0&pivots=without-bff-pattern | |
var builder = WebApplication.CreateBuilder(args); | |
// Add services to the container. | |
builder.Services.AddAuthentication(AWS_OIDC_SCHEME) | |
.AddOpenIdConnect(AWS_OIDC_SCHEME, oidcOptions => | |
{ | |
// For the following OIDC settings, any line that's commented out | |
// represents a DEFAULT setting. If you adopt the default, you can | |
// remove the line if you wish. | |
// ........................................................................ | |
// The OIDC handler must use a sign-in scheme capable of persisting | |
// user credentials across requests. | |
oidcOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |
// ........................................................................ | |
// ........................................................................ | |
// The "openid" and "profile" scopes are required for the OIDC handler | |
// and included by default. You should enable these scopes here if scopes | |
// are provided by "Authentication:Schemes:MicrosoftOidc:Scope" | |
// configuration because configuration may overwrite the scopes collection. | |
//oidcOptions.Scope.Add(OpenIdConnectScope.OpenIdProfile); | |
oidcOptions.Scope.Clear(); | |
// required OIDC scopes | |
oidcOptions.Scope.Add("openid"); | |
oidcOptions.Scope.Add("profile"); | |
// default Cognito Scope | |
oidcOptions.Scope.Add("email"); | |
// TODO: replace {User pool ID} below with the actual User pool ID from your User Pool | |
oidcOptions.MetadataAddress = "https://cognito-idp.ap-southeast-2.amazonaws.com/{User pool ID}/.well-known/openid-configuration"; | |
oidcOptions.Events = new OpenIdConnectEvents | |
{ | |
OnRedirectToIdentityProviderForSignOut = context => | |
{ | |
// TODO: replace {Cognito domain} with the actual domain value from the User Pool - e.g. https://my-test-app.auth.ap-southeast-2.amazoncognito.com if you use the attached Cloudformation template | |
var uri = new Uri("{Cognito domain}/logout", UriKind.Absolute); | |
// set the logout page that will be redirected to when logging out. | |
// if this is changed to be a different page, be sure to add or change the LogoutUrls in the cloudformation template | |
// or edit the Allowed sign-out URLs in the Hosted UI section of the App client | |
var logoutUrl = $"{context.Request.Scheme}://{context.Request.Host}/"; | |
context.ProtocolMessage.IssuerAddress = uri.AbsoluteUri; | |
context.ProtocolMessage.ResponseType = "code"; | |
// TODO: Replace {ClientId} with the Client Id from the App Client. | |
context.ProtocolMessage.SetParameter("client_id", "{ClientId}"); | |
context.ProtocolMessage.SetParameter("logout_uri", logoutUrl); | |
context.ProtocolMessage.SetParameter("redirect_uri", logoutUrl); | |
return Task.CompletedTask; | |
} | |
}; | |
// ........................................................................ | |
// ........................................................................ | |
// The following paths must match the redirect and post logout redirect | |
// paths configured when registering the application with the OIDC provider. | |
// For Microsoft Entra ID, this is accomplished through the "Authentication" | |
// blade of the application's registration in the Azure portal. Both the | |
// signin and signout paths must be registered as Redirect URIs. The default | |
// values are "/signin-oidc" and "/signout-callback-oidc". | |
// Microsoft Identity currently only redirects back to the | |
// SignedOutCallbackPath if authority is | |
// https://login.microsoftonline.com/{TENANT ID}/v2.0/ as it is above. | |
// You can use the "common" authority instead, and logout redirects back to | |
// the Blazor app. For more information, see | |
// https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783 | |
oidcOptions.CallbackPath = new PathString("/signin-oidc"); | |
oidcOptions.SignedOutCallbackPath = new PathString("/signout-callback-oidc"); | |
// ........................................................................ | |
// ........................................................................ | |
// The RemoteSignOutPath is the "Front-channel logout URL" for remote single | |
// sign-out. The default value is "/signout-oidc". | |
oidcOptions.RemoteSignOutPath = new PathString("/signout-oidc"); | |
// ........................................................................ | |
// ........................................................................ | |
// The following example Authority is configured for Microsoft Entra ID | |
// and a single-tenant application registration. Set the {TENANT ID} | |
// placeholder to the Tenant ID. The "common" Authority | |
// https://login.microsoftonline.com/common/v2.0/ should be used | |
// for multi-tenant apps. You can also use the "common" Authority for | |
// single-tenant apps, but it requires a custom IssuerValidator as shown | |
// in the comments below. | |
// TODO: Replace {Cognito domain} obviously with the actual domain value from the User Pool - e.g. https://my-test-app.auth.ap-southeast-2.amazoncognito.com if you use the attached Cloudformation template | |
oidcOptions.Authority = "{Cognito domain}/oauth2/authorize"; | |
// ........................................................................ | |
// ........................................................................ | |
// Set the Client ID for the app. Set the {CLIENT ID} placeholder to | |
// the Client ID. | |
oidcOptions.ClientId = "5a7ovdoip4astq0823rv1uq7ko"; // Cognito App Client Client ID | |
// ........................................................................ | |
// ........................................................................ | |
// ClientSecret shouldn't be compiled into the application assembly or | |
// checked into source control. Adopt User Secrets, Azure KeyVault, | |
// or an environment variable to supply the value. Authentication scheme | |
// configuration is automatically read from | |
// "Authentication:Schemes:{SchemeName}:{PropertyName}", so ClientSecret is | |
// for OIDC configuration is automatically read from | |
// "Authentication:Schemes:MicrosoftOidc:ClientSecret" configuration. | |
oidcOptions.ClientSecret = "uccma5rbi25pm1nuq2lsc8icahuvct23g747jafmcrke6mv3a35"; // Cognito App Client Secret | |
// ........................................................................ | |
// ........................................................................ | |
// Setting ResponseType to "code" configures the OIDC handler to use | |
// authorization code flow. Implicit grants and hybrid flows are unnecessary | |
// in this mode. In a Microsoft Entra ID app registration, you don't need to | |
// select either box for the authorization endpoint to return access tokens | |
// or ID tokens. The OIDC handler automatically requests the appropriate | |
// tokens using the code returned from the authorization endpoint. | |
oidcOptions.ResponseType = OpenIdConnectResponseType.Code; | |
// ........................................................................ | |
// ........................................................................ | |
// Many OIDC servers use "name" and "role" rather than the SOAP/WS-Fed | |
// defaults in ClaimTypes. If you don't use ClaimTypes, mapping inbound | |
// claims to ASP.NET Core's ClaimTypes isn't necessary. | |
oidcOptions.MapInboundClaims = false; | |
oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name; | |
oidcOptions.TokenValidationParameters.RoleClaimType = "role"; | |
// ........................................................................ | |
// ........................................................................ | |
// Many OIDC providers work with the default issuer validator, but the | |
// configuration must account for the issuer parameterized with "{TENANT ID}" | |
// returned by the "common" endpoint's /.well-known/openid-configuration | |
// For more information, see | |
// https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1731 | |
//var microsoftIssuerValidator = AadIssuerValidator.GetAadIssuerValidator(oidcOptions.Authority); | |
//oidcOptions.TokenValidationParameters.IssuerValidator = microsoftIssuerValidator.Validate; | |
// ........................................................................ | |
// ........................................................................ | |
// OIDC connect options set later via ConfigureCookieOidcRefresh | |
// | |
// (1) The "offline_access" scope is required for the refresh token. | |
// | |
// (2) SaveTokens is set to true, which saves the access and refresh tokens | |
// in the cookie, so the app can authenticate requests for weather data and | |
// use the refresh token to obtain a new access token on access token | |
// expiration. | |
// ........................................................................ | |
}) | |
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme); | |
// ConfigureCookieOidcRefresh attaches a cookie OnValidatePrincipal callback to get | |
// a new access token when the current one expires, and reissue a cookie with the | |
// new access token saved inside. If the refresh fails, the user will be signed | |
// out. OIDC connect options are set for saving tokens and the offline access | |
// scope. | |
//builder.Services.ConfigureCookieOidcRefresh(CookieAuthenticationDefaults.AuthenticationScheme, AWS_OIDC_SCHEME); | |
builder.Services.AddAuthorization(); | |
builder.Services.AddCascadingAuthenticationState(); | |
builder.Services.AddRazorComponents() | |
.AddInteractiveServerComponents() | |
.AddInteractiveWebAssemblyComponents(); | |
builder.Services.AddScoped<AuthenticationStateProvider, PersistingAuthenticationStateProvider>(); | |
builder.Services.AddScoped<IWeatherForecaster, ServerWeatherForecaster>(); | |
builder.Services.AddHttpContextAccessor(); | |
var app = builder.Build(); | |
// Configure the HTTP request pipeline. | |
if (app.Environment.IsDevelopment()) | |
{ | |
app.UseWebAssemblyDebugging(); | |
} | |
else | |
{ | |
app.UseExceptionHandler("/Error", createScopeForErrors: true); | |
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. | |
app.UseHsts(); | |
} | |
app.UseHttpsRedirection(); | |
app.UseStaticFiles(); | |
app.UseAntiforgery(); | |
var summaries = new[] | |
{ | |
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" | |
}; | |
app.MapGet("/weather-forecast", ([FromServices] IWeatherForecaster WeatherForecaster) => | |
{ | |
return WeatherForecaster.GetWeatherForecastAsync(); | |
}).RequireAuthorization(); | |
app.MapRazorComponents<App>() | |
.AddInteractiveServerRenderMode() | |
.AddInteractiveWebAssemblyRenderMode() | |
.AddAdditionalAssemblies(typeof(BlazorWebAppOidc.Client._Imports).Assembly); | |
app.MapGroup("/authentication").MapLoginAndLogout(); | |
app.Run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment