问题:
我正在创建一个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);
})
});
}
}
在这里,我首先搜索路线中指定的用户,然后将具有访问令牌的相关模型添加到生成的用户中。因此,利用搜索中的所有信息,我可以检索由于某些原因而无法使用的预订信息。
因此,如果有人知道这个问题可能是什么,请告诉我,或者如果有人对我采用的方法有提示,他们将永远受到欢迎。
谢谢!
答案 0 :(得分:0)
所以我找到了想要实现的解决方案,但是我不得不使用Microsoft库。因此,我找到了Microsoft Graph demo app并进行了编辑,以实现自己想要的目标。当它起作用时,我只是将其复制到我的项目中,并对其进行了进一步的编辑,因此它仅使用我需要的内容。