Identity Server 4 Redirect flow Correlation failed - azure

I use IdentityServer4 to log in user. Client and identity server run on .net core 2.2.
I have these enviroments:
dev - using debbuging in visual studio
localhost - using IIS on my computer
staging - Azure
Production - Azure
On each env, is identity server as separate instance.
When I run client (dev),
with Identity (dev),
it WORKS.
When I run client (localhost/IIS),
with Identity (dev),
it do not WORKS.
When I run client (localhost/IIS),
with Identity (localhost/IIS),
it WORKS.
When I run client (dev),
with Identity (localhost/IIS),
it do not WORKS.
On azure it do now works on staging and also on prod.
It looks like Identity server and client must run under same user.
Here is error from logs:
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.OpenIdConnect.oaZfttaJrS8SNFK1sUNQ6PBDZ_32jcnjc-kXY8Fk5Dk' cookie not found.
info: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[4]
Error from RemoteAuthentication: Correlation failed..
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Exception: An error was encountered while handling the remote login. ---> System.Exception: Correlation failed.
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Here is my start Up class for client:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var logger = LogManager.GetLogger(Assembly.GetEntryAssembly(),
Assembly.GetExecutingAssembly().GetName().Name);
services.AddSingleton(logger);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
logger.Info($"authority set to {Configuration["AuthorityUrl"]}");
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options => {
options.Authority = Configuration["AuthorityUrl"];
options.ClientId = Configuration["ClientId"];
options.ClientSecret = Configuration["ClientSecret"];
options.SaveTokens = true;
options.TokenValidationParameters.NameClaimType = "name";
options.RequireHttpsMetadata = false;
});
IdentityModelEventSource.ShowPII = true;
services.AddMvc();
services.AddLocalization(options => options.ResourcesPath = "Translations");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-US"),
new CultureInfo("sk")
};
options.DefaultRequestCulture = new RequestCulture("sk");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var log4NetFile = Configuration["log4netConfigFile"];
loggerFactory.AddLog4Net(log4NetFile);
if (!env.IsProduction())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// 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();
}
var supportedCultures = new[]
{
//new CultureInfo("en-US"),
new CultureInfo("sk"),
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("sk"),
// Formatting numbers, dates, etc.
SupportedCultures = supportedCultures,
// UI strings that we have localized.
SupportedUICultures = supportedCultures
});
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseRequestLocalization();
}
}
Edit:
I forgot to mention that I run Identity Server on Linux environment on Azure.
I think problem is with certificate. Do you know how can I verify it? I'm loading certificate from file.
Edit 2
This code solved my problem. I'm not sure about security, so I will not mark it as answer. Just as hotfix for now.
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.None;
});

Related

Can I use a basic Web App in Azure to just define a SignalR hub with AZ SignalR Service and use other apps as clients to communicate between them

