Microsoft.Extensions.Caching.Redis select different database than db0 - c#

a question on understanding which redis database is used and how it can be configured.
i have a default ASP.NET Core Web Application and a default configured local redis-server (containing 15 databases)
Over Package Management Console i have installed:
Install-Package Microsoft.Extensions.Caching.Redis
Redis is configured in Startup.cs like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDistributedRedisCache(option =>
{
option.Configuration = "127.0.0.1";
option.InstanceName = "master";
});
}
The code to read and write values into the cache is taken from the docs:
var cacheKey = "TheTime";
var existingTime = _distributedCache.GetString(cacheKey);
if (!string.IsNullOrEmpty(existingTime))
{
return "Fetched from cache : " + existingTime;
}
else
{
existingTime = DateTime.UtcNow.ToString();
_distributedCache.SetString(cacheKey, existingTime);
return "Added to cache : " + existingTime;
}
But this code only uses the default database db0 no matter what i configure.
E.g. using this configuration:
services.AddDistributedRedisCache(option =>
{
option.Configuration = "127.0.0.1";
option.InstanceName = "db6";
});
leads to:
What do i have to configure to use e.g. db6?
Do i have to use Stackexchange.Redis for this?

Microsoft.Extensions.Caching.Redis is using Stackexchange.Redis to connect to Redis.
The Configuration string is documented on StackExchange.Redis. That said, you should be able to do:
services.AddDistributedRedisCache(option =>
{
option.Configuration = "127.0.0.1;defaultDatabase=4";
option.InstanceName = "master";
});

Related

.NET 5.0 API is working fine with AWS Services, but not working when dockerized

I have developed an API in .NET 5.0 where I am using Amazan SQS and Elasticsearch. It works perfectly when I run it, but it gives me an exception:
No RegionEndpoint or ServiceURL configured
This issue is happening only when I dockerized the API.
I have configured AWS Creds using CLI and accessing them as follows:
public void ConfigureServices(IServiceCollection services)
{
Configuration.GetAWSOptions();
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions
.Converters.Add(new StringConverter());
options.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
services.AddTransient<IQueService, SQSService>();
services.AddTransient<IAmazonSQS, AmazonSQSClient>();
services.AddTransient<IAmazonSimpleSystemsManagement, AmazonSimpleSystemsManagementClient>();
services.AddElasticsearch(options => {
options.Endpoint = Configuration.GetValue<string>(ConfigurationConstants.ES_URL);
options.AWSOptions = Configuration.GetAWSOptions();
});
}

Update asp.net core Authentication outside of Startup.cs

With respect to asp.net core identity management, we have a requirement to change the Microsoft ClientId and ClientSecret after our asp.net core app has started and, therefore, not in startup.cs. We have various identity management logins working fine with, for example this for Microsoft Azure:
.AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.CorrelationCookie.HttpOnly = true;
microsoftOptions.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always;
microsoftOptions.ClientId = "removed";
microsoftOptions.ClientSecret = "removed";
})
We now need to change the ClientId and ClientSecret dynamically after the core application has started and what we can't figure out is how to access this from the services collection later in other pages so we can update them.
Any help appreciated.
Thanks.
ASP.NET Core provides IAuthenticationSchemeProvider interface to dynamically add/remove authentication schemes at runtime. You can inject this interface and add Microsoft Account auth schemes after the app has started.
Using Microsoft's demo app as reference, here's a basic implementation:
public class DynamicAuthController: ControllerBase
{
private IAuthenticationSchemeProvider _schemeProvider;
private IOptionsMonitorCache<MicrosoftAccountOptions> _optionsCache;
public DynamicAuthController(IAuthenticationSchemeProvider schemeProvider, IOptionsMonitorCache<MicrosoftAccountOptions> optionsCache)
{
_schemeProvider = schemeProvider;
_optionsCache = optionsCache;
}
[HttpPost]
public ActionResult Add()
{
var schemeName = "MicrosoftCustom1"; // must be unique for different schemes
var schemeOptions = new MicrosoftAccountOptions
{
ClientId = "ididid", // fetch credentials from another service or database
ClientSecret = "secretsecret",
CorrelationCookie =
{
HttpOnly = true,
SecurePolicy = CookieSecurePolicy.Always
}
};
var scheme = new AuthenticationScheme(schemeName, displayName:null, typeof(MicrosoftAccountHandler));
_schemeProvider.TryAddScheme(scheme);
_optionsCache.TryAdd(
schemeName,
schemeOptions
);
return Ok();
}
}

