jQuery使用AAD到达.Net Web Api会抛出401 invalid_token /发行者无效

我正在尝试在Azure中创建一个简单的.Net Core Web Api以使用JQuery测试身份验证。我设法解决了CORS问题,但是在尝试使用不记名令牌时,我一直收到401“发行者无效”错误。我可以使用邮递员和一个秘密来测试Web Api,但在使用JQuery和AAD时却无法。我从一个单独工作的示例中提取了一些演示SPA代码,但当我将客户端和Web Api保留在单独的项目中时却没有。我以为可能需要我使用Web Api的客户端ID来获取令牌,但这似乎没有任何效果。控制器再也不是最基本的了。

namespace test_core_web_api_spa.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public actionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1","value2" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public actionResult<string> Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody] string value)
        {
            // For more information on protecting this API from Cross Site Request Forgery (CSRF) attacks,see https://go.microsoft.com/fwlink/?LinkID=717803
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        public void Put(int id,[FromBody] string value)
        {
            // For more information on protecting this API from Cross Site Request Forgery (CSRF) attacks,see https://go.microsoft.com/fwlink/?LinkID=717803
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
            // For more information on protecting this API from Cross Site Request Forgery (CSRF) attacks,see https://go.microsoft.com/fwlink/?LinkID=717803
        }
    }
}

Web Api的启动是基本的。

namespace test_core_web_api_spa
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });
            services.AddAuthentication(AzureADDefaults.BearerauthenticationScheme)
                .AddAzureADBearer(options => Configuration.Bind("AzureAd",options));
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app,IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios,see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseCors(options => options.WithOrigins("https://localhost:44399","https://localhost:44308").AllowAnyMethod().AllowAnyHeader());
            app.UseMvc();
        }
    }
}

使用AAD从SPA演示复制了HTML页面。

<!DOCTYPE html>
<html>
<head>
    <title>Test API Call</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
    <link rel="stylesheet" href="/css/app.css">
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed"
                        data-toggle="collapse"
                        data-target=".navbar-collapse">

                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/#Home">test api call</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/#Home">Home</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <li class="app-user navbar-text"></li>
                    <li><a href="javascript:;" class="app-logout">Logout</a></li>
                    <li><a href="javascript:;" class="app-login">Login</a></li>
                </ul>
            </div>
        </div>
    </div>

    <div id="divHome" class="container-fluid">
        <div class="jumbotron">
            <h5 id="WelcomeMessage"></h5>
            <div class="text-hide">Surname: <span id="userSurName"></span><span id="userEmail"></span></div>
            <h2>test page</h2>
        </div>

        <div>
            <br />
            <a href="javascript:;" class="btnCallApiTest">Call Api</a>
            <br />
            <p class="view-loading">Loading...</p>
            <div class="app-error"></div>
            <br />
            <span id="data-container"></span>
        </div>
        <br />
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.4/bluebird.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.1.3/js/msal.js" integrity="sha384-m/3NDUcz4krpIIiHgpeO0O8uxSghb+lfBTngquAo2Zuy2fEF+YgFeP08PWFo5FiJ" crossorigin="anonymous"></script>
    <script src="js/rest_api.js"></script>

</body>
</html>

Javascript也从GitHub上的SPA AAD演示中借用。

// the AAD application
var clientApplication;

(function () {
    console.log("document ready done");
    window.config = {
        clientID: 'clientidof_web_api_in_azure'
    };

    const loginRequest = {
        scopes: ["openid","profile","User.Read"]
    };
    const tokenRequest2 = {
        scopes: ["https://myPortal.onmicrosoft.com/test_core_web_api_spa"]
    };
    var scope = [window.config.clientID];
    const msalConfigDemo = {
            auth: {
                clientId: "myclientid",authority: "https://login.microsoftonline.com/mytenantid",consentScopes: ["user.read","https://myportal.onmicrosoft.com/test_core_web_api_spa/user_impersonation"],validateAuthority: true
            },cache: {
                cacheLocation: "localStorage",storeAuthStateInCookie: false
            }
    };
    function authCallback(errorDesc,token,error,tokenType) {
        //This function is called after loginRedirect and acquireTokenRedirect. Not called with loginPopup
        // msal object is bound to the window object after the constructor is called.
        if (token) {
            log("authCallback success");
            console.log({ 'token': token });
            console.log({ 'tokenType': tokenType });
        }
        else {
            log(error + ":" + errorDesc);
        }
    }

    if (!clientApplication) {
        clientApplication = new clientApplication = new Msal.UserAgentApplication(msalConfigDemo,msalConfigDemo,authCallback);
    } else {
        console.log({ 'clientApplication': clientApplication });
    }

    // Get UI jQuery Objects
    var $panel = $(".panel-body");
    var $userDisplay = $(".app-user");
    var $signInButton = $(".app-login");
    var $signOutButton = $(".app-logout");
    var $errorMessage = $(".app-error");
    var $btnCallApiTest = $(".btnCallApiTest");
    onSignin(null);


    // Handle Navigation Directly to View
    window.onhashchange = function () {
        loadView(stripHash(window.location.hash));
    };
    window.onload = function () {
        $(window).trigger("hashchange");
    };

    $btnCallApiTest.click(function () {
        call_api_test();
    });

    // Register NavBar Click Handlers
    $signOutButton.click(function () {
        clientApplication.logout();
    });

    $signInButton.click(function () {
        clientApplication.loginPopup(loginRequest).then(onSignin);
    });


    function stripHash(view) {
        return view.substr(view.indexOf('#') + 1);
    }

    function call_api_test() {
        // Empty Old View Contents
        var $dataContainer = $(".data-container");
        $dataContainer.empty();
        var $loading = $(".view-loading");

        clientApplication.acquireTokenSilent(tokenRequest2)
            .then(function (token) {
                getTodoList(token.accessToken,$dataContainer,$loading);
            },function (error) {
                clientApplication.acquireTokenPopup(tokenRequest2).then(function (token) {
                    getTodoList(token.accessToken,$loading);
                },function (error) {
                    printErrorMessage(error);
                });
            });
    }

    function getTodoList(accessToken,dataContainer,loading) {
        // Get TodoList Data
        let urlstring = 'https://localhost:44363/api/values';
        console.log({ 'accessToken': accessToken });
        $.ajax({
            type: "GET",url: urlstring,headers: {
                'Authorization': 'Bearer ' + accessToken,},}).done(function (data) {
            // Update the UI
            console.log({ 'data': data });
            loading.hide();
            dataContainer.html(data);
        }).fail(function (jqXHR,textStatus) {
            printErrorMessage('Error getting todo list data statusText->' + textStatus + ' status->' + jqXHR.status);
            console.log({ 'jqXHR': jqXHR });
            loading.hide();
        }).always(function () {
            // Register Handlers for Buttons in Data Table
            //registerDataClickHandlers();
        });
    }

    function printErrorMessage(mes) {
        var $errorMessage = $(".app-error");
        $errorMessage.html(mes);
    }
    function onSignin(idToken) {
        // Check Login Status,Update UI
        var user = clientApplication.getUser();
        if (user) {
            $userDisplay.html(user.name);
            $userDisplay.show();
            $signInButton.hide();
            $signOutButton.show();
        } else {
            $userDisplay.empty();
            $userDisplay.hide();
            $signInButton.show();
            $signOutButton.hide();
        }

    }
}());

