// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext <WebClientContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddDistributedSqlServerCache(options => { options.ConnectionString = Configuration.GetConnectionString("DefaultConnection"); options.SchemaName = "dbo"; options.TableName = "TokenCache"; }); services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.Unspecified; // Handling SameSite cookie according to https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1 options.HandleSameSiteCookieCompatibility(); }); services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddDistributedTokenCaches(); services.Configure <MicrosoftIdentityOptions>(options => { var existingOnTokenValidatedHandler = options.Events.OnTokenValidated; options.Events.OnTokenValidated = async context => { await existingOnTokenValidatedHandler(context); //Allows us to add extra claims specific to our application await PrincipalTransformer.Transform(context); }; }); services.AddHttpClient <IApi01Service, Api01Service>(); services.AddHttpClient <IApi02Service, Api02Service>(); //Allows us to use MicrosoftIdentityConsentAndConditionalAccessHandler in the Api services. //This in turn means we don't need AuthorizeForScope on each page and can trigger //a challenge in the service if the token can't be found or has expired. services.TryAddScoped <MicrosoftIdentityConsentAndConditionalAccessHandler>(); services.AddControllersWithViews(options => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); }).AddMicrosoftIdentityUI(); services.AddRazorPages(); }
protected override Task <HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var tokenStringFromHeader = GetTokenStringFromHeader(request); var tokenStringFromCookie = GetTokenStringFromCookie(CookieNameToCheckForToken); var tokenString = tokenStringFromHeader ?? tokenStringFromCookie; if (!string.IsNullOrEmpty(tokenStringFromHeader) && !string.IsNullOrEmpty(tokenStringFromCookie)) { _logger.DebugFormat( "Both the Authorization header and {0} cookie contained tokens; header token was used", CookieNameToCheckForToken); } if (string.IsNullOrEmpty(tokenString)) { _logger.Debug("Token not found in authorization header or request cookie"); return(BaseSendAsync(request, cancellationToken)); } IJwtSecurityToken token; try { token = CreateToken(tokenString); } catch (Exception ex) { _logger.WarnFormat("Error converting token string to JWT: {0}", ex); return(BaseSendAsync(request, cancellationToken)); } if (SigningToken != null && token.SignatureAlgorithm != null) { if (token.SignatureAlgorithm.StartsWith("RS") && !(SigningToken is X509SecurityToken)) { _logger.DebugFormat("Incoming token signature is X509, but token handler's signing token is not."); return(BaseSendAsync(request, cancellationToken)); } if (token.SignatureAlgorithm.StartsWith("HS") && !(SigningToken is BinarySecretSecurityToken)) { _logger.DebugFormat("Incoming token signature is SHA, but token handler's signing token is not."); return(BaseSendAsync(request, cancellationToken)); } } var parameters = new TokenValidationParameters { ValidAudience = AllowedAudience, IssuerSigningToken = SigningToken, ValidIssuer = Issuer, ValidAudiences = AllowedAudiences }; try { var tokenHandler = CreateTokenHandler(); IPrincipal principal = tokenHandler.ValidateToken(token, parameters); if (PrincipalTransformer != null) { principal = PrincipalTransformer.Transform((ClaimsPrincipal)principal); CheckPrincipal(principal, PrincipalTransformer.GetType()); } Thread.CurrentPrincipal = principal; _logger.DebugFormat("Thread principal set with identity '{0}'", principal.Identity.Name); if (HttpContext.Current != null) { HttpContext.Current.User = principal; } } catch (SecurityTokenExpiredException e) { _logger.ErrorFormat("Security token expired: {0}", e); var response = new HttpResponseMessage((HttpStatusCode)440) { Content = new StringContent("Security token expired exception") }; var tsc = new TaskCompletionSource <HttpResponseMessage>(); tsc.SetResult(response); return(tsc.Task); } catch (SecurityTokenSignatureKeyNotFoundException e) { _logger.ErrorFormat("Error during JWT validation: {0}", e); var response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("Untrusted signing cert") }; var tsc = new TaskCompletionSource <HttpResponseMessage>(); tsc.SetResult(response); return(tsc.Task); } catch (SecurityTokenInvalidAudienceException e) { _logger.ErrorFormat("Error during JWT validation: {0}", e); var response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("Invalid token audience") }; var tsc = new TaskCompletionSource <HttpResponseMessage>(); tsc.SetResult(response); return(tsc.Task); } catch (SecurityTokenValidationException e) { _logger.ErrorFormat("Error during JWT validation: {0}", e); var response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("Invalid token") }; var tsc = new TaskCompletionSource <HttpResponseMessage>(); tsc.SetResult(response); return(tsc.Task); } catch (SignatureVerificationFailedException e) { _logger.ErrorFormat("Error during JWT validation: {0}", e); var response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent("Invalid token signature") }; var tsc = new TaskCompletionSource <HttpResponseMessage>(); tsc.SetResult(response); return(tsc.Task); } catch (Exception e) { _logger.ErrorFormat("Error during JWT validation: {0}", e); throw; } return(BaseSendAsync(request, cancellationToken)); }