【问题标题】:Using MSAL on Android to get an access token to call an Azure AD protected API gives MsalDeclinedScopeException在 Android 上使用 MSAL 获取访问令牌以调用受 Azure AD 保护的 API 会产生 MsalDeclinedScopeException
【发布时间】:2021-07-13 13:58:12
【问题描述】:

我正在使用 MSAL 以单帐户模式将用户登录到 Android 应用程序。这行得通。我希望能够使用收到的访问令牌来调用受 Azure AD 保护的 API 端点。我已注册客户端应用程序和 API,并已授予从 API 到客户端应用程序所需范围的访问权限。

这是来自我的build.gradle的库参考

implementation "com.microsoft.identity.client:msal:2.0.8"

我们在 Azure 门户中使用应用注册体验,并且在使用具有 Postman 的 OAuth2 token acquisition 功能的 v2.0 授权端点时,它工作正常。这是我的设置

Grant Type           : Authorization Code (With PKCE)
Callback URL         : "http://localhost"
Authorization URL    : "https://login.microsoftonline.com/{{Tenant}}/oauth2/v2.0/authorize"
Token URL            : "https://login.microsoftonline.com/{{Tenant}}/oauth2/v2.0/token"
Client ID & Client Secret from portal
Code Challenge Method: SHA-256
Scope                : From API Registration

但是,来自 MSAL Android 的令牌没有范围或受众。身份验证失败,这是日志:

Authentication failed: com.microsoft.identity.client.exception.MsalDeclinedScopeException: Some or all requested scopes have been declined by the Server

这种行为似乎与使用旧的 Authorize 端点时会发生的情况相当一致。
无论如何,是否有解决此问题的方法/解决方法,或者我可以做些什么来使其正常工作?

编辑
这是 API 应用注册中暴露 API 刀片的屏幕截图
Expose API from API registration

这是我用来登录用户的 Kotlin 代码

var SCOPES = arrayOf("api://<api-app-id>/<scope-name>","api://<api-app-id>/scope-name>")
PublicClientApplication.createSingleAccountPublicClientApplication(
  applicationContext,
  R.raw.auth_config_single_account, object : ISingleAccountApplicationCreatedListener {
  override fun onCreated(application: ISingleAccountPublicClientApplication) {
    mSingleAccountApp = application
    loadAccount()  //processes the logged in account
  }
  override fun onError(exception: MsalException) {
  }
})
loginButton.setOnClickListener(View.OnClickListener {
    if (mSingleAccountApp == null) {
        return@OnClickListener
    }
    getAuthInteractiveCallback()?.let { it1 ->
        mSingleAccountApp!!.signIn(
            this@MainActivity,
            null,
            SCOPES,
            it1
        )
    }
})

【问题讨论】:

  • 请分享您在 MSAL 中所做的事情。
  • @PamelaPeng Here's 我指的是这样做。我没有使用图形范围,而是使用自定义范围。我授予我的客户端应用注册权限以使用受保护的 API。我还将客户端应用添加到 API 注册的受信任应用中。

标签: android azure kotlin azure-active-directory msal


【解决方案1】:

不确定您提到的自定义范围。您需要像这样使用api://&lt;client-id&gt;/xxx 更改范围。

请尝试以下代码。

通过 MSAL 获取令牌:

String[] SCOPES = {"api://<client-id>/.default"};
PublicClientApplication sampleApp = new PublicClientApplication(
                    this.getApplicationContext(),
                    R.raw.auth_config);

// Check if there are any accounts we can sign in silently.
// Result is in the silent callback (success or error).
sampleApp.getAccounts(new PublicClientApplication.AccountsLoadedCallback() {
    @Override
    public void onAccountsLoaded(final List<IAccount> accounts) {

        if (accounts.isEmpty() && accounts.size() == 1) {
            // TODO: Create a silent callback to catch successful or failed request.
            sampleApp.acquireTokenSilentAsync(SCOPES, accounts.get(0), getAuthSilentCallback());
        } else {
            /* No accounts or > 1 account. */
        }
    }
});

[...]

// No accounts found. Interactively request a token.
// TODO: Create an interactive callback to catch successful or failed requests.
sampleApp.acquireToken(getActivity(), SCOPES, getAuthInteractiveCallback());

使用访问令牌调用 API:

    RequestQueue queue = Volley.newRequestQueue(this);
    JSONObject parameters = new JSONObject();

    try {
        parameters.put("key", "value");
    } catch (Exception e) {
        // Error when constructing.
    }
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, MSGRAPH_URL,
            parameters,new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            // Successfully called Graph. Process data and send to UI.
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // Error.
        }
    }) {
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String, String> headers = new HashMap<>();
            
            // Put access token in HTTP request.
            headers.put("Authorization", "Bearer " + accessToken);
            return headers;
        }
    };

    request.setRetryPolicy(new DefaultRetryPolicy(
            3000,
            DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    queue.add(request);

【讨论】:

  • 如果我的回复有帮助,请采纳为答案(点击回复旁边的标记选项将其从灰色切换为填写。),请参阅meta.stackexchange.com/questions/5234/…
  • 谢谢,但我使用的范围与在我的 API 应用程序的公开 API 屏幕中完全一样。他们确实遵循这种模式。我将编辑问题并添加屏幕截图。此外,当我使用 Postman 的 v2.0 端点时,我能够获得正确的令牌。
  • 公开 API 后,您需要为客户端应用的注册添加权限,并在必要时授予管理员同意。图:i.stack.imgur.com/2t1v2.png
  • 是的。它已被添加并授予。我本来打算在我的问题本身中这么说,但我想我没有正确地说出来。问题是我可以使用 OAuth2 like this 从客户端应用注册中正确获取令牌,但在 Android MSAL 中它不起作用
猜你喜欢
  • 1970-01-01
  • 2019-06-19
  • 1970-01-01
  • 2021-12-28
  • 2019-11-03
  • 1970-01-01
  • 2020-03-10
  • 2018-08-24
  • 2021-03-19
相关资源
最近更新 更多