Azure: 无法使用RefreshToken获取新的AccessToken

17
我正在开发一个需要访问我们客户的Office 365管理活动的应用程序。 我已按照Azure Active Directory概述中概述的步骤操作,并能够使用OAuth代码获取初始访问令牌,以及使用此令牌设置O365订阅。但是,当我使用初始令牌提供的refresh_token来获取新的访问令牌时,我会收到以下错误:
{"error_description":"AADSTS65001: 用户或管理员未同意使用 ID 为 '8f72f805-dfd2-428d-8b0e-771a98d26c16' 的应用程序。向此用户和资源发送交互式授权请求。\r\n跟踪 ID: df229c3f-8f28-420b-9ac3-321ab1b2ad09\r\n相关 ID: 0e0f2bcb-4b19-458a-8556-2a6d4e51379f\r\n时间戳: 2016-10-03 17:33:20Z","error":"invalid_grant"}

由于我能够获取并使用初始访问令牌,所以我非常确定用户授予了我的应用程序某些权限。是否有特定的权限需要我才能使用刷新令牌获取新的访问令牌?

编辑: 具体地,我正在使用com.microsoft.azure::adal4jJava包,AuthenticationContext类,acquireTokenByAuthorizationCodeacquireTokenByRefreshToken方法:

public class AzureProvisioner {
    private final AuthenticationContext authService = new AuthenticationContext(
            "https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
    private final ClientCredential clientCredential = new ClientCredential("azureAppId", "azureAppSecret");
    public static final String resource = "https://manage.office.com";
    // Internal implementation of REST interface; Microsoft didn't provide a Java Library
    final Office365ManagementApi managementApi;

    public void acquireToken(final String authCode, final URI redirectUri) {
        final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
                authCode, redirectUri, clientCredential, resource, null).get()
        // internal library code, gets the "tid" field from parsing the JWT token
        final String tenantId = JwtAccessToken.fromToken(authResult.getAccessToken()).getTid();

        // works
        createInitialSubscription(customerId, authResult.getAccessToken(), tenantId);

        // throws an error
        final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
                authResult.getRefreshToken(), clientCredential, null).get();
    }

    private void createInitialSubscription(final String accessToken, final String tenantId) {
        final String authHeader = "Authorization: Bearer " + accessToken;
        final String contentType = "Audit.AzureActiveDirectory";
        // internal implementation
        final CreateWebhookRequest requestBody = new CreateWebhookRequest();
        managementApi.createSubscription(authHeader, tenantId, contentType, requestBody);
    }
}

同样的代码,没有任何外部依赖,对我也不起作用:
public class AzureProvisioner {
    private final AuthenticationContext authService = new AuthenticationContext(
            "https://login.windows.net/common/oauth2/token", true, Executors.newSingleThreadExecutor());
    private final ClientCredential clientCredential = new ClientCredential("8f72f805-dfd2-428d-8b0e-771a98d26c16", "secret");
    public final String resource = "https://manage.office.com";
    private URI redirectUri = new URI("https://localhost");

    private static final String oAuthUrl = "https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16&resource=https%3A%2F%2Fmanage.office.com&redirect_uri=https%3A%2F%2Flocalhost";

    public AzureProvisioner() throws Exception {
        // do nothing
    }

    public static void main(String... args) throws Exception {
        final String authCode = "AQABAAAAAADRNYRQ3dhRSrm...";
        new AzureProvisioner().acquireToken(authCode);
    }

    public void acquireToken(final String authCode) throws Exception {
        final AuthenticationResult authResult = authService.acquireTokenByAuthorizationCode(
                authCode, redirectUri, clientCredential, resource, null).get();
        System.out.println(authResult.getAccessToken());

        // throws an error
        final AuthenticationResult refreshResult = authService.acquireTokenByRefreshToken(
                authResult.getRefreshToken(), clientCredential, resource, null).get();
        System.out.println(refreshResult.getAccessToken());
    }
}

使用代理,我对https刷新请求进行了跟踪:

Method: POST
Protocol-Version: HTTP/1.1
Protocol: https
Host: login.windows.net
File: /common/oauth2/token
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 876

refresh_token={token}
&resource=https%3A%2F%2Fmanage.office.com
&grant_type=refresh_token
&scope=openid
&client_secret={secret}
&client_id=8f72f805-dfd2-428d-8b0e-771a98d26c16

你能展示一下你是如何获取初始访问令牌和刷新令牌的,以及你如何尝试使用刷新令牌吗? - Philippe Signoret
你确定你尝试了那段完全相同的代码吗?你有几个未处理的异常(在初始化authServiceacquireToken时),还有一个分号丢失。 - Philippe Signoret
现在进入真正的问题。对我来说,你会在“/token”端点收到这样的错误,但在“/authorize”端点没有收到,这很奇怪。而且你的应用程序似乎正在请求正确的权限。你能否发布完整的错误消息,包括未编辑的错误描述、时间戳和关联 ID? - dstrockis
1
@dstrokis 在UserInfo中没有tid或类似的内容。@AndrewRueckert如果你想的话,可以查看ID令牌(authResult.getIdToken())。 - Philippe Signoret
我尝试过注册一个新的应用程序和创建一个新的Office365试用帐户。:( 我想,我没有应用到我的帐户或应用程序的某些配置,但我无法弄清楚会导致这个特定错误的原因。 - Andrew Rueckert
显示剩余10条评论
2个回答

2
原来问题出在我的应用程序权限上。在“我的应用程序>设置>所需权限>Office 365管理API”下,我选择了“应用程序权限”,而实际上需要选择“委派权限”。将这些权限互换后,我的代码立即按预期开始工作。
请看下图:wrong!

0

那似乎是一个用于客户端应用程序的C#库。我正在编写Java代码,用于多租户基于服务器的应用程序。 - Andrew Rueckert
这里的重点是解释使用刷新令牌过程是自动和透明的,无论您使用哪种语言,但在幕后,您正在使用AAD。如果您创建新令牌,则该令牌将无法验证,因为存在未过期的令牌。 - Mitin Dixit
2
@MitinDixit:不,这不是真的。在ADAL for .NET中包含的令牌缓存并未在ADAL for Java中实现。 - Philippe Signoret
@AndrewRueckert 这个问题看起来很类似 https://dev59.com/UlsW5IYBdhLWcg3wm4UK - Brent Schmaltz
我看了一下,但不幸的是它稍有不同。那个用户无法获得初始令牌,因为他们没有以管理员用户身份登录。我已经以管理员用户身份登录,并能够获得初始令牌;然而,我无法刷新它。 - Andrew Rueckert
你好,你能找到解决方案了吗? - imal hasaranga perera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接