Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents (.NET)
Azure Functions extension for handling Microsoft Entra ID custom authentication events.
Installation
bash
dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEventsCurrent Version: v1.1.0 (stable)
Supported Events
| Event | Purpose |
|---|---|
OnTokenIssuanceStart | Add custom claims to tokens during issuance |
OnAttributeCollectionStart | Customize attribute collection UI before display |
OnAttributeCollectionSubmit | Validate/modify attributes after user submission |
OnOtpSend | Custom OTP delivery (SMS, email, etc.) |
Core Workflows
1. Token Enrichment (Add Custom Claims)
Add custom claims to access or ID tokens during sign-in.
csharp
using Microsoft.Azure.WebJobs;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;using Microsoft.Extensions.Logging;
public static class TokenEnrichmentFunction{ [FunctionName("OnTokenIssuanceStart")] public static WebJobsAuthenticationEventResponse Run( [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request, ILogger log) { log.LogInformation("Token issuance event for user: {UserId}", request.Data?.AuthenticationContext?.User?.Id);
// Create response with custom claims var response = new WebJobsTokenIssuanceStartResponse(); // Add claims to the token response.Actions.Add(new WebJobsProvideClaimsForToken { Claims = new Dictionary<string, string> { { "customClaim1", "customValue1" }, { "department", "Engineering" }, { "costCenter", "CC-12345" }, { "apiVersion", "v2" } } });
return response; }}2. Token Enrichment with External Data
Fetch claims from external systems (databases, APIs).
csharp
using Microsoft.Azure.WebJobs;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;using Microsoft.Extensions.Logging;using System.Net.Http;using System.Text.Json;
public static class TokenEnrichmentWithExternalData{ private static readonly HttpClient _httpClient = new();
[FunctionName("OnTokenIssuanceStartExternal")] public static async Task<WebJobsAuthenticationEventResponse> Run( [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request, ILogger log) { string? userId = request.Data?.AuthenticationContext?.User?.Id; if (string.IsNullOrEmpty(userId)) { log.LogWarning("No user ID in request"); return new WebJobsTokenIssuanceStartResponse(); }
// Fetch user data from external API var userProfile = await GetUserProfileAsync(userId); var response = new WebJobsTokenIssuanceStartResponse(); response.Actions.Add(new WebJobsProvideClaimsForToken { Claims = new Dictionary<string, string> { { "employeeId", userProfile.EmployeeId }, { "department", userProfile.Department }, { "roles", string.Join(",", userProfile.Roles) } } });
return response; }
private static async Task<UserProfile> GetUserProfileAsync(string userId) { var response = await _httpClient.GetAsync($"https://api.example.com/users/{userId}"); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize<UserProfile>(json)!; }}
public record UserProfile(string EmployeeId, string Department, string[] Roles);3. Attribute Collection - Customize UI (Start Event)
Customize the attribute collection page before it's displayed.
csharp
using Microsoft.Azure.WebJobs;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;using Microsoft.Extensions.Logging;
public static class AttributeCollectionStartFunction{ [FunctionName("OnAttributeCollectionStart")] public static WebJobsAuthenticationEventResponse Run( [WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionStartRequest request, ILogger log) { log.LogInformation("Attribute collection start for correlation: {CorrelationId}", request.Data?.AuthenticationContext?.CorrelationId);
var response = new WebJobsAttributeCollectionStartResponse();
// Option 1: Continue with default behavior response.Actions.Add(new WebJobsContinueWithDefaultBehavior());
// Option 2: Prefill attributes // response.Actions.Add(new WebJobsSetPrefillValues // { // Attributes = new Dictionary<string, string> // { // { "city", "Seattle" }, // { "country", "USA" } // } // });
// Option 3: Show blocking page (prevent sign-up) // response.Actions.Add(new WebJobsShowBlockPage // { // Message = "Sign-up is currently disabled." // });
return response; }}4. Attribute Collection - Validate Submission (Submit Event)
Validate and modify attributes after user submission.
csharp
using Microsoft.Azure.WebJobs;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;using Microsoft.Extensions.Logging;
public static class AttributeCollectionSubmitFunction{ [FunctionName("OnAttributeCollectionSubmit")] public static WebJobsAuthenticationEventResponse Run( [WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionSubmitRequest request, ILogger log) { var response = new WebJobsAttributeCollectionSubmitResponse();
// Access submitted attributes var attributes = request.Data?.UserSignUpInfo?.Attributes; string? email = attributes?["email"]?.ToString(); string? displayName = attributes?["displayName"]?.ToString();
// Validation example: block certain email domains if (email?.EndsWith("@blocked.com") == true) { response.Actions.Add(new WebJobsShowBlockPage { Message = "Sign-up from this email domain is not allowed." }); return response; }
// Validation example: show validation error if (string.IsNullOrEmpty(displayName) || displayName.Length < 3) { response.Actions.Add(new WebJobsShowValidationError { Message = "Display name must be at least 3 characters.", AttributeErrors = new Dictionary<string, string> { { "displayName", "Name is too short" } } }); return response; }
// Modify attributes before saving response.Actions.Add(new WebJobsModifyAttributeValues { Attributes = new Dictionary<string, string> { { "displayName", displayName.Trim() }, { "city", attributes?["city"]?.ToString()?.ToUpperInvariant() ?? "" } } });
return response; }}5. Custom OTP Delivery
Send one-time passwords via custom channels (SMS, email, push notification).
csharp
using Microsoft.Azure.WebJobs;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;using Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;using Microsoft.Extensions.Logging;
public static class CustomOtpFunction{ [FunctionName("OnOtpSend")] public static async Task<WebJobsAuthenticationEventResponse> Run( [WebJobsAuthenticationEventsTrigger] WebJobsOnOtpSendRequest request, ILogger log) { var response = new WebJobsOnOtpSendResponse();
string? phoneNumber = request.Data?.OtpContext?.Identifier; string? otp = request.Data?.OtpContext?.OneTimeCode;
if (string.IsNullOrEmpty(phoneNumber) || string.IsNullOrEmpty(otp)) { log.LogError("Missing phone number or OTP"); response.Actions.Add(new WebJobsOnOtpSendFailed { Error = "Missing required data" }); return response; }
try { // Send OTP via your SMS provider await SendSmsAsync(phoneNumber, $"Your verification code is: {otp}"); response.Actions.Add(new WebJobsOnOtpSendSuccess()); log.LogInformation("OTP sent successfully to {PhoneNumber}", phoneNumber); } catch (Exception ex) { log.LogError(ex, "Failed to send OTP"); response.Actions.Add(new WebJobsOnOtpSendFailed { Error = "Failed to send verification code" }); }
return response; }
private static async Task SendSmsAsync(string phoneNumber, string message) { // Implement your SMS provider integration (Twilio, Azure Communication Services, etc.) await Task.CompletedTask; }}6. Function App Configuration
Configure the Function App for authentication events.
csharp
// Program.cs (Isolated worker model)using Microsoft.Extensions.Hosting;
var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .Build();
host.Run();json
// host.json{ "version": "2.0", "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true } } }, "extensions": { "http": { "routePrefix": "" } }}json
// local.settings.json{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet" }}Key Types Reference
| Type | Purpose |
|---|---|
WebJobsAuthenticationEventsTriggerAttribute | Function trigger attribute |
WebJobsTokenIssuanceStartRequest | Token issuance event request |
WebJobsTokenIssuanceStartResponse | Token issuance event response |
WebJobsProvideClaimsForToken | Action to add claims |
WebJobsAttributeCollectionStartRequest | Attribute collection start request |
WebJobsAttributeCollectionStartResponse | Attribute collection start response |
WebJobsAttributeCollectionSubmitRequest | Attribute submission request |
WebJobsAttributeCollectionSubmitResponse | Attribute submission response |
WebJobsSetPrefillValues | Prefill form values |
WebJobsShowBlockPage | Block user with message |
WebJobsShowValidationError | Show validation errors |
WebJobsModifyAttributeValues | Modify submitted values |
WebJobsOnOtpSendRequest | OTP send event request |
WebJobsOnOtpSendResponse | OTP send event response |
WebJobsOnOtpSendSuccess | OTP sent successfully |
WebJobsOnOtpSendFailed | OTP send failed |
WebJobsContinueWithDefaultBehavior | Continue with default flow |
Entra ID Configuration
After deploying your Function App, configure the custom extension in Entra ID:
- Register the API in Entra ID → App registrations
- Create Custom Authentication Extension in Entra ID → External Identities → Custom authentication extensions
- Link to User Flow in Entra ID → External Identities → User flows
Required App Registration Settings
Expose an API: - Application ID URI: api://<your-function-app-name>.azurewebsites.net - Scope: CustomAuthenticationExtension.Receive.Payload
API Permissions: - Microsoft Graph: User.Read (delegated)Best Practices
- Validate all inputs — Never trust request data; validate before processing
- Handle errors gracefully — Return appropriate error responses
- Log correlation IDs — Use
CorrelationIdfor troubleshooting - Keep functions fast — Authentication events have timeout limits
- Use managed identity — Access Azure resources securely
- Cache external data — Avoid slow lookups on every request
- Test locally — Use Azure Functions Core Tools with sample payloads
- Monitor with App Insights — Track function execution and errors
Error Handling
csharp
[FunctionName("OnTokenIssuanceStart")]public static WebJobsAuthenticationEventResponse Run( [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request, ILogger log){ try { // Your logic here var response = new WebJobsTokenIssuanceStartResponse(); response.Actions.Add(new WebJobsProvideClaimsForToken { Claims = new Dictionary<string, string> { { "claim", "value" } } }); return response; } catch (Exception ex) { log.LogError(ex, "Error processing token issuance event"); // Return empty response - authentication continues without custom claims // Do NOT throw - this would fail the authentication return new WebJobsTokenIssuanceStartResponse(); }}Related SDKs
| SDK | Purpose | Install |
|---|---|---|
Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents | Auth events (this SDK) | dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents |
Microsoft.Identity.Web | Web app authentication | dotnet add package Microsoft.Identity.Web |
Azure.Identity | Azure authentication | dotnet add package Azure.Identity |


