多租户场景下基于MSAL实现授权码流及Graph API调用方案咨询
多租户场景下授权码流的实现方案
1. Azure AD应用注册配置
- 注册一个支持SPA和Web API的多租户应用:在Azure AD中创建应用,将“支持的账户类型”设为“任何组织目录中的账户和个人Microsoft账户”。
- 配置SPA参数:添加Angular前端的重定向URI,启用授权码流(保留PKCE,这是SPA的安全要求,MSAL Angular默认支持)。
- 配置Web API参数:添加客户端密钥(作为机密客户端凭证),设置API权限为Graph API的委托权限(如
User.Read、Mail.Read),并完成管理员同意(多租户场景下需全局管理员操作)。 - 记录应用的客户端ID、客户端密钥、授权机构地址(
https://login.microsoftonline.com/common/)。
2. Angular前端获取授权码并传递给Web API
- 使用MSAL Angular库初始化,仅配置授权码获取逻辑,禁用自动令牌交换:
MsalModule.forRoot(new PublicClientApplication({ auth: { clientId: '你的SPA客户端ID', authority: 'https://login.microsoftonline.com/common/', redirectUri: 'Angular前端地址', }, cache: { cacheLocation: BrowserCacheLocation.LocalStorage, storeAuthStateInCookie: false, } }), { interactionType: InteractionType.Redirect, authRequest: { responseType: 'code', // 仅获取授权码 scopes: ['User.Read', 'Mail.Read'] } }) - 用户登录后,通过MSAL的回调或路由参数捕获授权码,再通过POST请求将授权码发送至Web API的指定接口。
3. .NET 6 Web API交换授权码并存储令牌
- 安装
Microsoft.Identity.Client和Microsoft.GraphNuGet包。 - 编写接口接收授权码,使用MSAL.NET交换获取访问令牌、刷新令牌:
[HttpPost("exchange-code")] public async Task<IActionResult> ExchangeAuthorizationCode([FromBody] string code) { var confidentialClient = ConfidentialClientApplicationBuilder .Create("Web API客户端ID") .WithClientSecret("Web API客户端密钥") .WithAuthority("https://login.microsoftonline.com/common/") .WithRedirectUri("Web API的重定向URI(需与Azure AD配置完全一致)") .Build(); var result = await confidentialClient.AcquireTokenByAuthorizationCode( new[] { "User.Read", "Mail.Read" }, code ).ExecuteAsync(); // 将令牌(AccessToken、RefreshToken)、过期时间、关联用户ID存入SQL数据库 // 注意:令牌需加密存储,避免明文泄露 return Ok(new { Success = true }); }
4. .NET 6 Windows服务调用Graph API并自动刷新令牌
- 安装
Microsoft.Identity.Client和Microsoft.GraphNuGet包。 - 从SQL数据库读取用户的刷新令牌,使用MSAL.NET刷新获取新令牌:
private async Task<string> GetValidAccessToken(string refreshToken) { var confidentialClient = ConfidentialClientApplicationBuilder .Create("Web API客户端ID") .WithClientSecret("Web API客户端密钥") .WithAuthority("https://login.microsoftonline.com/common/") .Build(); var result = await confidentialClient.AcquireTokenByRefreshToken( new[] { "User.Read", "Mail.Read" }, refreshToken ).ExecuteAsync(); // 更新数据库中的新AccessToken、RefreshToken和过期时间 return result.AccessToken; } - 调用Graph API逻辑:
private async Task CallGraphApi(string accessToken) { var graphClient = new GraphServiceClient( new DelegateAuthenticationProvider(req => { req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); return Task.CompletedTask; }) ); var userProfile = await graphClient.Me.Request().GetAsync(); // 处理Graph API返回结果 } - 自动刷新机制:
- 定时检查数据库中令牌的过期时间,提前5分钟触发刷新;
- 调用Graph API时捕获
ServiceException,判断是否为令牌过期错误,若则刷新令牌后重试请求。
关键注意事项
- 权限一致性:前端、Web API、Windows服务使用的权限范围必须完全一致,否则令牌无法用于Graph API调用。
- 安全存储:客户端密钥、数据库中的令牌需加密存储,避免泄露;Windows服务使用AD身份验证访问SQL数据库。
- 多租户适配:授权机构统一使用
common,确保任何租户用户都能完成授权流程。 - 版本兼容:使用最新版的MSAL.NET和MSAL Angular,避免出现兼容性问题。
内容的提问来源于stack exchange,提问作者Ishika Jain
相关产品推荐
相关产品推荐