SignalR - Change server timeout response

i've created a SignalR application but when i set the KeepAliveInternal and ClientTimeOutInterval a value in the hub configuration, the application ignore it and always set to "30,000ms" for both. This is my code:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR().AddHubOptions<ActivityHub>(SetConfig);
// Local function to set hub configuration
void SetConfig(HubOptions<ActivityHub> options)
{
options.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
options.KeepAliveInterval = TimeSpan.FromMinutes(15);
}
}
I've read the SignalR Net Core docs and there is no limit for these two properties. The timeout always is "30,000" even i set those to differente values.
when i set the KeepAliveInternal and ClientTimeOutInterval a value in the hub configuration, the application ignore it and always set to "30,000ms" for both.
For SignalR JavaScript client, the default serverTimeoutInMilliseconds value is 30,000 milliseconds (30 seconds). If you set KeepAliveInterval of HubOptions with a value > 30 seconds, but not specify an appropriate value for serverTimeoutInMilliseconds of HubConnection on client side, the connection will be terminated with an error, like below.
To fix it, you can try to set serverTimeoutInMilliseconds of your HubConnection, like below.
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub")
.configureLogging(signalR.LogLevel.Trace)
.build();
connection.serverTimeoutInMilliseconds = 120000;
Test Result
Note:
In my above test, I configure SignalR hubs with below code snippet, and we can find a ping message is sent automatically per 60s.
hubOptions.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(1);
Please refer to the official documentation for configuring server options
You may try to configure it as following:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(15);
});
}
Or for a single hub:
services.AddSignalR().AddHubOptions<MyHub>(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
options.KeepAliveInterval = TimeSpan.FromMinutes(15);
});
I had the same problem back then, and what I changed was simple. I changed TimeSpan.FromMinutes to TimeSpan.FromSeconds since in the documentation you can see that those intervals are in seconds.
So my configuration code now is like this:
/// <summary>
/// Adds SignalR.
/// </summary>
private void AddSignalR(IServiceCollection services)
{
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.ClientTimeoutInterval);
hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.HandshakeTimeout);
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.KeepAliveInterval);
hubOptions.EnableDetailedErrors = this.azureConfiguration.SignalR.EnableDetailedErrors;
hubOptions.MaximumReceiveMessageSize = this.azureConfiguration.SignalR.MaximumReceiveMessageSize;
hubOptions.StreamBufferCapacity = this.azureConfiguration.SignalR.StreamBufferCapacity;
}).AddAzureSignalR(azureOptions =>
{
azureOptions.ConnectionCount = this.azureConfiguration.SignalR.ServerConnectionCount;
});
}

Heartbeat functionality from AppInsights does not appear in azure portal

