如何授权来自不同 AAD 租户的特定客户端守护程序?
Posted
技术标签:
【中文标题】如何授权来自不同 AAD 租户的特定客户端守护程序?【英文标题】:How to authorize a specific client daemon from a different AAD tenant? 【发布时间】:2021-06-25 16:34:01 【问题描述】:在我的服务器到服务器方案中,我在(Azure Active Directory)租户 A(我控制)中有一个 ASP.NET Core 服务,我想允许在租户 B 中注册特定的(守护程序)客户端应用程序(我无法控制)访问我的服务。
我发现了一些通过基于角色的访问控制进行多租户授权的示例,但我不想使用角色,因为我不了解允许租户 B 中的管理员决定谁获得与我的应用程序交谈的适当“角色”。相反,我想自己决定,访问控制列表似乎是一个很好的方法。
理论上,我认为我可以使用 B 颁发的 JWT 不记名令牌,并在 A 中使用它们,但我无法使其正常工作:根据我的尝试,服务器上的身份验证失败,类似 401
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer '(null)' is invalid"
和
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
即使我的不记名令牌似乎在 jwt.io 中验证良好(“签名验证”,“iss”:“https://sts.windows.net/4510468d-3790-4a1a-8209-84281b2d1596/”)。
我的代码在 git repo 中,这是我的启动:
type Startup(configuration: IConfiguration) =
member _.Configuration = configuration
// This method gets called by the runtime. Use this method to add services to the container.
// see https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-web-api for more
member _.ConfigureServices(services: IServiceCollection) =
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(fun o ->
configuration.Bind(o)
o.TokenValidationParameters <- new Microsoft.IdentityModel.Tokens.TokenValidationParameters(ValidateAudience=true)
)
.Services.AddControllers() |> ignore
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
member _.Configure(app: IApplicationBuilder, env: IWebHostEnvironment) =
if (env.IsDevelopment()) then
app.UseDeveloperExceptionPage() |> ignore
app.UseHttpsRedirection()
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseEndpoints(fun endpoints ->
endpoints.MapControllers() |> ignore
) |> ignore
appsettings.json:
"Logging":
"LogLevel":
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,
"AllowedHosts": "*",
"AzureAd":
"Instance": "https://login.microsoftonline.com/",
"Domain": "wilsonsoft.onmicrosoft.com",
"ClientId": "88ac0449-3fae-4113-a1ba-fb4f2d041702",
"TenantId": "c4568757-6752-4ed0-a24a-b5ab2df02011"
一个典型的请求:
GET https://localhost:44336/weatherforecast/who HTTP/1.1
Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiJodHRwczovL3dpbHNvbnNvZnQub25taWNyb3NvZnQuY29tL0hlbGxvV2VhdGhlciIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzQ1MTA0NjhkLTM3OTAtNGExYS04MjA5LTg0MjgxYjJkMTU5Ni8iLCJpYXQiOjE2MTcwMzI5NDAsIm5iZiI6MTYxNzAzMjk0MCwiZXhwIjoxNjE3MDM2ODQwLCJhaW8iOiJFMlpnWUNqYlpMKzk5TGErZWQ5blYrUEhsUmI5QUE9PSIsImFwcGlkIjoiNDE1OTA3NjMtZWM3YS00Mzc2LTkwNmYtY2VlMTM4NzExMzg0IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNDUxMDQ2OGQtMzc5MC00YTFhLTgyMDktODQyODFiMmQxNTk2LyIsIm9pZCI6IjVkOGQ0ZTZkLTQ4MzQtNDA3Ny1hZGIyLTQ5ODNjYWQxMGY4ZiIsInJoIjoiMC5BUTBBalVZUVJaQTNHa3FDQ1lRb0d5MFZsbU1IV1VGNjdIWkRrR19PNFRoeEU0UU5BQUEuIiwic3ViIjoiNWQ4ZDRlNmQtNDgzNC00MDc3LWFkYjItNDk4M2NhZDEwZjhmIiwidGlkIjoiNDUxMDQ2OGQtMzc5MC00YTFhLTgyMDktODQyODFiMmQxNTk2IiwidXRpIjoiYWE4MDEtX2d1a21PQUpBcGFQOThBQSIsInZlciI6IjEuMCJ9.DwpWaOqoZgNoDka6-0FYQr1ivllL2taXdqtat_65x_kuT6r3uiknhL19Fu6dFmJ7UCgjc3-JZh5Bee0uZvVHbwCjZKHsUNrDEANnDkK4hGFzSKyU3NL7X9iRdPeBl3-GUSRGbPeozHTF93epSEhDyY3PkS1ICEfdAG7yi8cerBzmuy-lsUWs90sWlrWVYjDRtFWzwlovNgS6mPkx2cKlsC34WK6QXafJYqPcA5XW1EqZGyA5S0qvQS0VaheABEfkTIC8pEijieImWKIqFefd2G7blBB1Qdng4NAPcHOhmnRSiClCrwXS_5hOYsUXFS4xVAMPIZx9peWXMlk6XyQzNg
Host: localhost:44336
回复:
HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Microsoft-IIS/10.0
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
X-Powered-By: ASP.NET
Date: Mon, 29 Mar 2021 16:06:48 GMT
0
谁能建议这个错误消息试图告诉我什么和/或是否有更好的方法来实现我在 ASP.NET Core 中授权不同租户中的特定客户端的目标?
【问题讨论】:
【参考方案1】:答案:在多租户场景中,您必须针对“通用”端点进行身份验证,并且还必须使用自定义 TokenValidationParameters.IssuerValidator 在检查“iss”声明之前对其进行转换。这将允许您使用 jwt 令牌进行身份验证。详情请见https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-aspnet-core-web-api。
那么,授权就是检查应用 ID 声明的简单问题。
// This method gets called by the runtime. Use this method to add services to the container.
member _.ConfigureServices(services: IServiceCollection) =
IdentityModelEventSource.ShowPII <- true
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(fun jwtOptions ->
jwtOptions.TokenValidationParameters <-
new Microsoft.IdentityModel.Tokens.TokenValidationParameters(
ValidateAudience = true,
ValidateIssuer = true
)
jwtOptions.Authority <- "https://login.microsoftonline.com/common"
jwtOptions.Audience <- "https://wilsonsoft.onmicrosoft.com/HelloWeather"
jwtOptions.TokenValidationParameters.IssuerValidator <-
fun issuer jwt tokenValidationParams ->
// In order to support multi-tenant auth, replace tenantid placeholder with actual tenantId,
// e.g. "https://sts.windows.net/tenantid/" becomes "https://sts.windows.net/c4568757-6752-4ed0-a24a-b5ab2df02011/"
// See https://thomaslevesque.com/2018/12/24/multitenant-azure-ad-issuer-validation-in-asp-net-core/ for more
let validatedIssuer =
match jwt with
| :? JwtSecurityToken as jwt ->
match jwt.Payload.TryGetValue("tid") with
| true, (:? string as tenantId) ->
let validIssuers =
(Seq.append tokenValidationParams.ValidIssuers [tokenValidationParams.ValidIssuer])
|> Seq.filter (System.String.IsNullOrEmpty >> not)
|> Seq.map (fun i -> i.Replace("tenantid", tenantId))
validIssuers |> Seq.tryFind (fun i -> i = issuer)
| _ -> None
| _ -> None
match validatedIssuer with
| Some i -> i
| None ->
$"IDX10205: Issuer validation failed. Issuer: 'issuer'. Did not match: validationParameters.ValidIssuer: 'tokenValidationParams.ValidIssuer' or validationParameters.ValidIssuers: 'tokenValidationParams.ValidIssuers'."
|> SecurityTokenInvalidIssuerException
|> raise
)
.Services.AddAuthorization(fun authzOptions ->
// only allow specific appIds (from trusted issuers) access to this app
authzOptions.AddPolicy(
"ACL",
(fun policy ->
policy.RequireAssertion(fun ctx ->
ctx.User.HasClaim(fun claim ->
claim.Type = "appid" && claim.Value = "083d3ba2-ed4e-4e11-b7ef-d8cc46ffe346")
)
|> ignore
))
)
.AddControllers() |> ignore
Fixed version.
【讨论】:
以上是关于如何授权来自不同 AAD 租户的特定客户端守护程序?的主要内容,如果未能解决你的问题,请参考以下文章
AAD B2C 中具有客户端凭据授予流的 Azure 应用服务轻松身份验证