“AADSTS50027:无效的JWT令牌。令牌格式无效”代表场景中的错误

时间:2018-03-12 06:27:08

标签: oauth-2.0 asp.net-web-api2 jwt access-token adal

尝试使用AcquireTokenAsync代表AAD用户获取原生应用的令牌时,会出现上述ADAL错误。

方案: Angular单页面应用程序正在使用adal.js库来获取用户的访问令牌。 此令牌传递给自托管的Web api,它本质上是相同的应用程序(通过adal.js初始登录和自托管的Web api都使用相同的Azure App Id)。 webapi尝试代表用户获取新令牌,以使用AcquireTokenAsync调用另一个Azure托管的Web api。

代码:

var bootstrapContext =
     ClaimsPrincipal.Current.Identities.First().BootstrapContext as
          System.IdentityModel.Tokens.BootstrapContext;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var userAccessToken = bootstrapContext.Token;
var userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
var authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, _aadInstance, _tenant);

var authContext = new AuthenticationContext(authority, new TokenCache());

var result = await authContext.AcquireTokenAsync(_resourceUri, _appId, userAssertion);
var accessToken = result.AccessToken;
return accessToken;

请注意,Azure中的应用注册是一个没有应用密钥的本机应用。如果我将应用程序创建为webapi应用程序并使用ClientCredential对象中的appid和key,则会按预期返回该令牌。

var clientCredential = new ClientCredential(_appId, _appKey);
...
var result = await authContext.AcquireTokenAsync(_resourceUri, clientCredential, userAssertion);

我无法使用本机应用程序。有什么建议?或者为什么我收到无效令牌错误的任何想法?

原理: 我需要使用本机应用程序而不是webapi应用程序注册,客户端(angluar SPA应用程序+自托管webapi)部署在站点而不是Web服务器上。我不想使用应用程序密钥部署应用程序,因为应用程序密钥到期并在到期时使用新密钥重新部署客户端应用程序不是一个很好的选择。使用没有密钥的本机应用程序将避免这样做。

感谢。

3 个答案:

答案 0 :(得分:1)

当使用代表授权类型调用令牌端点时,documentation似乎建议您需要拥有机密客户端,因为client_secret参数已根据需要进行了记录。这意味着您不能将该授权类型与本机(公共)客户端类型一起使用。

但是,如果您是通过自托管的Web API进行呼叫并且它是一个机密客户端,那么on-behalf-of应该可以正常工作。

  

是否可以绕过第一个API的AcquireTokenAsync调用   获取新令牌以调用第二个API?即在初次登录时   从adal.js,用户被授予一个令牌来调用下游API?

是的我认为你可以通过manifest configuration来做到这一点。如果您修改了AAD应用程序权限,并根据需要在相关应用程序之间添加了委派权限(requiredResourceAccess)。初始令牌请求需要用于下游Web API的资源ID,并且此资源ID将在每个资源中验证为audience

此外,我相信您可以在您的网络API的清单中设置KnownClientApplications,以包含您的Angular应用注册的应用ID,这样,一旦用户同意您的原生应用,将自动为网络API创建同意无需明确同意每个API。

虽然它确实不是一个好的解决方案!

此外,正如您提出的问题所说,您所获得的错误与JWT令牌格式无效有关,原始访问令牌是否肯定以BootstrapContext传递?

在owin中间件中,我似乎记得默认情况下访问令牌没有传递给声明主体,你必须设置

SaveSignInToken = true

作为中间件中TokenValidationParameters的一部分。

e.g。

 app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                {
                    Tenant = ConfigurationManager.AppSettings["ida:Tenant"],


TokenValidationParameters = new TokenValidationParameters{ SaveSigninToken = true, ValidAudience= ConfigurationManager.AppSettings["ida:Audience"] }
                });

代码示例here

答案 1 :(得分:1)

如果我完全删除客户端应用程序ID,我可以使其工作,但我不确定这是否是正确的解决方案。因此,如果adal.js检索的初始令牌用于下游API,并且自托管的Web api受众也是下游API应用程序ID,它只是将JWT令牌直接传递给下游API,并且不会获得一个新的令牌。这可以实现,并且可以实现不在客户端上存储任何应用程序密钥的预期结果,但它感觉不对,感觉就像欺骗。

答案 2 :(得分:1)

找到此内容的Google搜索的通用解决方案。

您可能需要在查询中使用 ClientCredential 类而不是 ClientAssertion 类-否则,将获得正确的AcquireToken重载

这个问题的真实答案(非常有帮助和周到)可以帮助您解决这个问题。