I have been struggling with this for few days so I am asking whether what I am trying to do is possible.
Basically I am experimenting things with signalR in Azure. I am using the latest version of SignalR NuGet.
I have a small Web App defining a simple signalR hub I want to access via a Azure SignalR Service.
I then have a couple of apps which communicate via hub in the signalR service.
I have various issues.
Getting the hub to load on the AZ SignalR Service. It does not work all the time. Complaining about Serverless/Default settings...
2023-02-16T14:05:25.034917571Z info: Microsoft.Azure.SignalR.Connections.Client.Internal.WebSocketsTransport[1]
2023-02-16T14:05:25.034960072Z Starting transport. Transfer mode: Binary. Url: 'wss://nmg-opus-common-inventory-dev-sigr.service.signalr.net/server/?hub=myhub&cid=9e64b380-b752-4175-8fd2-215a2b62139d'.
2023-02-16T14:05:25.057819457Z info: Microsoft.Azure.SignalR.Connections.Client.Internal.WebSocketsTransport[11]
2023-02-16T14:05:25.057859557Z WebSocket closed by the server. Close status NormalClosure.
2023-02-16T14:05:25.057865857Z info: Microsoft.Azure.SignalR.ServiceConnection[24]
2023-02-16T14:05:25.057870457Z Connection to service '(Primary)https://nmg-opus-common-inventory-dev-sigr.service.signalr.net(hub=MyHub)' handshake failed, probably caused by network instability or service restart. Will retry after the back off period. Error detail: Azure SignalR Service is in serverless mode, server connection is not allowed.. Id: 9e64b380-b752-4175-8fd2-215a2b62139d
2023-02-16T14:05:25.057875657Z info: Microsoft.Azure.SignalR.Connections.Client.Internal.WebSocketsTransport[6]
2023-02-16T14:05:25.057880257Z Transport is stopping.
2
On the client side, it seems that the connection succeeds if the service is set to Serverless mode.
Once connected, my call to join a group just hang and never returns. The connection happens after I click an element (div) on a blazor page.
So I am not sure why something so simple does not work.
here is my very simple hub :
public class MyHub : Hub
{
public async Task SendMessage(string groupName, string message)
{
await Group(groupName).SendAsync("ReceiveMessage", message);
}
public async Task JoinGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public async Task LeaveGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public override async Task OnConnectedAsync()
{
await Clients.Client(Context.ConnectionId).SendAsync("Connected", Context.ConnectionId);
}
}
here is the classic startup method for the app defining hub
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAzureAppConfiguration();
services.AddSignalR(e => { e.MaximumReceiveMessageSize = 10240000; });
string SignalRURLHub = Configuration.GetValue<string>("Resources:SignalR:Inventory:ConnectionString");
services.AddSignalR().AddAzureSignalR(SignalRURLHub);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAzureAppConfiguration();
app.UseRouting();
var SignalRHubName = $"/{Configuration.GetValue<string>("Resources:SignalR:HubName")}";
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<MyHub>(SignalRHubName);
});
}
}
Code from the client applications Initializing the connection and starting it.
protected HubConnection _connection;
private readonly string groupName;
protected ClientHandler(string connectionString, string hubName, string groupName, string userId = null)
{
var serviceUtils = new ServiceUtils(connectionString);
var url = GetClientUrl(serviceUtils.Endpoint, hubName);
_connection = new HubConnectionBuilder()
.WithUrl(url
, option =>
{
option.AccessTokenProvider = () =>
{
return Task.FromResult(serviceUtils.GenerateAccessToken(url, userId));
};
})
.WithAutomaticReconnect(new AlwaysRetryPolicy())
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
logging.AddConsole();
})
.Build();
this.groupName = groupName;
_connection.Reconnected += JoinGroup;
_connection.On("Connected", new[] { typeof(string) }, OnInitialConnection);
}
private string GetClientUrl(string endpoint, string hubName)
{
return $"{endpoint}/client/?hub={hubName}";
}
private async Task JoinGroup(string contextId)
{
try
{
await _connection.InvokeAsync("JoinGroup", groupName); //JoinGroup is C# method name
}
catch (Exception e)
{
}
}
// Called separately after the ClientHandler object is created.
public async Task StartAsyncWithRetry(CancellationToken cancellationToken = default)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
await _connection.StartAsync()
.ContinueWith(res =>
{
_connection.InvokeAsync("JoinGroup", groupName);
});
if (_connection.State == HubConnectionState.Disconnected)
{
await Task.Delay(GetRandomDelayMilliseconds());
continue;
}
return;
}
catch (Exception e)
{
//throw;
//cancellationToken.
await Task.Delay(GetRandomDelayMilliseconds());
}
}
}
I have tried tweaking various setting in Azure, in my apps, changed my code from synch to async...
I must be missing something ... I've found lot of post but many of them were out of date. Also tried my luck with ChatGPT ...

AspNetCoreRateLimit on Azure

