[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-25442c01-520e-4c26-aa95-03034d8ae11a":3,"$fX8wOBissFgkcoxBFuUspsVVF2IRbX33wsO0_JLPFK5A":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"25442c01-520e-4c26-aa95-03034d8ae11a","microsoft-azure-webjobs-extensions-authentication-events-dotnet","微软Entra身份验证事件SDK for .NET。Azure Functions触发器用于自定义身份验证扩展。","cat_coding_devops","mod_coding","sickn33,coding","---\nname: microsoft-azure-webjobs-extensions-authentication-events-dotnet\ndescription: Microsoft Entra Authentication Events SDK for .NET. Azure Functions triggers for custom authentication extensions.\nrisk: unknown\nsource: community\ndate_added: '2026-02-27'\n---\n\n# Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents (.NET)\n\nAzure Functions extension for handling Microsoft Entra ID custom authentication events.\n\n## Installation\n\n```bash\ndotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents\n```\n\n**Current Version**: v1.1.0 (stable)\n\n## Supported Events\n\n| Event | Purpose |\n|-------|---------|\n| `OnTokenIssuanceStart` | Add custom claims to tokens during issuance |\n| `OnAttributeCollectionStart` | Customize attribute collection UI before display |\n| `OnAttributeCollectionSubmit` | Validate\u002Fmodify attributes after user submission |\n| `OnOtpSend` | Custom OTP delivery (SMS, email, etc.) |\n\n## Core Workflows\n\n### 1. Token Enrichment (Add Custom Claims)\n\nAdd custom claims to access or ID tokens during sign-in.\n\n```csharp\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;\nusing Microsoft.Extensions.Logging;\n\npublic static class TokenEnrichmentFunction\n{\n    [FunctionName(\"OnTokenIssuanceStart\")]\n    public static WebJobsAuthenticationEventResponse Run(\n        [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,\n        ILogger log)\n    {\n        log.LogInformation(\"Token issuance event for user: {UserId}\", \n            request.Data?.AuthenticationContext?.User?.Id);\n\n        \u002F\u002F Create response with custom claims\n        var response = new WebJobsTokenIssuanceStartResponse();\n        \n        \u002F\u002F Add claims to the token\n        response.Actions.Add(new WebJobsProvideClaimsForToken\n        {\n            Claims = new Dictionary\u003Cstring, string>\n            {\n                { \"customClaim1\", \"customValue1\" },\n                { \"department\", \"Engineering\" },\n                { \"costCenter\", \"CC-12345\" },\n                { \"apiVersion\", \"v2\" }\n            }\n        });\n\n        return response;\n    }\n}\n```\n\n### 2. Token Enrichment with External Data\n\nFetch claims from external systems (databases, APIs).\n\n```csharp\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.TokenIssuanceStart;\nusing Microsoft.Extensions.Logging;\nusing System.Net.Http;\nusing System.Text.Json;\n\npublic static class TokenEnrichmentWithExternalData\n{\n    private static readonly HttpClient _httpClient = new();\n\n    [FunctionName(\"OnTokenIssuanceStartExternal\")]\n    public static async Task\u003CWebJobsAuthenticationEventResponse> Run(\n        [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,\n        ILogger log)\n    {\n        string? userId = request.Data?.AuthenticationContext?.User?.Id;\n        \n        if (string.IsNullOrEmpty(userId))\n        {\n            log.LogWarning(\"No user ID in request\");\n            return new WebJobsTokenIssuanceStartResponse();\n        }\n\n        \u002F\u002F Fetch user data from external API\n        var userProfile = await GetUserProfileAsync(userId);\n        \n        var response = new WebJobsTokenIssuanceStartResponse();\n        response.Actions.Add(new WebJobsProvideClaimsForToken\n        {\n            Claims = new Dictionary\u003Cstring, string>\n            {\n                { \"employeeId\", userProfile.EmployeeId },\n                { \"department\", userProfile.Department },\n                { \"roles\", string.Join(\",\", userProfile.Roles) }\n            }\n        });\n\n        return response;\n    }\n\n    private static async Task\u003CUserProfile> GetUserProfileAsync(string userId)\n    {\n        var response = await _httpClient.GetAsync($\"https:\u002F\u002Fapi.example.com\u002Fusers\u002F{userId}\");\n        response.EnsureSuccessStatusCode();\n        var json = await response.Content.ReadAsStringAsync();\n        return JsonSerializer.Deserialize\u003CUserProfile>(json)!;\n    }\n}\n\npublic record UserProfile(string EmployeeId, string Department, string[] Roles);\n```\n\n### 3. Attribute Collection - Customize UI (Start Event)\n\nCustomize the attribute collection page before it's displayed.\n\n```csharp\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;\nusing Microsoft.Extensions.Logging;\n\npublic static class AttributeCollectionStartFunction\n{\n    [FunctionName(\"OnAttributeCollectionStart\")]\n    public static WebJobsAuthenticationEventResponse Run(\n        [WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionStartRequest request,\n        ILogger log)\n    {\n        log.LogInformation(\"Attribute collection start for correlation: {CorrelationId}\",\n            request.Data?.AuthenticationContext?.CorrelationId);\n\n        var response = new WebJobsAttributeCollectionStartResponse();\n\n        \u002F\u002F Option 1: Continue with default behavior\n        response.Actions.Add(new WebJobsContinueWithDefaultBehavior());\n\n        \u002F\u002F Option 2: Prefill attributes\n        \u002F\u002F response.Actions.Add(new WebJobsSetPrefillValues\n        \u002F\u002F {\n        \u002F\u002F     Attributes = new Dictionary\u003Cstring, string>\n        \u002F\u002F     {\n        \u002F\u002F         { \"city\", \"Seattle\" },\n        \u002F\u002F         { \"country\", \"USA\" }\n        \u002F\u002F     }\n        \u002F\u002F });\n\n        \u002F\u002F Option 3: Show blocking page (prevent sign-up)\n        \u002F\u002F response.Actions.Add(new WebJobsShowBlockPage\n        \u002F\u002F {\n        \u002F\u002F     Message = \"Sign-up is currently disabled.\"\n        \u002F\u002F });\n\n        return response;\n    }\n}\n```\n\n### 4. Attribute Collection - Validate Submission (Submit Event)\n\nValidate and modify attributes after user submission.\n\n```csharp\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;\nusing Microsoft.Extensions.Logging;\n\npublic static class AttributeCollectionSubmitFunction\n{\n    [FunctionName(\"OnAttributeCollectionSubmit\")]\n    public static WebJobsAuthenticationEventResponse Run(\n        [WebJobsAuthenticationEventsTrigger] WebJobsAttributeCollectionSubmitRequest request,\n        ILogger log)\n    {\n        var response = new WebJobsAttributeCollectionSubmitResponse();\n\n        \u002F\u002F Access submitted attributes\n        var attributes = request.Data?.UserSignUpInfo?.Attributes;\n        \n        string? email = attributes?[\"email\"]?.ToString();\n        string? displayName = attributes?[\"displayName\"]?.ToString();\n\n        \u002F\u002F Validation example: block certain email domains\n        if (email?.EndsWith(\"@blocked.com\") == true)\n        {\n            response.Actions.Add(new WebJobsShowBlockPage\n            {\n                Message = \"Sign-up from this email domain is not allowed.\"\n            });\n            return response;\n        }\n\n        \u002F\u002F Validation example: show validation error\n        if (string.IsNullOrEmpty(displayName) || displayName.Length \u003C 3)\n        {\n            response.Actions.Add(new WebJobsShowValidationError\n            {\n                Message = \"Display name must be at least 3 characters.\",\n                AttributeErrors = new Dictionary\u003Cstring, string>\n                {\n                    { \"displayName\", \"Name is too short\" }\n                }\n            });\n            return response;\n        }\n\n        \u002F\u002F Modify attributes before saving\n        response.Actions.Add(new WebJobsModifyAttributeValues\n        {\n            Attributes = new Dictionary\u003Cstring, string>\n            {\n                { \"displayName\", displayName.Trim() },\n                { \"city\", attributes?[\"city\"]?.ToString()?.ToUpperInvariant() ?? \"\" }\n            }\n        });\n\n        return response;\n    }\n}\n```\n\n### 5. Custom OTP Delivery\n\nSend one-time passwords via custom channels (SMS, email, push notification).\n\n```csharp\nusing Microsoft.Azure.WebJobs;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents;\nusing Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents.Framework;\nusing Microsoft.Extensions.Logging;\n\npublic static class CustomOtpFunction\n{\n    [FunctionName(\"OnOtpSend\")]\n    public static async Task\u003CWebJobsAuthenticationEventResponse> Run(\n        [WebJobsAuthenticationEventsTrigger] WebJobsOnOtpSendRequest request,\n        ILogger log)\n    {\n        var response = new WebJobsOnOtpSendResponse();\n\n        string? phoneNumber = request.Data?.OtpContext?.Identifier;\n        string? otp = request.Data?.OtpContext?.OneTimeCode;\n\n        if (string.IsNullOrEmpty(phoneNumber) || string.IsNullOrEmpty(otp))\n        {\n            log.LogError(\"Missing phone number or OTP\");\n            response.Actions.Add(new WebJobsOnOtpSendFailed\n            {\n                Error = \"Missing required data\"\n            });\n            return response;\n        }\n\n        try\n        {\n            \u002F\u002F Send OTP via your SMS provider\n            await SendSmsAsync(phoneNumber, $\"Your verification code is: {otp}\");\n            \n            response.Actions.Add(new WebJobsOnOtpSendSuccess());\n            log.LogInformation(\"OTP sent successfully to {PhoneNumber}\", phoneNumber);\n        }\n        catch (Exception ex)\n        {\n            log.LogError(ex, \"Failed to send OTP\");\n            response.Actions.Add(new WebJobsOnOtpSendFailed\n            {\n                Error = \"Failed to send verification code\"\n            });\n        }\n\n        return response;\n    }\n\n    private static async Task SendSmsAsync(string phoneNumber, string message)\n    {\n        \u002F\u002F Implement your SMS provider integration (Twilio, Azure Communication Services, etc.)\n        await Task.CompletedTask;\n    }\n}\n```\n\n### 6. Function App Configuration\n\nConfigure the Function App for authentication events.\n\n```csharp\n\u002F\u002F Program.cs (Isolated worker model)\nusing Microsoft.Extensions.Hosting;\n\nvar host = new HostBuilder()\n    .ConfigureFunctionsWorkerDefaults()\n    .Build();\n\nhost.Run();\n```\n\n```json\n\u002F\u002F host.json\n{\n  \"version\": \"2.0\",\n  \"logging\": {\n    \"applicationInsights\": {\n      \"samplingSettings\": {\n        \"isEnabled\": true\n      }\n    }\n  },\n  \"extensions\": {\n    \"http\": {\n      \"routePrefix\": \"\"\n    }\n  }\n}\n```\n\n```json\n\u002F\u002F local.settings.json\n{\n  \"IsEncrypted\": false,\n  \"Values\": {\n    \"AzureWebJobsStorage\": \"UseDevelopmentStorage=true\",\n    \"FUNCTIONS_WORKER_RUNTIME\": \"dotnet\"\n  }\n}\n```\n\n## Key Types Reference\n\n| Type | Purpose |\n|------|---------|\n| `WebJobsAuthenticationEventsTriggerAttribute` | Function trigger attribute |\n| `WebJobsTokenIssuanceStartRequest` | Token issuance event request |\n| `WebJobsTokenIssuanceStartResponse` | Token issuance event response |\n| `WebJobsProvideClaimsForToken` | Action to add claims |\n| `WebJobsAttributeCollectionStartRequest` | Attribute collection start request |\n| `WebJobsAttributeCollectionStartResponse` | Attribute collection start response |\n| `WebJobsAttributeCollectionSubmitRequest` | Attribute submission request |\n| `WebJobsAttributeCollectionSubmitResponse` | Attribute submission response |\n| `WebJobsSetPrefillValues` | Prefill form values |\n| `WebJobsShowBlockPage` | Block user with message |\n| `WebJobsShowValidationError` | Show validation errors |\n| `WebJobsModifyAttributeValues` | Modify submitted values |\n| `WebJobsOnOtpSendRequest` | OTP send event request |\n| `WebJobsOnOtpSendResponse` | OTP send event response |\n| `WebJobsOnOtpSendSuccess` | OTP sent successfully |\n| `WebJobsOnOtpSendFailed` | OTP send failed |\n| `WebJobsContinueWithDefaultBehavior` | Continue with default flow |\n\n## Entra ID Configuration\n\nAfter deploying your Function App, configure the custom extension in Entra ID:\n\n1. **Register the API** in Entra ID → App registrations\n2. **Create Custom Authentication Extension** in Entra ID → External Identities → Custom authentication extensions\n3. **Link to User Flow** in Entra ID → External Identities → User flows\n\n### Required App Registration Settings\n\n```\nExpose an API:\n  - Application ID URI: api:\u002F\u002F\u003Cyour-function-app-name>.azurewebsites.net\n  - Scope: CustomAuthenticationExtension.Receive.Payload\n\nAPI Permissions:\n  - Microsoft Graph: User.Read (delegated)\n```\n\n## Best Practices\n\n1. **Validate all inputs** — Never trust request data; validate before processing\n2. **Handle errors gracefully** — Return appropriate error responses\n3. **Log correlation IDs** — Use `CorrelationId` for troubleshooting\n4. **Keep functions fast** — Authentication events have timeout limits\n5. **Use managed identity** — Access Azure resources securely\n6. **Cache external data** — Avoid slow lookups on every request\n7. **Test locally** — Use Azure Functions Core Tools with sample payloads\n8. **Monitor with App Insights** — Track function execution and errors\n\n## Error Handling\n\n```csharp\n[FunctionName(\"OnTokenIssuanceStart\")]\npublic static WebJobsAuthenticationEventResponse Run(\n    [WebJobsAuthenticationEventsTrigger] WebJobsTokenIssuanceStartRequest request,\n    ILogger log)\n{\n    try\n    {\n        \u002F\u002F Your logic here\n        var response = new WebJobsTokenIssuanceStartResponse();\n        response.Actions.Add(new WebJobsProvideClaimsForToken\n        {\n            Claims = new Dictionary\u003Cstring, string> { { \"claim\", \"value\" } }\n        });\n        return response;\n    }\n    catch (Exception ex)\n    {\n        log.LogError(ex, \"Error processing token issuance event\");\n        \n        \u002F\u002F Return empty response - authentication continues without custom claims\n        \u002F\u002F Do NOT throw - this would fail the authentication\n        return new WebJobsTokenIssuanceStartResponse();\n    }\n}\n```\n\n## Related SDKs\n\n| SDK | Purpose | Install |\n|-----|---------|---------|\n| `Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents` | Auth events (this SDK) | `dotnet add package Microsoft.Azure.WebJobs.Extensions.AuthenticationEvents` |\n| `Microsoft.Identity.Web` | Web app authentication | `dotnet add package Microsoft.Identity.Web` |\n| `Azure.Identity` | Azure authentication | `dotnet add package Azure.Identity` |\n\n## Reference Links\n\n| Resource | URL |\n|----------|-----|\n| NuGet Package | https:\u002F\u002Fwww.nuget.org\u002Fpackages\u002FMicrosoft.Azure.WebJobs.Extensions.AuthenticationEvents |\n| Custom Extensions Overview | https:\u002F\u002Flearn.microsoft.com\u002Fentra\u002Fidentity-platform\u002Fcustom-extension-overview |\n| Token Issuance Events | https:\u002F\u002Flearn.microsoft.com\u002Fentra\u002Fidentity-platform\u002Fcustom-extension-tokenissuancestart-setup |\n| Attribute Collection Events | https:\u002F\u002Flearn.microsoft.com\u002Fentra\u002Fidentity-platform\u002Fcustom-extension-attribute-collection |\n| GitHub Source | https:\u002F\u002Fgithub.com\u002FAzure\u002Fazure-sdk-for-net\u002Ftree\u002Fmain\u002Fsdk\u002Fentra\u002FMicrosoft.Azure.WebJobs.Extensions.AuthenticationEvents |\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,61,584,"2026-05-16 13:28:40",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"编程开发","coding","mdi-code-braces","代码生成、调试、审查，提升开发效率",2,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"DevOps","devops","mdi-cog-outline","CI\u002FCD、容器化、部署运维",3,162,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"28f3b74a-1e48-4a71-8f61-1f9f0914ffd9","1.0.0","microsoft-azure-webjobs-extensions-authentication-events-dotnet.zip",4232,"uploads\u002Fskills\u002F25442c01-520e-4c26-aa95-03034d8ae11a\u002Fmicrosoft-azure-webjobs-extensions-authentication-events-dotnet.zip","800b9b28ac2b94703d37c1ecd46df6c97befaad76ffa35dd451422c30abada6b","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":15179}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]