已解决:在调用Microsoft Graph API时,出现了Request_ResourceNotFound并且租户没有SPO许可证

时间:2019-08-19 16:36:55

标签: node.js microsoft-graph

我最终尝试使用Graph API将数据添加到OneDrive中的Excel文件中,并在我的PC而非网站上运行本地node.js应用程序,而我的目标是在Windows中简单地调用“ node index.js”控制台来执行此操作,而无需授权我的登录名(也许这是不可能的?)

我从这些端点开始首先获取简单数据,但无法使它们工作(基本URL https://graph.microsoft.com):

  • /v1.0/me/
  • /v1.0/users/{My-UserID}/
  • /v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets
  • /v1.0/users/{My-UserID}/drive/root:/TestFile.xlsx:/workbook/worksheets

/v1.0/me//v1.0/users/{My-UserID}/返回'Resource \'{My-UserID} \'不存在,或者其查询的参考属性对象之一不存在。'

/v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets/v1.0/users/{My-UserID}/drive/root:/TestFile.xlsx:/workbook/worksheets返回“ Tenant没有SPO许可证。”

我如何获得令牌

我使用adal-node库(https://www.npmjs.com/package/adal-node),并使用“通过客户端凭据进行服务器到服务器”流程获得了访问令牌。

我如何调用图形API

我正在使用axios在没有Microsoft Graph SDK的情况下手动调用API。

我如何在Azure门户中注册我的应用

  1. 我去了“ Azure Active Directory”。
  2. 点击了“应用注册”。
  3. 点击“新注册”。
  4. a)选择一个应用名称。

    b)在“支持的帐户类型”中选择了“仅此组织目录中的帐户(仅默认目录-单租户)”。

    c)左侧“重定向URI(可选)”为空

  5. 点击“注册”。
  6. 在下面的我的应用程序中复制了“应用程序(客户端)ID”并将其用作“ ClientID”常量。
  7. 复制了“目录(租户)ID”,并在下面的我的应用中将其用作“租户”常量。
  8. 点击“证书和机密”。
  9. 点击“新客户密码”。
  10. a)将“说明”留空。

    b)在“过期”下选择“从不”。

    c)点击“添加”。

  11. 复制生成的机密并将其用作下面我的应用程序中的“ ClientSecret”常量。
  12. 点击了“ API权限”。
  13. 点击“添加权限”。
  14. 点击“ Microsoft Graph”。
  15. 单击“应用程序权限”。
  16. 选中了“ Files.Read.All”。
  17. 点击“添加权限”。
  18. 单击“授予默认目录的管理员许可”,然后单击“是”。

我还尝试添加委派权限,以检查“ Directory.Read.All”和“ Group.Read.All”的所有可用权限和应用程序权限。

我尝试过的事情

