Microsoft Graph getSchedule方法导致InvalidAuthenticationToken错误代码80049217

时间:2020-02-20 14:44:02

标签: node.js angular express microsoft-graph-api loopback

问题:

我正在创建一个Angular Web应用程序,公司可以在其中注册1个用户。使用此帐户,他们可以在以后添加其Microsoft资源帐户,但除此之外,在使用其Microsoft帐户授权Microsoft Graph之前,他们什么都不做。获得授权(并保存访问令牌)后,他们可以检索其所选资源帐户的预订信息。这些信息将显示在整个公司的不同屏幕上,以便他们知道在某个时刻已经占用或预订了哪些会议室。

到目前为止,我已经能够完成授权过程,但是在尝试检索收到的预订时:

  "error": {
    "code": "InvalidAuthenticationToken",
    "message": "CompactToken parsing failed with error code: 80049217",
    "innerError": {
      "request-id": "1c24ceea-fa85-4cd0-bb0d-ab14ae26b65b",
      "date": "2020-02-19T15:54:07"
    }
  }
}

我不知道问题到底出在什么地方。我希望有人能够帮助我。我确实要道歉,因为这是我在StackOverflow上的第一篇文章,我不知道这是不是一篇好文章,以及是否有足够的信息。

我尝试过的事情:

在开始创建Web应用程序和api之前,我一直在尝试使用图浏览器,并且能够执行日历请求,并且响应看起来很有希望,因此我开始从事该项目。

起初,我使用的授权过程是Microsoft authorization process on behalf of the signed-in user。其原因是因为用户是授权并请求预订信息的人。当我开始检索预订信息时,我意识到我们必须从不同的资源邮箱中检索此信息。因此,由于普通用户无法访问其组织中的其他用户,因此我们需要有一个管理员用户。

那是当我发现我们现在正在使用的授权过程,即Microsoft authorization process without signed-in user时。这将需要管理员同意,并且能够从多个资源邮箱中检索预订信息。

现在,当授权过程完全完成时,我想尝试获取预订信息,但是每次我收到InvalidAuthenticationToken时(请参见上文以获取完整错误)。

我检查了azure门户中的权限,并更改了它们,希望可以解决此问题,但显然不能解决。因此,现在这些权限如下所示:Permissions in the azure portal (image)

因此,我开始查看我进行的授权过程。 最初的用户通过Angular Web应用程序上的链接进行授权:

<a 
    href="https://login.microsoftonline.com/common/adminconsent
        ?client_id=**CLIENT_ID**
        &state={{user.email}}
        &redirect_uri=**REDIRECT_URL_TO_API**"
    type="button"
    class="btn btn-icon btn-danger"
    aria-label="error"
>
    <clr-icon shape="error-standard"></clr-icon>
    Authorize
</a>

我检查了正确的客户端ID,与URL相同,因为它没有问题地进入了api。状态为用户电子邮件,因为稍后回送api需要使用该电子邮件。

因此,我使用具有管理员权限的Microsoft帐户登录,并且接受了要求使用的权限。完成后,它将重定向到我已构建的api。

此api是使用Loopback 3完全构建的,它使用MongoDB作为数据库。因此,要使用环回,我必须创建2个模型,其中1个模型用于网站上的登录用户(AuthUser),另外1个模型用于Microsoft Graph(MSGraphAuth)的访问令牌。 AuthUser模型与MSGraphAuth模型具有“ hasOne关系”。

除了使用的模型外,我还在/ server / boot目录中设置了中间件。在那里,我编辑了root.js文件,以便添加一些快速路由。 root.js文件如下所示:

'use strict';
const express = require('express');
const erouter = require('express').Router();
var cors = require('cors');
const msauth = require('./msAuth.service');
const getbookings = require('./retrieveBookings.service');
module.exports = function(server) {
    // Install a `/` route that returns server status
    var router = server.loopback.Router();
    router.get('/', server.loopback.status());

    var app = express();
    app.use(cors({
        origin: "*"
    }));
    erouter.get('/API/auth', msauth());
    erouter.get('/API/bookings/:userid', getbookings());
    app.use(erouter);
    server.use(app);
    server.use(router);
};

因此,当Microsoft重定向时,它将转到/API/auth路由,其背后的代码如下所示:

