英文:
Microsoft Graph .NET SDK on-behalf-of flow returns Access Denied 403 for Sites/Files
问题
我正在尝试为 Microsoft Graph API 实现代表用户流程。我正在使用 Microsoft Graph .NET SDK,但我的请求出现了拒绝访问(状态码 403)的问题。我不确定如何正确设置我组织中用户在 Azure Active Directory 中的权限。我正在尝试实现 SharePoint / OneDrive 文件操作,如列出文件/下载/上传等,使用代表流程。我希望能够访问 任何 驱动器/站点的文件并能够上传/下载/列出。
我的应用程序设计如下:
但不是桌面 WPF 应用程序,我有一个使用 next-auth 的 next.js 网站。这是我在 next.js 应用程序中使用 next-auth 定义 Azure Active Directory 提供程序的方式:
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID, // AAD 中的 NextAuth 客户端 ID
clientSecret: process.env.AZURE_AD_CLIENT_SECRET, // AAD 中的 NextAuth 客户端密钥值
tenantId: process.env.AZURE_AD_TENANT_ID,
authorization: {
params: {
scope: "openid email profile offline_access api://NextAuthAPI/.default",
prompt: "consent",
}
},
}),
]
在我的 Azure Active Directory 中,我注册了前端应用程序和 Web API 作为 NextAuth 和 NextAuthAPI,并为它们生成了客户端密钥。对于 NextAuthAPI(Web API),我创建了一个范围:
并添加了以下权限:
对于 NextAuth(next.js)应用程序,我添加了以下权限:
在我的 next.js 前端应用程序中登录后,我能够获取一个访问令牌,其中包含:
"aud": "api://NextAuthAPI",
"scp": "access_as_user"
....
然后,我将此访问令牌提供给 Web API 并创建客户端,如下所示:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "c0dc-------------------5ce7";
var clientId = "087e66-----------------------b5";
var clientSecret = "p5Q8Q-----------------------JddpK";
// 使用 Azure.Identity;
var options = new OnBehalfOfCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var oboToken = /* 在这里粘贴来自 Next.js 应用程序的访问令牌 */;
var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, oboToken, options);
m_GraphClient = new GraphServiceClient(onBehalfOfCredential, scopes);
现在,当我初始化了 m_GraphClient 后,我尝试进行一些请求。这个请求对我有效:
var response = await m_GraphClient.Me.GetAsync();
但像这样的请求:
var response2 = await m_GraphClient.Sites.GetAllSites.GetAsync();
会引发一个带有状态码 403 和消息 "Access denied" 的 ODataError 异常。
我使用的包:
- Microsoft.Graph 5.11.0(后端)
- Azure.Identity 1.9.0(后端)
- Next.JS 13.4.5-canary.2(前端)
- next-auth 4.22.1(前端)
英文:
I'm trying to implement on-behalf-of user flow for Microsoft Graph API. I'm using Microsoft Graph .NET SDK and I'm encountering Access Denied (status code 403) for my requests. I'm not sure how to correctly set up permissions for users of my organization in Active Azure Directory. I'm trying to implement SharePoint / OneDrive file operations like list files/download/upload etc. with on-behalf-of flow. I want to be able to access any drive/site files and be able to upload/download/list.
The way my application is designed is like this:
But instead of Desktop WPF app, I have next.js website using next-auth. This is how I define my Azure Active Directory provider to sign-in in my next.js application using next-auth:
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID, // NextAuth client id in AAD
clientSecret: process.env.AZURE_AD_CLIENT_SECRET, // NextAuth client secret value in AAD
tenantId: process.env.AZURE_AD_TENANT_ID,
authorization: {
params: {
scope: "openid email profile offline_access api://NextAuthAPI/.default",
prompt: "consent",
}
},
}),
]
In my AAD I registered my frontend app and web api as NextAuth and NextAuthAPI, generated client secret for both of them. For the NextAuthAPI (webAPI) I created a scope:
and added these permissions:
For the NextAuth (next.js) app I added this permission:
After signing in my next.js frontend application, I'm able to receive an access token with:
"aud": "api://NextAuthAPI",
"scp": "access_as_user"
....
Then I provide this access_token to the webAPI and create client like this:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "c0dc-------------------5ce7";
var clientId = "087e66-----------------------b5";
var clientSecret = "p5Q8Q-----------------------JddpK";
// using Azure.Identity;
var options = new OnBehalfOfCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var oboToken = /* HERE I PASTE ACCESS_TOKEN FROM NEXTJS APP */;
var onBehalfOfCredential = new OnBehalfOfCredential(tenantId, clientId, clientSecret, oboToken, options);
m_GraphClient = new GraphServiceClient(onBehalfOfCredential, scopes);
Now, when I have the m_GraphClient initialized, I'm trying to make some requests. This request works for me:
var response = await m_GraphClient.Me.GetAsync();
But something like this:
var response2 = await m_GraphClient.Sites.GetAllSites.GetAsync();
throws an ODataError exception with status code 403 and message "Access denied".
Packages I use:
> Microsoft.Graph 5.11.0 (backend)
>
> Azure.Identity 1.9.0 (backend)
>
> Next.JS 13.4.5-canary.2 (frontend)
>
> next-auth 4.22.1 (frontend)
答案1
得分: 0
您想要能够访问任何驱动器/站点文件并能够上传/下载/列出。然后,代表流程不适合您。代表流程将生成具有委派的API权限的访问令牌,不能用于访问其他人的驱动器/站点文件。我们需要使用具有应用程序API权限的访问令牌。
在这种情况下,我们只能使用客户端凭据流程,以便我们可以生成具有应用程序API权限的访问令牌。首先,在Azure门户中授予应用程序API权限。例如,我们尝试使用此Graph API。
然后,在您的API应用程序中,您的代码应该如下所示,使用ClientSecretCredential
而不是您的OnBehalfOfCredential
:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_id";
var clientId = "client_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
英文:
You want to be able to access any drive/site files and be able to upload/download/list. Then on-behalf-of flow isn't suitable for you. OBO flow will generate access token with Delegated API permission, which can't be used to access any others' drive/site files. We need to use access token with Application API permission.
Then in this scenario, we can only use client credential flow so that we can generate access token with Application API permission. First, grant application API permission in Azure portal. For example, we are trying to use this Graph API.
Then in your API application, your code should look like this which used ClientSecretCredential
instead of your OnBehalfOfCredential
:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_id";
var clientId = "client_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论