[英]Microsoft Graph API, sending email response: StatusCode: 401 ReasonPhrase: 'Unauthorized'
[英]Framework.net service, using ConfidentialClientApplication and the Outlook Tasks REST API, got StatusCode: 401, ReasonPhrase: 'Unauthorized'
大家好! 我是 StackOverflow 的新手,所以我会尽力更好地解释我的问题。
我正在尝试通过 ac# .net framework cli 程序与 Outlook Calendar 和 Tasks REST API 进行交互,这将成为后台 Windows 服务。
我创建了一个专用的 Outlook 帐户,在Azure 门户上注册了该应用程序并调整了rest-sender项目。
使用PublicClientApplicationBuilder.AcquireTokenInteractive
我可以获得一个有效的,即使是 effimeral(1 小时),访问令牌并列出我的任务。 它也适用于其他 API 调用,但问题是每次我需要一个令牌时,它都会显示一个用于获取帐户的交互式Web 浏览器窗口。 该应用程序将成为生产无头服务器中的 Windows 后台服务,因此不可能有交互窗口。
我发现ConfidentialClientApplication.AcquireTokenForClient
需要一个客户端密码。 我从 Azure 门户创建了一个。 我可以得到一个令牌,但它似乎没有被授权。
应用程序这是 Azure 页面中应用程序的屏幕。
auth这些是身份验证设置。
auths这些是授权设置。
秘密这是秘密页面。
邮件从邮件中我可以看到这些权限(通过GetInteractiveToken
授予)
令牌这是解码后的令牌。
这是一个说明性代码:
static async Task<string> GetSecretAccessToken()
{
var client =
ConfidentialClientApplicationBuilder
.Create("<applicationId>")
.WithClientSecret("<clientSecret>")
.Build();
var result = client.AcquireTokenForClient(new []{
"https://outlook.office.com/.default",
}).ExecuteAsync();
var r = result.Result.AccessToken;
return r;
}
static async Task<string> GetInteractiveAccessToken()
{
var client =
PublicClientApplicationBuilder
.Create("<applicationId>")
.Build();
var result = client.AcquireTokenInteractive(new []{
"https://outlook.office.com/Tasks.ReadWrite"
}).ExecuteAsync();
var r = result.Result.AccessToken;
return r;
}
static async Task<string> GetAccessFromPasswordToken(string[] scopes){
try
{
var b =
PublicClientApplicationBuilder.Create(ConfigurationManager.AppSettings.Get("applicationId"))
.Build();
var result = b.AcquireTokenByUsernamePassword(scopes, "CodeGen.Preventizzatore@Outlook.it", GetSecureString()).ExecuteAsync();
return result.Result.AccessToken;
}
catch (MsalException ex)
{
Output.WriteLine(Output.Error, "Could not acquire access token: Error code: {0}, Error message: ",
ex.ErrorCode, ex.Message);
return string.Empty;
}
}
static async Task ListTasks()
{
string token = await GetAccessToken();
var request = new HttpRequestMessage(
new HttpMethod("GET"),
new UriBuilder("https://outlook.office.com/api/v2.0/users/CodeGen.Preventizzatore@Outlook.it/taskfolders").Uri
);
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
request.Headers.UserAgent.Add(
new System.Net.Http.Headers.ProductInfoHeaderValue("rest-sender", "1.0"));
request.Headers.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var result = await new HttpClient().SendAsync(request);
if (result.StatusCode == HttpStatusCode.Unauthorized)
return; // when GetSecretAccessToken
// when GetInteractiveAccessToken
string response = await result.Content.ReadAsStringAsync();
}
这是 Azure 上的清单:
{
"id": "a752666c-11a9-425e-a382-c63ebcbc8bb2",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "608bd039-6bc9-4a7e-ad86-2b6a3e20584b",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2019-06-21T08:18:56Z",
"groupMembershipClaims": null,
"identifierUris": [
"api://608bd039-6bc9-4a7e-ad86-2b6a3e20584b"
],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "CodeGen.Preventizzatore",
"oauth2AllowIdTokenImplicitFlow": false,
"oauth2AllowImplicitFlow": true,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-30T23:00:00Z",
"keyId": "c4a2fe3a-a09c-41ff-9115-40a2a2ef1a89",
"startDate": "2019-06-21T11:46:06.306Z",
"value": null,
"createdOn": "2019-06-21T11:46:08.2116173Z",
"hint": "nR?",
"displayName": "Preventizzatore"
}
],
"preAuthorizedApplications": [],
"publisherDomain": null,
"replyUrlsWithType": [
{
"url": "urn:ietf:wg:oauth:2.0:oob",
"type": "InstalledClient"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
"resourceAccess": [
{
"id": "bbd1ca91-75e0-4814-ad94-9c5dbbae3415",
"type": "Scope"
},
{
"id": "6b49b74d-642f-4417-a6b4-820576845707",
"type": "Scope"
},
{
"id": "bf24470f-10c1-436d-8d53-7b997eb473be",
"type": "Role"
},
{
"id": "77e65b5a-ceae-48b3-9490-50a86a038a48",
"type": "Role"
},
{
"id": "dc890d15-9560-4a4c-9b7f-a736ec74ec40",
"type": "Role"
},
{
"id": "798ee544-9d2d-430c-a058-570e29e34338",
"type": "Role"
},
{
"id": "c1b0de0a-1de9-455d-919f-eca451053141",
"type": "Role"
},
{
"id": "2c6a42ca-0d4d-49ad-bc0e-21222c449a65",
"type": "Role"
},
{
"id": "ef54d2bf-783f-4e0f-bca1-3210c0444d99",
"type": "Role"
},
{
"id": "2dfdc6dc-2fa7-4a2c-a922-dbd4f85d17be",
"type": "Role"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [],
"tokenEncryptionKeyId": null
}
使用非交互式令牌时,我得到:
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
Cache-Control: private
WWW-Authenticate: Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0000-0000-c000-000000000000@*", token_types="app_asserted_user_v1 service_asserted_app_v1", authorization_uri="https://login.windows.net/common/oauth2/authorize", error="invalid_token"
WWW-Authenticate: Basic Realm=""
request-id: 8a2ccebc-e8c3-4ec2-b8cb-64d1005a5bf7
X-CalculatedBETarget: MR2P264MB0817.FRAP264.PROD.OUTLOOK.COM
X-BackEndHttpStatus: 401
X-RUM-Validated: 1
x-ms-diagnostics: 2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
X-AspNet-Version: 4.0.30319
X-BeSku: WCS5
X-DiagInfo: MR2P264MB0817
X-BEServer: MR2P264MB0817
X-Powered-By: ASP.NET
X-FEServer: MRXP264CA0011
X-MSEdge-Ref: Ref A: 25DDA6AB2E72497199D611B555DACB60 Ref B: MIL30EDGE0414 Ref C: 2019-06-24T06:10:44Z
Date: Mon, 24 Jun 2019 06:10:43 GMT
Content-Type: text/html; charset=utf-8
}}
GetSecretAccessToken
和GetInteractiveAccessToken
提供令牌,但只有交互式令牌在ListTasks
方法中有效。 我需要另一个来使用 Win 服务。
我读到https://outlook.office.com/.default
使应用程序使用我在 Azure 门户上设置的静态应用程序权限。 但我无法让这种方法起作用。
我只是不知道它需要什么样的授权才能在这种模式下工作。
将近一周的时间,我一直在努力使其奏效,但没有成功。 也许我错过了一些微不足道的东西......所以我在这个星球上最好的地方寻求帮助。
我还在 .net core 中找到了一个注册设备代码的示例,但我更喜欢使用基本的 .net 框架,因为我的客户系统上已经有一个正在运行的项目。
我希望我已经澄清了我的问题,这是可行的。 有一个 Outlook 集成会非常好。
提前致谢。
==============
Microsoft 身份平台不支持个人帐户,因此如果您想从后台服务/守护程序访问 Outlook365.com REST API,您需要在 Azure AD 下并获得管理员的批准(他将按“为 [你自己] 授予管理员同意”按钮)。 否则您将无法执行任何请求,因为您的非交互式令牌将不包含任何范围权限。
感谢杰森约翰斯顿的大力帮助!
您正在使用客户端凭据流来获取应用程序令牌。 此令牌没有用户上下文,因此您不能在请求 URL 中使用/me
段。 将其替换为/users/{user-id}
。 {user-id}
是用户的对象 ID(通过执行GET /users
调用获得)或他们的 UPN(通常是他们的电子邮件地址)。
我还建议复制令牌并在https://jwt.ms解析它。 您想查看令牌并确保它包含Tasks.Read
权限。
还要确保您已获得管理员同意您在应用程序注册中配置的应用程序权限。 如果您是管理员,您应该会在API 权限部分下方看到一个授予同意部分,其中有一个按钮,您可以单击以授予同意。
如果您编写此内容是为了访问个人 Outlook.com 帐户,则不能使用客户端凭据流。 该特定流程仅适用于 Office 365 上的工作或学校帐户。
Azure OAuth 当前不支持个人帐户的非交互式流。 您可以获得的最接近的是实现令牌缓存序列化,然后使用应用程序进行一次交互式登录(用用户的刷新令牌为缓存设置种子)。 然后在随后的运行中,您可以调用AcquireTokenSilent
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.