I'm trying to get the heartbeat feature of AppInsights sdk to work but I'm having some trouble.
I have a simple app (just the default ASP.net core 2.2 project created by using dotnet new webapp) running on a k8 cluster inside Azure and is configured with the following settings:
public void ConfigureServices(IServiceCollection services)
{
ApplicationInsightsServiceOptions aiOptions
= new ApplicationInsightsServiceOptions();
// Disables adaptive sampling.
aiOptions.EnableAdaptiveSampling = false;
// Disables QuickPulse (Live Metrics stream).
aiOptions.EnableQuickPulseMetricStream = false;
aiOptions.InstrumentationKey = InstrumentationKey;
aiOptions.EnableHeartbeat=true;
services.AddApplicationInsightsTelemetry(aiOptions);
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddApplicationInsightsKubernetesEnricher();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
However, I can't see any properties in Application Insights related to the heartbeat functionality. I can see other stuff like the kubernetes pod name, etc.
Am I missing some configuration?
Thank you.
The Heartbeat feature is enabled by default as of base SDK 2.5.0 and the ability to configure the Heartbeat was added in 2.3.0-beta1.
I would suggest you to modify your startup file like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
ApplicationInsightsServiceOptions aiOpts =
new ApplicationInsightsServiceOptions();
aiOpts.EnableHeartbeat = true; // false to disable
services.AddApplicationInsightsTelemetry(aiOpts);
...
}
Also add using Microsoft.ApplicationInsights.AspNetCore.Extensions; in the top of your file.
Configure the Heartbeat feature in code by modifying the IHeartbeatPropertyManager directly. You can do this when you first obtain the property manager via the TelemetryModules.Instance singleton.
foreach (var md in TelemetryModules.Instance.Modules)
{
if (md is IHeartbeatPropertyManager heartbeatPropertyMan)
{
heartbeatPropertyMan.HeartbeatInterval = TimeSpan.FromMinutes(5.0);
heartbeatPropertyMan.ExcludedHeartbeatProperties.Add("osType");
...
Try this and see if it helps.
To query heartbeat events you can use the following KQL query:
customMetrics
| where name == "HeartbeatState"

.Net Core Reset Password Token and Identityserver 4 token invalid

My applications and APIs are protected using IdentityServer 4.
I have a centralized API for user management (registering new users, updating, deleting and resetting passwords). The token generated by this api will be used by identityserver to reset the user's password.
Problem is I always get invalid token error. I know this has nothing to do with url encoding because forgotten password is handled by identity server and the token generated by identity server works fine. The problem is when a token is generated by different api (even on a single machine).
I looked into creating a common data protection provider but I'm unclear how this is done. Basically, how can I have reset password token created by one api accepted by another?
I'm using Asp Identity's usermanager to generate the reset password token:
var token = await _userManager.GeneratePasswordResetTokenAsync(appUser);
This is how my IdentityServer is setup to use Asp Identity:
services
.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Lockout.AllowedForNewUsers = true;
options.Lockout.DefaultLockoutTimeSpan = new System.TimeSpan(12, 0, 0);
options.Lockout.MaxFailedAccessAttempts = int.Parse(Configuration["MaxFailedAttempts"]);
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.Authentication.CookieSlidingExpiration = true;
})
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.DefaultSchema = Globals.SCHEMA_IDS;
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.DefaultSchema = Globals.SCHEMA_IDS;
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
})
.AddProfileService<CustomProfileService>()
.AddSigninCredentialFromConfig(Configuration.GetSection("SigninKeyCredentials"), Logger);
and this is how my UserManagement Api is setup to use Asp Identity:
services.AddTransient<IUserStore<ApplicationUser>, UserStore<ApplicationUser, IdentityRole, ApplicationDbContext>>();
services.AddTransient<IRoleStore<IdentityRole>, RoleStore<IdentityRole, ApplicationDbContext>>();
services.AddTransient<IPasswordHasher<ApplicationUser>, PasswordHasher<ApplicationUser>>();
services.AddTransient<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.AddTransient<IdentityErrorDescriber>();
var identityBuilder = new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole), services);
identityBuilder.AddTokenProvider("Default", typeof(DataProtectorTokenProvider<ApplicationUser>));
services.AddTransient<UserManager<ApplicationUser>>();
Had to move on to other issues and just now getting back to this. I ended up solving this by ensuring that all my APIs and IdentityServer instance was configured to use ASP.NET Core Data Protection. I'm using redis as my distributed caching system and so just had to configure each of my api and identityserver and everything is now using the same keys when generating tokens. Below is what I use in each startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSession();
services.Configure<RedisConfiguration>(Configuration.GetSection("redis"));
services.AddDistributedRedisCache(options =>
{
options.Configuration = Configuration.GetValue<string>("redis:host");
});
var redis = ConnectionMultiplexer.Connect(Configuration.GetValue<string>("redis:host"));
services.AddDataProtection()
.PersistKeysToRedis(redis, "DataProtection-Keys")
.SetApplicationName(<application name>);
services.AddTransient<ICacheService, CacheService>();
...
}
and then don't forget to use the session (in APIs):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
app.UseSession();
...
}
use the session (in IdentityServer):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseIdentityServer();
app.UseSession();
...
}
As this is a 2 months old question you already soled it I guess. My suggestion would be: take a look at the life span of your token. And set it in your startup.cs like this:
services.Configure<DataProtectionTokenProviderOptions>(options =>
{
options.TokenLifespan = TimeSpan.FromHours(tokenlifespan);
});
Hope this will help you out!
I encountered the same "invalid token" issue with IdentityServer4 on ASP.NET Core 3.1.
An answer of Prathamesh Shende on learn.microsoft.com solved it for me:
In the ResetPassword action, the code first needed to be decoded before being passed on to the _userManager.ResetPasswordAsync method. Like so:
var decodedCode = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(model.Code));

Categories