'use strict';
const request = require('request');
var querystring = require('querystring');
const server = require("../server");
module.exports = function() {
    return function(req, res, next) {
        var AuthUser = server.models.AuthUser;
        var user = {};
        AuthUser.find({ where: { email: req.query.state } }, function(err, au) {
            if (err) return next(err);
            if (au.length != 1) return res.redirect("https://*WEBSITE*/settings?error=user_not_found");
            user = au[0];
            if (!req.query.admin_consent) return res.redirect("https://*WEBSITE*/settings?error=permission_denied");
            const dataTokenReq = querystring.stringify({
                "client_id": process.env.client_id,
                "scope": "https://graph.microsoft.com/.default",
                "client_secret": process.env.client_secret,
                "grant_type": "client_credentials"
            });
            const postOptions = {
                url: `https://login.microsoftonline.com/${req.query.tenant}/oauth2/v2.0/token`,
                method: 'POST',
                body: dataTokenReq,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Host': 'login.microsoftonline.com'
                }
            }
            request(postOptions, function(err, resp, body) {
                if (err) return next(err);
                var body = JSON.parse(body);
                const dataMsAuth = querystring.stringify({
                    "created": new Date().toDateString(),
                    "userId": user.id,
                    "token_type": body.token_type,
                    "expires": body.expires_in,
                    "access_token": body.access_token,
                    "tenant": req.query.tenant
                });
                const postMSAuth = {
                    url: "https://*API_SITE*/api/AuthUsers/" + user.id + "/msauth",
                    method: 'POST',
                    body: dataMsAuth,
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    }
                }
                request(postMSAuth, function(err, resp, body) {
                    if (err) return next(err);
                    // Update user
                    console.log(body);
                    res.redirect("https://*WEBSITE*/settings?auth=success");
                });
            });
        });
    }
}

因此,在这里我首先根据州的电子邮件地址找到该用户,以便可以向该用户添加令牌,但是首先我要检查find和admin_consent中的问题。如果没有问题,我将按照documentation中所述,使用客户端ID,机密,默认范围和grant_type作为client_credentials检索令牌。当我收到令牌没有问题时,我通过访问指定用户的相关模型来发布它们。 (通常,如果完成此操作并且没有问题,我将使用布尔值更新用户,以便它知道它已通过身份验证,而无需访问相关模型,该模型可以工作,但由于某种原因未在数据库中进行更新。)

现在,我被重定向到该网站,并且只需要注销即可刷新cookie,因此它知道我已通过Microsoft Graph进行身份验证。因此,当我去查看所选资源邮箱的预订时,我会看到此信息,但这不起作用。

当我尝试检索预订信息时,我会通过我的回送环境/API/bookings/:userid中的一条定制路线来询问。在这条路线上,我有以下代码来检索预订信息:

'use strict';
const request = require('request');
var querystring = require('querystring');
const server = require("../server");
module.exports = function() {
    return function(req, res, next) {
        /**
         * retrieve bookings user
         */
        var AuthUser = server.models.AuthUser;
        AuthUser.findById(req.params.userid, { include: 'msauth' }, function(err, au) {
            if (err) return next(err);
            if (au == null) return next;
            console.log(au);
            if (au.msauth == null) return next;
            const data = querystring.stringify({
                "schedules": ["meetingroom@example.com", *RESOURCE_MAILBOX*],
                "startTime": {
                    "dateTime": "2020-02-20T01:00:00",
                    "timeZone": "Europe/Paris"
                },
                "endTime": {
                    "dateTime": "2020-02-29T23:00:00",
                    "timeZone": "Europe/Paris"
                }
            });
            const postOptions = {
                url: "https://graph.microsoft.com/v1.0/me/calendar/getSchedule",
                method: 'POST',
                body: data,
                headers: {
                    'Authorization': "Bearer " + au.msauth.access_token,
                    'Content-Type': 'application/json',
                    'Prefer': "outlook.timezone='Pacific Standard Time'"
                }
            }
            request(postOptions, function(err, resp, body) {
                if (err) return next(err);
                console.log(body);
                res.json(body);
            })
        });
    }
}

在这里,我首先搜索路线中指定的用户,然后将具有访问令牌的相关模型添加到生成的用户中。因此,利用搜索中的所有信息,我可以检索由于某些原因而无法使用的预订信息。

因此,如果有人知道这个问题可能是什么,请告诉我,或者如果有人对我采用的方法有提示,他们将永远受到欢迎。

谢谢!

1 个答案:

答案 0 :(得分:0)

所以我找到了想要实现的解决方案,但是我不得不使用Microsoft库。因此,我找到了Microsoft Graph demo app并进行了编辑,以实现自己想要的目标。当它起作用时,我只是将其复制到我的项目中,并对其进行了进一步的编辑,因此它仅使用我需要的内容。