I am struggling with getting the package AspNetCoreRateLimit version 4.0.2 to work when hosting a Blazor WebAsssembly project in Azure.
The solution has been developed in Visual studio 2022 and is made up of 5 individual projects, where one of them is a standard API project. It is based on the net6.0 framework. I have a startup.cs configuration and a RateLimitingMiddleware class used to setup the rate limits and the configuration.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRateLimiting();
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 10,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
});
}, ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddAutoMapper(typeof(Startup));
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRateLimiting();
if (env.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseSwagger();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseHsts();
...
}
RateLimitingMiddleware.cs
internal static class RateLimitingMiddleware
{
internal static IServiceCollection AddRateLimiting(this IServiceCollection services)
{
// Used to store rate limit counters and ip rules
services.AddOptions();
services.AddMemoryCache();
services.Configure<ClientRateLimitOptions>(options =>
{
options.EnableEndpointRateLimiting = true;
//options.RealIpHeader = "X-Real-IP";
options.ClientIdHeader = "authorization";
//options.EndpointWhitelist = new List<string> { "get:/_framework/*", "get:/_content/*", "*:/lib/*", "*:/css/*", "*:/js/", "*:/appsettings.json", "*:/images/" };
options.GeneralRules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "15m",
Limit=30
},
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "12h",
Limit=20000
},
new RateLimitRule
{
Endpoint="*:/api/*",
Period = "7d",
Limit=1000000
}
};
});
services.AddInMemoryRateLimiting();
// Inject Counter and Store Rules
services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
//services.AddSingleton<IRateLimitConfiguration, CustomRateLimitConfiguration>();
//
// Return the services
return services;
}
internal static IApplicationBuilder UseRateLimiting(this IApplicationBuilder app)
{
var ipPolicyStore = app.ApplicationServices.GetRequiredService<IIpPolicyStore>();
ipPolicyStore.SeedAsync().GetAwaiter().GetResult();
var clientPolicyStore = app.ApplicationServices.GetRequiredService<IClientPolicyStore>();
clientPolicyStore.SeedAsync().GetAwaiter().GetResult();
app.UseClientRateLimiting();
app.UseIpRateLimiting();
return app;
}
}
With the above configuration the api calls are being limited when testing localhost and using Postman. However when the api is uploaded to our Azure environment the rules are not being implemented correctly.
As stated in the rule defined in the middelware I would like to use the authorization token to limit the number of count the number of requests but cannot get it to work.
I hope that someone has dealt with the same issue and can see where I am going wrong?
Thanks,
Problem solved. Wrong ratelimitheader. Should be CLIENT-IP.

Azure SignalR Services with Bearer Authentication

We are trying to scale out our calendar application, that uses SignalR to push updates to clients based on their user's OrganizationId. Previously, the SignalR stuff was hosted within the single App Server, but to make it work across multiple servers we have opted to use Azure SignalR Services.
However, when the application uses the Azure solution, autorisation breaks.
Authentication is set up in Startup.cs to look for the token in the url/query-string when dealing with Hub endpoints:
//From: Startup.cs (abridged)
public IServiceProvider ConfigureServices(IServiceCollection services)
var authenticationBuilder = services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = OAuthValidationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OAuthValidationDefaults.AuthenticationScheme;
});
authenticationBuilder
.AddOAuthValidation(options => {
options.Events.OnRetrieveToken = async context => {
// Based on https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.0
var accessToken = context.HttpContext.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/signalr/calendar")) {
context.Token = accessToken;
}
return;
};
})
.AddOpenIdConnectServer(options => {
options.TokenEndpointPath = "/token";
options.ProviderType = typeof(ApplicationOAuthProvider);
/*...*/
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime) {
app.UseAuthentication();
}
When using the Azure SignalR Service, the OnRetrieveToken event code is simply never hit, which makes sense given that the request is no longer directed at the App Service, but instead to the url of the Azure SignalR Service.
This Hub works while SignalR is hosted on the App Server:
[Authorize(Roles = "Manager, Seller")]
public class CalendarHub : Hub<ICalendarClient> {
private IHttpContextAccessor httpContextAccessor;
public CalendarHub(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor }
public override async Task OnConnectedAsync() {
await Groups.AddToGroupAsync(Context.ConnectionId, GetClaimValue("OrganizationId"));
await base.OnConnectedAsync();
}
private string GetClaimValue(string claimType) {
var identity = (ClaimsIdentity)httpContextAccessor?.HttpContext?.User.Identity;
var claim = identity?.FindFirst(c => c.Type == claimType);
if (claim == null)
throw new InvalidOperationException($"No claim of type {claimType} found.");
return claim.Value;
}
}
But when I switch to the Azure solution:
//From: Startup.cs (abridged)
public IServiceProvider ConfigureServices(IServiceCollection services)
services.AddSignalR().AddAzureSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime applicationLifetime) {
app.UseAzureSignalR(routes => routes.MapHub<CalendarHub>("/signalr/calendar"));
}
...connecting to the hub causes exception No claim of type OrganizationId found. because the identity is completely empty, as if no user was authenticated. This is especially strange, given that I've restricted access to users of specific roles.
Turns out the error is the same as this question where HttpContext is used to get the claim values, because that's what we do everywhere else. And this seems to work as long as it is the App Service itself handling the connection to the client.
But Azure SignalR Service supplies the claims somewhere else:
The correct way is using just Context which has the type HubCallerContext when accessed from a SignalR Hub. All the claims are available from here with no extra work.
So the method for getting the claim becomes
private string GetClaimValue(string claimType) {
var identity = Context.User.Identity;
var claim = identity.FindFirst(c => c.Type == claimType);
if (claim == null)
throw new InvalidOperationException($"No claim of type {claimType} found.");
return claim.Value;
}