登录确实适用于AAD,并撤回我的电子邮件地址。我确实看到了创建的令牌并将其传递给Web Api。但是随后它给出了“发出者无效” 401错误。发出令牌请求时,我是Web Api的客户端ID,因此我不确定还有什么可以更改的。

对于每个注释,我都试图将范围传递给loginPopup调用。

    $signInButton.click(function () {
        clientApplication.loginPopup(requestObj).then(onSignin);
    });

但是,唯一起作用的值会给出相同的结果:

var requestObj = ["web-api-client-id"];

我已经尝试运行本地Web服务的URL,包括使用https://localhost:44399/.default进行组合,但是在获取带有the resource principal named https://localhost:44399 was not found in the tenant之类的消息的令牌之前,它会立即引发错误。如果问题是此调用中的作用域设置,那么我不确定在调试时使用什么值使它在本地工作。附带说明一下,我发现其他使用

格式的Github示例
var requestObj = {scopes: ["api://clientid/access_as_user"]};

但这些命令无法执行,说API does not accept non-array scopes。我可能会在一个单独的线程中问这个问题。

11月13日更新 我从0.2.3的MSAL切换到1.1.3,然后更新了逻辑以反映不同版本中所做的更改。

我还确认了客户端应用具有对Web api的API权限。我在Web api中添加了一个名为“ user_impersonation”的新范围。现有的“ api-access”已锁定为我的租户中的管理员控制。

尝试使用形式“ api //”时,找不到资源。这是我尝试过的所有值都得到相同错误的值。我认为这种格式是传统格式。


scopes: ["api://web-api-clientid"]
scopes: ["api://web-api-clientid/api-access"]
scopes: ["api://web-api-clientid/user_impersonation"]
scopes: ["api://web-api-clientid/.default"]
ServerError: AADSTS500011: The resource principal named api://web-api-clientid was not found in the tenant named my-tenant-id. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.

尝试使用这些示波器时,错误为401观众无效。

scopes: ["https://myPortal.onmicrosoft.com/test_core_web_api_spa/user_impersonation"]
scopes: ["https://myPortal.onmicrosoft.com/test_core_web_api_spa/.default"]
401 www-authenticate: Bearer error="invalid_token",error_description="The audience is invalid" 
"aud": "https://myPortal.onmicrosoft.com/test_core_web_api_spa"

尝试这些作用域时,错误消息使我的客户端应用正确无误,但似乎又不认为我的门户网站上存在我的Web api应用。

scopes: ["https://myPortal.onmicrosoft.com/test_core_web_api_spa"]
scopes: ["https://myPortal.onmicrosoft.com/test_core_web_api_spa","user_impersonation"]
ServerError: AADSTS650053: The application 'demoapp-frontend' asked for scope 'test_core_web_api_spa' that doesn't exist on the resource 'myportal_guid'. 

对于所有的混乱,我们深表歉意,但是我一直在尝试一切,并且代码变得混乱。我快要重新开始了。

snowboyz 回答:jQuery使用AAD到达.Net Web Api会抛出401 invalid_token /发行者无效

按如下所示修改代码

window.config = {
        clientID: 'clientidof_web_client'
    };
    var scope = ["api://{clientidof_web_api}/.default"];

您可以通过decoding来检查令牌。 aud的值应为api://client_id_of_web_api

enter image description here

更新

您是否已将api添加到客户端应用权限?

enter image description here

,

问题是Web API的配置数据。当他们说ClientId时,他们真正想要的是“公开API”选项下的值,其中显示“应用程序ID URI”。我输入的是Web Api应用程序注册的向导。下面是它的外观。

  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/","Domain": "myportal.onmicrosoft.com","TenantId": "mytenant-guid","ClientId": "https://myportal.onmicrosoft.com/test_core_web_api_spa"
  },
本文链接:https://www.f2er.com/3141267.html

大家都在问