如何通过单次登录提示获取SharePoint API与Graph API双令牌(Kotlin)
核心结论
可以通过单次用户登录授权同时获取访问两个API的权限,但单次令牌请求只能返回一个资源的访问令牌。你遇到的「不存在的作用域」错误,本质是混合了不同资源URI的作用域格式,导致Azure AD无法识别。
错误原因分析
Graph API和SharePoint API的作用域属于不同的资源URI:
- Graph的作用域格式为
https://graph.microsoft.com/<权限>(如https://graph.microsoft.com/User.Read) - SharePoint的作用域格式为
https://<你的租户域名>.sharepoint.com/<权限>(如https://contoso.sharepoint.com/Sites.Read.All)
如果直接把两种不带资源前缀的权限(比如User.Read + Sites.Read.All)混合,或者格式错误,Azure AD会判定为无效作用域,抛出错误。
具体实现步骤
1. 配置Azure AD应用权限
在Azure AD应用注册中:
- 添加Graph API的所需权限(如
User.Read、Files.Read) - 添加SharePoint Online的所需权限(如
Sites.Read.All,注意选择「SharePoint」类别下的权限,而非Graph中的SharePoint权限) - 对租户级权限完成管理员同意
2. 单次登录获取刷新令牌
发起登录请求时,指定包含两个API权限的作用域列表,同时加上offline_access以获取刷新令牌(用于后续兑换其他资源的令牌):
// 替换为你的租户域名和所需权限 val scopes = listOf( "https://graph.microsoft.com/.default", "https://contoso.sharepoint.com/.default", "offline_access" ) // 以MSAL for Android为例初始化客户端 val pca = PublicClientApplication.create( context, "<你的客户端ID>", Authority.create("https://login.microsoftonline.com/<你的租户ID>") ) // 发起登录请求 val authParams = AcquireTokenParameters.Builder() .startAuthorizationFromActivity(activity) .withScopes(scopes) .withCallback(object : AuthenticationCallback { override fun onSuccess(result: IAuthenticationResult) { // 保存刷新令牌和账户信息,用于后续获取SharePoint令牌 val refreshToken = result.refreshToken val account = result.account } override fun onError(e: MsalException) { /* 处理错误 */ } override fun onCancel() { /* 用户取消登录 */ } }) .build() pca.acquireToken(authParams)
登录成功后,你会得到Graph API的访问令牌,以及一个可用于兑换SharePoint令牌的刷新令牌。
3. 用刷新令牌获取SharePoint访问令牌
无需再次提示用户登录,直接通过刷新令牌静默获取SharePoint的令牌:
val silentParams = AcquireTokenSilentParameters.Builder() .withScopes(listOf("https://contoso.sharepoint.com/.default")) .withAccount(account) // 从登录结果中获取的账户 .withForceRefresh(false) .withCallback(object : SilentAuthenticationCallback { override fun onSuccess(result: IAuthenticationResult) { val sharePointToken = result.accessToken // 使用该令牌调用SharePoint API } override fun onError(e: MsalException) { // 仅当权限变更等特殊情况时需要重新授权 } }) .build() pca.acquireTokenSilentAsync(silentParams)
关键注意事项
- 始终使用完整的资源前缀定义作用域,避免省略
https://<租户域名>.sharepoint.com/或https://graph.microsoft.com/ offline_access作用域是获取刷新令牌的必要条件,没有它无法后续静默获取其他资源的令牌- 确保应用注册的权限已正确配置并完成管理员同意,否则会出现权限不足的错误
内容的提问来源于stack exchange,提问作者IEatGemstones
相关产品推荐
相关产品推荐

