// Fix samesite issue when running eShop from docker-compose locally as by default http protocol is being used // Refer to https://github.com/dotnet-architecture/eShopOnContainers/issues/1391 using Yarp.ReverseProxy.Forwarder; internal static class Extensions { public static void AddHealthChecks(this IServiceCollection services, IConfiguration configuration) { services.AddHealthChecks() .AddUrlGroup(_ => new Uri(configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); } public static void AddApplicationSevices(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration) .AddSession() .AddDistributedMemoryCache(); if (configuration["DPConnectionString"] is string redisConnection) { services.AddDataProtection(opts => { opts.ApplicationDiscriminator = "eshop.webmvc"; }) .PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(redisConnection), "DataProtection-Keys"); } } // Adds all Http client services public static void AddHttpClientServices(this IServiceCollection services) { // Register delegating handlers services.AddTransient() .AddTransient(); // Add http client services services.AddHttpClient() .SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes .AddHttpMessageHandler(); services.AddHttpClient(); services.AddHttpClient() .AddHttpMessageHandler() .AddHttpMessageHandler(); //add custom application services services.AddTransient, IdentityParser>(); } public static void AddAuthenticationServices(this IServiceCollection services, IConfiguration configuration) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); var identityUrl = configuration.GetRequiredValue("IdentityUrl"); var callBackUrl = configuration.GetRequiredValue("CallBackUrl"); var sessionCookieLifetime = configuration.GetValue("SessionCookieLifetimeMinutes", 60); // Add Authentication services services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie(options => options.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime)) .AddOpenIdConnect(options => { options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.Authority = identityUrl; options.SignedOutRedirectUri = callBackUrl; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.RequireHttpsMetadata = false; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("orders"); options.Scope.Add("basket"); options.Scope.Add("webshoppingagg"); options.Scope.Add("orders.signalrhub"); options.Scope.Add("webhooks"); }); } public static IEndpointConventionBuilder MapForwardSignalR(this WebApplication app) { // Forward the SignalR traffic to the bff var destination = app.Configuration.GetRequiredValue("PurchaseUrl"); var authTransformer = new BffAuthTransfomer(); var requestConfig = new ForwarderRequestConfig(); return app.MapForwarder("/hub/notificationhub/{**any}", destination, requestConfig, authTransformer); } private sealed class BffAuthTransfomer : HttpTransformer { public override async ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix, CancellationToken cancellationToken) { // Set the access token as a bearer token for the outgoing request var accessToken = await httpContext.GetTokenAsync("access_token"); if (accessToken is not null) { proxyRequest.Headers.Authorization = new("Bearer", accessToken); } await base.TransformRequestAsync(httpContext, proxyRequest, destinationPrefix, cancellationToken); } } }