diff --git a/BREAKINGCHANGES.md b/BREAKINGCHANGES.md
index 102bbea5..cf18bf16 100644
--- a/BREAKINGCHANGES.md
+++ b/BREAKINGCHANGES.md
@@ -7,6 +7,7 @@ ___Note:___ We might, and will probably, miss to document some of this - if so -
## TOC
+* [Version 13.0.0 (TBD)](#version-1300-tbd)
* [Version 12.0.0](#version-1200)
* [Version 11.0.0](#version-1100)
* [Version 10.0.0](#version-1000)
@@ -20,6 +21,30 @@ ___Note:___ We might, and will probably, miss to document some of this - if so -
------
+## Version 13.0.0 (TBD)
+
+Breaking changes between version 12.0.0 and 13.0.0
+
+### Upgrade to Application Insights 3.x
+
+`ActiveLogin.Authentication.BankId.AzureMonitor` now depends on `Microsoft.ApplicationInsights` 3.x. Application Insights 2.x and 3.x can't be mixed in the same application, so if you use `AddApplicationInsightsEventListener()` you must also upgrade the Application Insights packages in your application (for example `Microsoft.ApplicationInsights.AspNetCore`) to 3.x. Application Insights 3.x requires a connection string (not an instrumentation key) and fails at startup without one. See the [Application Insights migration guide](https://learn.microsoft.com/en-us/azure/azure-monitor/app/migrate-to-opentelemetry) for details.
+
+### Age hint moved from customMeasurements to customDimensions
+
+`AL_User_AgeHint` is no longer sent as a metric (`customMeasurements`). It is now sent as a string property in `customDimensions` on the same event. Update any queries or dashboards that read it:
+
+```kql
+// Before
+UserAgeHint = toint(customMeasurements.AL_User_AgeHint)
+
+// After
+UserAgeHint = toint(customDimensions.AL_User_AgeHint)
+```
+
+The sample queries in [the monitor documentation](https://docs.activelogin.net/articles/monitor.html) have been updated.
+
+---
+
## Version 12.0.0
Breaking changes between version 11.0.0 and 12.0.0
diff --git a/docs/articles/bankid.md b/docs/articles/bankid.md
index 3cd8f665..7854802d 100644
--- a/docs/articles/bankid.md
+++ b/docs/articles/bankid.md
@@ -1091,7 +1091,7 @@ services
});
```
-By default it will use whatever InstrumentationKey is registered with the application. There are overloads available so you can customize this:
+By default it will use whatever Application Insights connection string is registered with the application. There are overloads available so you can customize this:
```csharp
services
diff --git a/docs/articles/monitor.md b/docs/articles/monitor.md
index 002d99fd..1d32b377 100644
--- a/docs/articles/monitor.md
+++ b/docs/articles/monitor.md
@@ -127,7 +127,7 @@ customEvents
customEvents
| where name == "ActiveLogin_BankId_CollectCompleted"
| project
- UserAgeHint = toint(customMeasurements.AL_User_AgeHint)
+ UserAgeHint = toint(customDimensions.AL_User_AgeHint)
| summarize AverageUserAge = avg(UserAgeHint)
```
diff --git a/samples/AzureProvisioningSample/ActiveLogin.json b/samples/AzureProvisioningSample/ActiveLogin.json
index a89f01bc..2f24c5a4 100644
--- a/samples/AzureProvisioningSample/ActiveLogin.json
+++ b/samples/AzureProvisioningSample/ActiveLogin.json
@@ -369,7 +369,7 @@
"[resourceId('Microsoft.Insights/components', variables('insights_resource_name'))]"
],
"properties": {
- "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', variables('insights_resource_name'))).InstrumentationKey]",
+ "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('Microsoft.Insights/components', variables('insights_resource_name'))).ConnectionString]",
"APPINSIGHTS_PORTALINFO": "ASP.NETCORE",
"APPINSIGHTS_PROFILERFEATURE_VERSION": "disabled",
"APPINSIGHTS_SNAPSHOTFEATURE_VERSION": "disabled"
diff --git a/samples/IdentityServer.ClientSample/IdentityServer.ClientSample.csproj b/samples/IdentityServer.ClientSample/IdentityServer.ClientSample.csproj
index 0087ca21..8289ba00 100644
--- a/samples/IdentityServer.ClientSample/IdentityServer.ClientSample.csproj
+++ b/samples/IdentityServer.ClientSample/IdentityServer.ClientSample.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/samples/IdentityServer.ClientSample/Program.cs b/samples/IdentityServer.ClientSample/Program.cs
index 01c49a3d..0c462322 100644
--- a/samples/IdentityServer.ClientSample/Program.cs
+++ b/samples/IdentityServer.ClientSample/Program.cs
@@ -26,8 +26,16 @@
var environment = builder.Environment;
-// Add telemetry
-services.AddApplicationInsightsTelemetry(configuration);
+// Add telemetry (Application Insights 3.x requires a connection string)
+var applicationInsightsConnectionString = configuration["ApplicationInsights:ConnectionString"];
+if (string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
+{
+ applicationInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+}
+if (!string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
+{
+ services.AddApplicationInsightsTelemetry(configuration);
+}
// Add authentication
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
diff --git a/samples/IdentityServer.ClientSample/appsettings.json b/samples/IdentityServer.ClientSample/appsettings.json
index 0c007234..219c6b1d 100644
--- a/samples/IdentityServer.ClientSample/appsettings.json
+++ b/samples/IdentityServer.ClientSample/appsettings.json
@@ -7,7 +7,7 @@
}
},
"ApplicationInsights": {
- "InstrumentationKey": ""
+ "ConnectionString": ""
},
"ActiveLogin": {
"IdentityProvider": {
diff --git a/samples/IdentityServer.ServerSample/IdentityServer.ServerSample.csproj b/samples/IdentityServer.ServerSample/IdentityServer.ServerSample.csproj
index 094782ea..54a00bef 100644
--- a/samples/IdentityServer.ServerSample/IdentityServer.ServerSample.csproj
+++ b/samples/IdentityServer.ServerSample/IdentityServer.ServerSample.csproj
@@ -20,9 +20,8 @@
-
+
-
diff --git a/samples/IdentityServer.ServerSample/Program.cs b/samples/IdentityServer.ServerSample/Program.cs
index b73599f0..e0133857 100644
--- a/samples/IdentityServer.ServerSample/Program.cs
+++ b/samples/IdentityServer.ServerSample/Program.cs
@@ -40,8 +40,17 @@
var environment = builder.Environment;
-// Add telemetry
-services.AddApplicationInsightsTelemetry(configuration);
+// Add telemetry (Application Insights 3.x requires a connection string)
+var applicationInsightsConnectionString = configuration["ApplicationInsights:ConnectionString"];
+if (string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
+{
+ applicationInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+}
+var useApplicationInsights = !string.IsNullOrWhiteSpace(applicationInsightsConnectionString);
+if (useApplicationInsights)
+{
+ services.AddApplicationInsightsTelemetry(configuration);
+}
// Configure cookie policy
services.Configure(options =>
@@ -83,17 +92,20 @@
.AddBankId(bankId =>
{
bankId.AddDebugEventListener();
- bankId.AddApplicationInsightsEventListener(options =>
+ if (useApplicationInsights)
{
- options.LogUserPersonalIdentityNumber = false;
- options.LogUserPersonalIdentityNumberHints = true;
+ bankId.AddApplicationInsightsEventListener(options =>
+ {
+ options.LogUserPersonalIdentityNumber = false;
+ options.LogUserPersonalIdentityNumberHints = true;
- options.LogUserNames = false;
- options.LogUserBankIdIssueDate = false;
+ options.LogUserNames = false;
+ options.LogUserBankIdIssueDate = false;
- options.LogDeviceIpAddress = false;
- options.LogDeviceUniqueHardwareId = false;
- });
+ options.LogDeviceIpAddress = false;
+ options.LogDeviceUniqueHardwareId = false;
+ });
+ }
bankId.UseQrCoderQrCodeGenerator();
bankId.UseUaParserDeviceDetection();
diff --git a/samples/IdentityServer.ServerSample/Views/Shared/_LayoutMaster.cshtml b/samples/IdentityServer.ServerSample/Views/Shared/_LayoutMaster.cshtml
index 55cc812c..f314355e 100644
--- a/samples/IdentityServer.ServerSample/Views/Shared/_LayoutMaster.cshtml
+++ b/samples/IdentityServer.ServerSample/Views/Shared/_LayoutMaster.cshtml
@@ -1,4 +1,8 @@
-@inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet
+@using Microsoft.Extensions.DependencyInjection
+@{
+ // Only registered when an Application Insights connection string is configured
+ var JavaScriptSnippet = Context.RequestServices.GetService();
+}
@@ -56,7 +60,10 @@
- @Html.Raw(JavaScriptSnippet.FullScript)
+ @if (JavaScriptSnippet != null)
+ {
+ @Html.Raw(JavaScriptSnippet.FullScript)
+ }
@RenderBody()
diff --git a/samples/IdentityServer.ServerSample/appsettings.json b/samples/IdentityServer.ServerSample/appsettings.json
index 9364b03c..fc4c7e54 100644
--- a/samples/IdentityServer.ServerSample/appsettings.json
+++ b/samples/IdentityServer.ServerSample/appsettings.json
@@ -7,7 +7,7 @@
}
},
"ApplicationInsights": {
- "InstrumentationKey": ""
+ "ConnectionString": ""
},
"ActiveLogin": {
"Clients": {
diff --git a/samples/Standalone.MvcSample/Program.cs b/samples/Standalone.MvcSample/Program.cs
index 49e271e4..f98d946c 100644
--- a/samples/Standalone.MvcSample/Program.cs
+++ b/samples/Standalone.MvcSample/Program.cs
@@ -38,8 +38,17 @@
var configuration = builder.Configuration;
var environment = builder.Environment;
-// Add telemetry
-services.AddApplicationInsightsTelemetry(configuration);
+// Add telemetry (Application Insights 3.x requires a connection string)
+var applicationInsightsConnectionString = configuration["ApplicationInsights:ConnectionString"];
+if (string.IsNullOrWhiteSpace(applicationInsightsConnectionString))
+{
+ applicationInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+}
+var useApplicationInsights = !string.IsNullOrWhiteSpace(applicationInsightsConnectionString);
+if (useApplicationInsights)
+{
+ services.AddApplicationInsightsTelemetry(configuration);
+}
// Configure cookie policy
services.Configure(options =>
@@ -54,18 +63,21 @@
.AddBankId(bankId =>
{
bankId.AddDebugEventListener();
- bankId.AddApplicationInsightsEventListener(options =>
+ if (useApplicationInsights)
{
- options.LogUserPersonalIdentityNumber = false;
- options.LogUserPersonalIdentityNumberHints = true;
+ bankId.AddApplicationInsightsEventListener(options =>
+ {
+ options.LogUserPersonalIdentityNumber = false;
+ options.LogUserPersonalIdentityNumberHints = true;
- options.LogUserNames = false;
+ options.LogUserNames = false;
- options.LogUserBankIdIssueDate = false;
+ options.LogUserBankIdIssueDate = false;
- options.LogDeviceIpAddress = false;
- options.LogDeviceUniqueHardwareId = false;
- });
+ options.LogDeviceIpAddress = false;
+ options.LogDeviceUniqueHardwareId = false;
+ });
+ }
bankId.UseDeviceData(config =>
{
diff --git a/samples/Standalone.MvcSample/Standalone.MvcSample.csproj b/samples/Standalone.MvcSample/Standalone.MvcSample.csproj
index f7bd8945..b8aac693 100644
--- a/samples/Standalone.MvcSample/Standalone.MvcSample.csproj
+++ b/samples/Standalone.MvcSample/Standalone.MvcSample.csproj
@@ -31,6 +31,6 @@
-
+
diff --git a/samples/Standalone.MvcSample/appsettings.json b/samples/Standalone.MvcSample/appsettings.json
index c2b34dc7..b270ce50 100644
--- a/samples/Standalone.MvcSample/appsettings.json
+++ b/samples/Standalone.MvcSample/appsettings.json
@@ -1,7 +1,7 @@
{
"AllowedHosts": "*",
"ApplicationInsights": {
- "InstrumentationKey": ""
+ "ConnectionString": ""
},
"Logging": {
"LogLevel": {
diff --git a/src/ActiveLogin.Authentication.BankId.AzureMonitor/ActiveLogin.Authentication.BankId.AzureMonitor.csproj b/src/ActiveLogin.Authentication.BankId.AzureMonitor/ActiveLogin.Authentication.BankId.AzureMonitor.csproj
index 587f4cdc..e2ccf32d 100644
--- a/src/ActiveLogin.Authentication.BankId.AzureMonitor/ActiveLogin.Authentication.BankId.AzureMonitor.csproj
+++ b/src/ActiveLogin.Authentication.BankId.AzureMonitor/ActiveLogin.Authentication.BankId.AzureMonitor.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsEventListener.cs b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsEventListener.cs
index 6304768b..223732d7 100644
--- a/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsEventListener.cs
+++ b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsEventListener.cs
@@ -1,3 +1,5 @@
+using System.Globalization;
+
using ActiveLogin.Authentication.BankId.Api;
using ActiveLogin.Authentication.BankId.Core.Events;
using ActiveLogin.Authentication.BankId.Core.Events.Infrastructure;
@@ -257,10 +259,9 @@ public override Task HandleCancelFailureEvent(BankIdCancelErrorEvent e)
// Helpers
- private Task Track(BankIdEvent e, Dictionary? properties = null, Dictionary? metrics = null, PersonalIdentityNumber? personalIdentityNumber = null, Exception? exception = null, BankIdSupportedDevice? detectedDevice = null, BankIdFlowOptions? bankIdOptions = null)
+ private Task Track(BankIdEvent e, Dictionary? properties = null, PersonalIdentityNumber? personalIdentityNumber = null, Exception? exception = null, BankIdSupportedDevice? detectedDevice = null, BankIdFlowOptions? bankIdOptions = null)
{
var allProperties = properties == null ? new Dictionary() : new Dictionary(properties);
- var allMetrics = metrics == null ? new Dictionary() : new Dictionary(metrics);
allProperties.Add(PropertyName_ProductName, e.ActiveLoginProductName);
allProperties.Add(PropertyName_ProductVersion, e.ActiveLoginProductVersion);
@@ -278,7 +279,7 @@ private Task Track(BankIdEvent e, Dictionary? properties = null,
if (personalIdentityNumber != null)
{
- AddPersonalIdentityNumberProperties(allProperties, allMetrics, personalIdentityNumber);
+ AddPersonalIdentityNumberProperties(allProperties, personalIdentityNumber);
}
if (detectedDevice != null)
@@ -294,15 +295,15 @@ private Task Track(BankIdEvent e, Dictionary? properties = null,
allProperties.Add(PropertyName_BankIdErrorDetails, bankIdApiException.ErrorDetails);
}
- _telemetryClient.TrackException(exception, allProperties, metrics);
+ _telemetryClient.TrackException(exception, allProperties);
}
- _telemetryClient.TrackEvent(e.EventTypeName, allProperties, allMetrics);
+ _telemetryClient.TrackEvent(e.EventTypeName, allProperties);
return Task.CompletedTask;
}
- private void AddPersonalIdentityNumberProperties(Dictionary properties, Dictionary metrics, PersonalIdentityNumber personalIdentityNumber)
+ private void AddPersonalIdentityNumberProperties(Dictionary properties, PersonalIdentityNumber personalIdentityNumber)
{
if (_options.LogUserPersonalIdentityNumber)
{
@@ -317,7 +318,7 @@ private void AddPersonalIdentityNumberProperties(Dictionary prop
var ageHint = personalIdentityNumber?.GetAgeHint();
if (ageHint != null)
{
- metrics.Add(PropertyName_UserAgeHint, ageHint.Value);
+ properties.Add(PropertyName_UserAgeHint, ageHint.Value.ToString(CultureInfo.InvariantCulture));
}
}
}
diff --git a/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsTelemetryClientProvider.cs b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsTelemetryClientProvider.cs
new file mode 100644
index 00000000..454e4ea1
--- /dev/null
+++ b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdApplicationInsightsTelemetryClientProvider.cs
@@ -0,0 +1,43 @@
+using Microsoft.ApplicationInsights;
+using Microsoft.ApplicationInsights.Extensibility;
+
+namespace ActiveLogin.Authentication.BankId.AzureMonitor;
+
+///
+/// Owns a and created from a connection string,
+/// so that buffered telemetry is flushed and the configuration is disposed when the container is disposed.
+///
+internal sealed class BankIdApplicationInsightsTelemetryClientProvider : IDisposable
+{
+ private readonly TelemetryConfiguration _telemetryConfiguration;
+ private bool _disposed;
+
+ public BankIdApplicationInsightsTelemetryClientProvider(string connectionString)
+ {
+ _telemetryConfiguration = new TelemetryConfiguration
+ {
+ ConnectionString = connectionString
+ };
+ TelemetryClient = new TelemetryClient(_telemetryConfiguration);
+ }
+
+ public TelemetryClient TelemetryClient { get; }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+ try
+ {
+ TelemetryClient.Flush();
+ }
+ finally
+ {
+ _telemetryConfiguration.Dispose();
+ }
+ }
+}
diff --git a/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdBuilderAzureMonitorExtensions.cs b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdBuilderAzureMonitorExtensions.cs
index 74257ca8..e6fd53ee 100644
--- a/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdBuilderAzureMonitorExtensions.cs
+++ b/src/ActiveLogin.Authentication.BankId.AzureMonitor/BankIdBuilderAzureMonitorExtensions.cs
@@ -2,7 +2,6 @@
using ActiveLogin.Authentication.BankId.Core.Events.Infrastructure;
using Microsoft.ApplicationInsights;
-using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.DependencyInjection;
namespace ActiveLogin.Authentication.BankId.AzureMonitor;
@@ -80,12 +79,12 @@ public static IBankIdBuilder AddApplicationInsightsEventListener(this IBankIdBui
///
public static IBankIdBuilder AddApplicationInsightsEventListener(this IBankIdBuilder builder, string connectionString, ApplicationInsightsBankIdEventListenerOptions options)
{
- builder.Services.AddTransient(x =>
- {
- var telemetryConfiguration = new TelemetryConfiguration();
- telemetryConfiguration.ConnectionString = connectionString;
- return new BankIdApplicationInsightsEventListener(new TelemetryClient(telemetryConfiguration), options);
- });
+ // The listener is transient, so the configuration and client are owned by a singleton provider.
+ // The container disposes it on shutdown, which flushes buffered telemetry.
+ // The provider is keyed per call so that multiple registrations each use their own connection string.
+ var providerKey = new object();
+ builder.Services.AddKeyedSingleton(providerKey, (_, _) => new BankIdApplicationInsightsTelemetryClientProvider(connectionString));
+ builder.Services.AddTransient(x => new BankIdApplicationInsightsEventListener(x.GetRequiredKeyedService(providerKey).TelemetryClient, options));
return builder;
}