.Net Core Web API Basic Authentication Authorize does not work on Azure

I wrote a custom auth handler for a web API in .net core 3.0 following this tutorial by Jason Watmore. The Authorize attribute works great IIS express. However, when I publish the code to Azure Web App the Authorize attribute does not fire. There is no authentication challenge and data is returned without authentication.
Azure Authentication Authorization Settings
Here is the custom BasicAuthenticationHandler
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IAPIRepo _apiRepo;
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder,
ISystemClock clock,
IAPIRepo apiRepo): base(options, logger, encoder, clock)
{
_apiRepo = apiRepo;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
//throw new NotImplementedException();
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header");
User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
user = _apiRepo.Authenticate(username, password);
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}
if (user == null)
return AuthenticateResult.Fail("Invalid Username or Password");
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.User_Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
Startup.cs
services.AddScoped<IAPIRepo, APIRepo>();
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Edit: Difference between .net core 2.2 and 3.1. Changing the run time to 3.1 fixed the issue
It looks like you are using the Startup.cs of .NET Core 3.0 instead of 3.1 like the article is using.

How do I need to log to see my logs in App Service logs

I have enabled logging to Application logging to both FileSystem and Blob. I log messages using ILogger<T>.LogInformation() and Trace.WriteLine(). None of those are visible in blob. I also cannot find them on filesystem. I also can't see them when I enable log streaming.
Do I need to configure something in my ASP.NET Core application?
The [Microsoft.Extensions.Logging.AzureAppServices][1] provider package writes logs to text files in an Azure App Service app's file system and to blob storage in an Azure Storage account.
The provider package isn't included in the shared framework. To use the provider, add the provider package to the project.
To configure provider settings, use AzureFileLoggerOptions and AzureBlobLoggerOptions, as shown in the following example:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var todoRepository = host.Services.GetRequiredService<ITodoRepository>();
todoRepository.Add(new Core.Model.TodoItem() { Name = "Feed the dog" });
todoRepository.Add(new Core.Model.TodoItem() { Name = "Walk the dog" });
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Seeded the database.");
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddAzureWebAppDiagnostics())
.ConfigureServices(serviceCollection => serviceCollection
.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
}).Configure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
})
)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
You can refer below link for additional reference:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1#azure-app-service-provider
Hope it helps.
Here is a simple way(the referred article is here):
I tested it with .NET core 2.2 mvc project.
The necessary nuget package:
Microsoft.Extensions.Logging.AzureAppServices, version 2.2.0
1.In Startup.cs -> ConfigureServices method, add this line of code:
services.AddSingleton<ILoggerFactory>(new LoggerFactory());
In Startup.cs -> Configure method, change it looks like below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//add the following code here
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddAzureWebAppDiagnostics(
new AzureAppServicesDiagnosticsSettings
{
OutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss zzz} [{Level}] {RequestId}-{SourceContext}: {Message}{NewLine}{Exception}"
}
);
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
then add following code in the HomeController.cs:
private readonly ILogger _logger;
public HomeController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<HomeController>();
}
public IActionResult Index()
{
_logger.LogInformation("This is a Log information!!!");
_logger.LogError("This is a Logger error!!!");
return View();
}
2.After publish to azure, in azure portal -> the web app -> App service logs, set the blob storage. The screenshot as below:
3.Run the web, and check the logs in the specified blob storage:

Resources