多租户Azure Web App与Web Job的身份验证/授权共享咨询
We'll split this into three core parts: setting up multi-tenant authentication in the Web App, passing user context to the Web Job securely, and modifying the Web Job to use delegated permissions via refresh tokens.
1. Configure Multi-Tenant App Registration
- Update your app registration to support multi-tenant accounts (set "Supported account types" to "Any Azure AD directory").
- Add delegated permissions (e.g.,
Mail.Read,offline_access) and ensure admin consent is granted for your tenant (or let users consent individually if allowed). - Enable the
offline_accesspermission to obtain refresh tokens, which the Web Job can use to get access tokens for the user's tenant over time.
2. Implement User Login in the Web App
Use the Microsoft.Identity.Web library to handle authentication and token acquisition in your ASP.NET Core Web App:
Web App Setup (Program.cs)
var builder = WebApplication.CreateBuilder(args); // Add authentication with Microsoft Identity builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi(new[] { "Mail.Read", "offline_access" }) .AddInMemoryTokenCaches(); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
Retrieve Refresh Token and Trigger Web Job
In your Web App controller, after user login, fetch the refresh token and send a message to Azure Queue Storage to trigger the Web Job asynchronously:
using Azure.Storage.Queues; using Microsoft.Identity.Web; using System.Text.Json; public class HomeController : Controller { private readonly ITokenAcquisition _tokenAcquisition; private readonly IConfiguration _configuration; public HomeController(ITokenAcquisition tokenAcquisition, IConfiguration configuration) { _tokenAcquisition = tokenAcquisition; _configuration = configuration; } public async Task<IActionResult> TriggerWebJob() { // Get refresh token for the logged-in user var refreshToken = await _tokenAcquisition.GetRefreshTokenAsync( new[] { "Mail.Read", "offline_access" }, user: User); // Extract tenant ID from user claims var tenantId = User.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value; // Send job details to Azure Queue var queueClient = new QueueClient( _configuration["AzureStorage:ConnectionString"], "webjob-task-queue"); await queueClient.CreateIfNotExistsAsync(); var jobPayload = new JobPayload { TenantId = tenantId, RefreshToken = refreshToken, Scopes = new[] { "Mail.Read" } }; await queueClient.SendMessageAsync(JsonSerializer.Serialize(jobPayload)); return RedirectToAction("Index"); } } // Helper class for queue message serialization public class JobPayload { public string TenantId { get; set; } public string RefreshToken { get; set; } public string[] Scopes { get; set; } }
Note: Store sensitive values like queue connection strings in Azure Key Vault, not plaintext config files.
3. Modify Web Job Code to Use Delegated Permissions
Update your Web Job to use RefreshTokenCredential (from Azure.Identity) instead of ClientSecretCredential. This credential uses the user's refresh token to authenticate and fetch access tokens for their tenant:
Modified Web Job Code
using Azure.Identity; using Microsoft.Graph; using System.Text.Json; // Queue-triggered Web Job function public static async Task ProcessQueueTask([QueueTrigger("webjob-task-queue")] string message, ILogger logger) { var jobPayload = JsonSerializer.Deserialize<JobPayload>(message); // Fetch app credentials from Web Job config var clientId = Environment.GetEnvironmentVariable("ClientId"); var clientSecret = Environment.GetEnvironmentVariable("ClientSecret"); var credentialOptions = new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud }; // Create credential using user's refresh token var refreshTokenCredential = new RefreshTokenCredential( tenantId: jobPayload.TenantId, clientId: clientId, refreshToken: jobPayload.RefreshToken, clientSecret: clientSecret, options: credentialOptions); var graphClient = new GraphServiceClient(refreshTokenCredential, jobPayload.Scopes); // Example: Fetch user's recent messages var messages = await graphClient.Me.Messages.Request().Take(10).GetAsync(); logger.LogInformation($"Retrieved {messages.Count} messages for the user."); } // Reuse the same JobPayload class from the Web App public class JobPayload { public string TenantId { get; set; } public string RefreshToken { get; set; } public string[] Scopes { get; set; } }
Web Job Configuration
- Store your multi-tenant app's
ClientIdandClientSecretin the Web Job's application settings (Azure Portal > Your Web App > Web Jobs > Configuration). - Ensure the Web Job has permissions to read from the target Azure Queue Storage.
Key Considerations
- Refresh Token Security: Refresh tokens are long-lived, so encrypt them when storing or transmitting. Use Azure Key Vault to store refresh tokens instead of sending them directly in queue messages (send a secret ID reference instead).
- Token Management: The
RefreshTokenCredentialautomatically handles refreshing access tokens using the refresh token, so you don't need to manage token expiry manually. - User Consent: Ensure users have consented to the required delegated permissions (e.g.,
Mail.Read) when logging into the Web App. - Admin Consent Flow: If your app requires permissions that need tenant admin approval, implement a consent flow for admins to grant access across their tenant.
内容的提问来源于stack exchange,提问作者Gabriele