我尝试使用Graph Explorer(https://developer.microsoft.com/en-us/graph/graph-explorer),可以看到两者 /v1.0/me/和/v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets返回正确的数据。

我还尝试用/ users / {My-UserID} /替换/ me /,并且Graph Explorer返回相同的正确数据,但是我的node.js应用仍然返回相同的错误。

我用JWT解码器检查了检索到的令牌,看起来像

  • oid正确等于{My-UserID}。

  • roles包括“ Files.Read.All”和“ User.Read.All”,这与我在Azure门户中的应用程序注册中设置的一样。

  • appid正确等于{My-ApplicationID}。

  • tid正确等于{My-TenantID}。

  • aud等于'https://graph.microsoft.com'

我还尝试了通过向标头中的https://login.microsoftonline.com/{My-TenantID}/oauth2/v2.0/token调用POST来手动获取令牌的方法:

'Content-Type': 'application/x-www-form-urlencoded'

和数据:

client_id=${My-ClientID}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret={My-ClientSecret}&grant_type=client_credentials

,并显示以下错误:“ AADSTS7000215:提供了无效的客户端密码”。 <-编辑:我发现了为什么出现此错误。我必须使用%{ascii-in-hex}来转义特殊字符,例如:/ +。

其他注意事项

我有一个Office 365家庭订阅和一个随用随付Azure帐户,使用与OneDrive相同的电子邮件地址,该电子邮件具有要向其添加数据的Excel文件。

编辑:我发现,如果您使用Graph Explorer并调用/v1.0/users/{any-number},您将获得自己的个人资料。这意味着{My-UserID}可能一直都不是我正确的用户ID。

const Tenant = {My-TenantID};
const ClientID = {My-ApplicationID};
const ClientSecret = {My-ClientSecret};

function GetAccessToken()
{
    const AuthenticationContext = require('adal-node').AuthenticationContext;

    const authorityUrl = `https://login.microsoftonline.com/${Tenant}`;    
    const applicationId = ClientID; 
    const clientSecret = ClientSecret; 
    const resource = 'https://graph.microsoft.com';

    const context = new AuthenticationContext(authorityUrl);

    return new Promise((resolve, reject) => 
    {
        context.acquireTokenWithClientCredentials(resource, applicationId, clientSecret, (error, tokenResponse) =>
        {
            if (error) 
            {
                console.log('Get token error:');
                console.dir(error.stack, {depth: null});
                reject(error);
            } 
            else 
            {
                console.log('Get token success:');
                console.dir(tokenResponse, {depth: null});
                resolve(tokenResponse.accessToken);
            }
        });
    });
}

async function GetData()
{
    const axios = require('axios');
    const AccessToken = await GetAccessToken();

    axios(
    {
        method: 'GET',
        url: 'https://graph.microsoft.com/v1.0/me/', //I have also tried /v1.0/users/{My-UserID}/
        headers: 
        {
            'Authorization': `Bearer ${AccessToken}`,
        }
    })
    .then(result => 
    {
        console.log('Get data success:');
        console.dir(result.data, {depth: null});
    })
    .catch(error =>
    {
        console.log('Get data failed:');
        if (error.response.data != undefined)
        {
            console.dir(error.response.data, {depth: null});
        }
        else
        {
            console.dir(error, {depth: null});
        }   
    })
}

GetData();

更新1

我想我已经弄清楚了/v1.0/me/v1.0/users/{My-UserID}都不起作用的原因。如果我们在没有用户的情况下进行身份验证(我在此处选择的流程),则该应用程序只能访问的用户是Azure Active Directory(AAD)中的用户。现在,如果您使用您的hotmail帐户登录到Azure并转到“ Azure Active Directory”,然后单击“用户”,您将在那里看到您的帐户,但是我认为该帐户实际上是与“普通” Microsoft帐户(“普通”)不同的帐户。 'Microsoft帐户是您通过hotmail注册的帐户)。是的,微软似乎创建了一个单独的“ AAD”身份,该用户与您的“普通”身份用户不同。

{My-UserID}实际上是“公共”身份的用户ID。因此,这意味着,如果我调用/v1.0/users/{My-UserID},则我的应用程序将无法识别该用户ID,因为该用户ID不属于AAD。因此,我尝试的是从AAD中的用户复制“对象ID”,然后使用它:/v1.0/users/{AAD-ObjectID}然后中提琴,我看到了我的个人资料。 编辑:{My-UserID}实际上不是我的“公共”身份的用户ID。这是我还不知道的其他ID。

然后我尝试:

  • 创建一个新应用程序,而不是在“支持的帐户类型”中选择“仅此组织目录中的帐户(仅默认目录-单租户)”,而是选择“任何组织目录中的帐户(任何Azure AD目录-多租户)”,然后个人Microsoft帐户(例如Skype,Xbox)”代替
  • 遵循“代表用户的访问权限”身份验证流程

并可以确认/v1.0/mev1.0/users/{My-UserID}确实有效。 编辑:无论您指定哪个ID,Graph API实际上都会返回我的个人资料,因此您可以指定v1.0/users/123,它仍然会返回您的个人资料!

因此,我想,嗯,我不是使用Azure门户授予管理员同意,而是使用https://login.microsoftonline.com/common/adminconsent端点授予管理员同意。这样做,然后从/common/oauth2/v2.0/token获取访问令牌

  • /v1.0/me返回“当前认证的上下文对此请求无效。”
  • /v1.0/users/{My-UserID}返回“无法建立调用应用程序的身份”错误。
  • /v1.0/users/{AAD-ObjectID}返回“无法建立调用应用程序的身份”错误。

如果我从/{My-TenantID}/oauth2/v2.0/token获取访问令牌,则

  • /v1.0/me/v1.0/users/{My-UserID}返回原始的“ Request_ResourceNotFound”错误。
  • /v1.0/users/{AAD-ObjectID}正确返回了我的个人资料。

然后我尝试了/v1.0/users/{AAD-ObjectID}/drive/root:/TestFile.xlsx:/workbook/worksheets,但仍然收到“ Tenant没有SPO许可证”的信息。我怀疑这是因为我的AAD身份没有操纵OneDrive项目所需的许可。

一种解决方案,它不使用我发现有效的“代表用户获取访问权限”身份验证流程而向Excel添加数据,而是使用Logic App。因此,在Logic App中,我设置了一个HTTP请求端点,该端点在被触发时会将数据添加到Excel,并让我的Node.js应用程序调用此端点,而不是直接调用Graph API。

总结

我认为这可能是Microsoft必须最终解决的问题-AAD身份不应是一个单独的身份,而应与我的“公共”身份相同,以便我可以使用{{1 }},并使用/v1.0/users/{My-UserID}处理我的OneDrive文件。


更新2

我找到了一种无需使用Logic App即可直接调用Graph API的方法!

对于那些关注我的问题的人,有两种方法可以调用Graph API:

  • 代表用户致电
  • 没有用户拨打电话

我发现的关键点是“代表用户调用”,就像说,您(用户)想要通过您的应用程序使用Graph API,这实际上是我想要的,因为我想调用Graph API来编辑我的Excel文件。 “无需用户调用”就像是说,应用程序本身正在调用Graph API-不是使用该应用程序来调用Graph API的用户,因此,除非该用户位于活动目录中,否则它将无法访问用户数据。重要的是要注意(根据我先前的回答),您在AAD中看到的与您同名的用户实际上不是'you',而是您的'AAD'版本。因此,即使您在AAD中看到您的名字,“没有用户拨打电话”也将无法访问您的OneDrive项目。为此,您需要“代表用户致电”。

现在,如果我们需要执行“代表用户呼叫”,我想我每次都要访问“获取授权码->获取访问令牌”来访问我的OneDrive文件,但是我发现还可以刷新令牌,以便可以继续访问OneDrive文件而无需获取新的授权码。此步骤最重要的是,在获取授权码和令牌时,请确保在范围参数中包含/v1.0/users/{My-UserID}/drive,否则,当您要求令牌并且您返回offline_access时,将不会返回refreshToken将无法刷新令牌。您还需要在应用注册中添加offline_access委派权限。

因此,我可以让我的节点应用程序保存refreshToken并在每次运行时刷新令牌,并且中提琴,我可以随时编辑Excel文件!

1 个答案:

答案 0 :(得分:0)

您不能将/me与客户端凭据一起使用。由于您不对用户进行身份验证,因此Graph无法确定应映射到哪个用户“我”。

在使用仅应用程序令牌(即客户端凭据)时,您需要指定所需地址的用户:

  • /v1.0/users/{id or userPrincipalName}
  • /v1.0/{id or userPrincipalName}/drive/root:/TestFile.xlsx:/workbook/worksheets