如何从 Angular 获取 OneDrive API 的访问令牌

How to get the Access Token for OneDrive API from Angular

我有一个 Angular 应用程序可以让用户上传文件。我计划使用 OneDrive API(我为应用程序设置的 OneDrive 帐户)将这些文件存储在 OneDrive 中。

我知道我们必须使用 OAuth2.0 从网络服务器获取访问令牌并将该令牌用作不记名令牌以使用 API 来管理我在 OneDrive 中的文件。

如何在我的 Angular 应用程序中获取此访问令牌?

I need to get the access token without redirecting to the login page(Without interaction from the user). but in the background.

我尝试了以下 URL 在 POSTMAN 中获取访问令牌。

https://login.microsoftonline.com/{Tenant_ID}/oauth2/token

我试过了,

Angular中的隐式流函数 (我硬编码了 URL 和要测试的值)

    getToken() {
    var msFormData = new FormData();
    msFormData.append('grant_type', 'client_credentials');
    msFormData.append('client_id', 'client_id');
    msFormData.append('client_secret', 'client_secret');
    msFormData.append('resource', 'https://graph.microsoft.com');

    return this.http.post("https://login.microsoftonline.com/{id}/oauth2/token", msFormData);
}

这三个都在工作并且能够获得令牌。 当我在 Angular 中尝试隐式流时,出现 CORS 错误。 (无论如何我都不能使用 implicit 因为客户端机密会被暴露)。

When tried with the Resource_owner flow I got the SPO license error message. Where if I use the user flow and retrieve the access token from the redirect URL. I am able to use the Graph API with that access token to get the drive items. So I know I don't need an SPO license for this.(Maybe)

如果这不是最好的方法,我可以在后端创建服务以获取访问 token/refresh 令牌并使用 API 将其提供给 Angular 应用程序,所以用户可以从浏览器上传文件。但是从上面的流程中获得的访问令牌给我一个 SPO 错误。


更新: 我发现要访问一个驱动器,我们需要一个委托访问令牌。这与 client_credenttial 流有何不同?以及如何获得它们?

要在没有用户交互的情况下获得有效的图形令牌,您可以使用以下请求。但请注意,这意味着在您的客户端代码中某个地方存储了客户端密码。有了这个,用户就可以请求有效的应用程序令牌并可以访问应用程序范围内允许的任何内容。

因此,就像名称中已经说明的那样,客户端机密应该保密并且不能在客户端站点的代码中使用。更好的办法是拥有一个自己的 REST api,它将以下请求和 returns 令牌发送到您的 angular 应用程序。如何保护此 api 取决于您。

非交互式用户令牌

var settings = {
  "async": true,
  "crossDomain": true,
  "url": `https://login.microsoftonline.com/${tenantId}/oauth2/token`,
  "method": "POST",
  "headers": {
    "content-type": "application/x-www-form-urlencoded",
    "cache-control": "no-cache"
  },
  "data": {
    "client_id": `${clientId}`,
    "client_secret": ${clientSecret}``,
    "resource": "https://graph.microsoft.com/",
    "username": `${username}`,
    "password": `${password}`,
    "grant_type": "password",
    "scope": "openid"
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});

不过,您可以将上述请求转换为您的具体语言。但作为一项特殊服务,我将上面的代码重写为您给定的代码(未经测试):

getToken() {
    var msFormData = new FormData();
    msFormData.append('clientId', `${clientId}`);
    msFormData.append('client_secret', `${clientSecret}`);
    msFormData.append('resource', 'https://graph.microsoft.com');
    msFormData.append('username', `${username}`);
    msFormData.append('password', `${password}`);
    msFormData.append('grant_type', 'password');
    msFormData.append('scope', 'openid');
    
    return this.http.post(`https://login.microsoftonline.com/${tenantId}/oauth2/token`, msFormData);
}

非交互式应用程序令牌

var settings = {
  "async": true,
  "crossDomain": true,
  "url": `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
  "method": "POST",
  "headers": {
    "content-type": "application/x-www-form-urlencoded",
    "cache-control": "no-cache",
  },
  "data": {
    "grant_type": "client_credentials",
    "scope": "https://graph.microsoft.com/.default",
    "client_id": `${clientId}`,
    "client_secret": `${clientSecret}`
  }
}

$.ajax(settings).done(function (response) {
  console.log(response);
});

除了使用仅包含变量的反引号字符串,您还可以直接使用变量本身。但是我选择这种风格是为了更好的强调,这些参数必须自己提供。

在 angular 中将文件上传到一个驱动器涉及三个步骤,

  1. 针对 Azure AD 对最终用户进行身份验证并获得 id_token
  2. 获取图表API令牌
  3. 为一个驱动器调用文件上传API

第一步是用户使用 Azure AD 进行身份验证,这将以交互方式进行。我们可以使用 MSAL 库来获取 id 令牌。下面提到的 Link 有所有的文档,

https://docs.microsoft.com/en-us/graph/auth-v2-user#1-register-your-app

在 Azure AD 应用程序的第二步之前,我们需要映射配置受保护的资源映射并在其中添加图形 API url,范围为 "Files.ReadWrite.All" 。然后尝试使用上述 link 中提到的第 3 步在后台获取图 api 令牌的令牌。图 API 令牌已使用客户端密钥生成。

一旦我们收到 Graph API 令牌,我们就可以发送文件了。为此,您可以使用下面提到的 links,

委托令牌通常称为user token,您通过客户端凭证流获得的令牌是application token。它们之间唯一的区别是是否有用户登录。

如果您想获得委托令牌,必须登录用户。无论是interactive login or a non-interactive login,用户必须登录!

此外,您需要将 /tenant id 端点更改为 /common 端点以避免租户级别登录。

以防其他人有类似的问题,几周前,当我试图在我的 angular 应用程序中做与 OP 相同的事情时。由于我工作的公司使用 个人 Microsoft 帐户 ,因此无法使用非交互式流程 [1][] 访问公司的一个驱动器。因此,由于公司员工已经 将公司的一个驱动器作为共享文件夹 ,我们正在寻找一种新的登录解决方案,我们只能使用交互式方式来访问 Graph API.我们拥抱 MSAL 并将我们的登录方法更改为使用员工的个人 Microsoft 帐户。

所以为了登录,我们使用 @azure/msal-angular,它也提供了一个 HTTP 拦截器和弹出策略。因此,该员工使用 Oauth2 Token Flow 在弹出窗口中填写了他们的 Microsoft 凭据。登录后,MSAL_INTECEPTOR 会刷新我们的令牌,并在向 Graph API 发出请求时输入授权码。我们代码中的更改在这 2 个提交中进行了描述[3][4]. With this part ready, now we can make a request to Onedrive API to upload files to the company shared folder that all employees have. I shared some tips about upload to shared folders here: Microsoft Graph: Uploading files to shared with me folder on OneDrive?.

我非常喜欢我使用 MSAL 获得的结果,公司员工也喜欢直接的 Onedrive 集成。因为他们只需要登录我们的应用程序一次就可以了。

我分享了一步一步的 Postman Oauth2 Code Flow 来练习 Onedrive 文件上传到个人账户: