pet*_*ski 5 c# authentication azure oauth-2.0 microsoft-graph
真诚的道歉,如果我错过了这篇文章的内容,因为我在读完了几个小时之后我的智慧结束了.
我正在尝试编写一个后端服务(Windows),它将通过Azure AD连接到MS Graph API.我正在使用C#来破坏概念验证,但是在MS文档,博客等方面遇到了很多问题,这些问题相当令人费解并且质量很差.他们似乎都认为他们的API的消费者是前端桌面或基于浏览器的应用程序.
无论如何,我设法使用服务帐户的UserPasswordCredential连接到Graph,我在AuthenticationContext响应中收到一个令牌,但没有刷新令牌信息.MSDN建议该方法可能会返回刷新信息,但嘿嘿,它可能不会.
此外,阅读(或尝试阅读)ADAL 3上的博客文章(从2015年开始):
让我对刷新现在的工作方式感到困惑.这篇文章似乎暗示着令人神奇的是令牌在缓存中为你刷新,但在用我的POC测试之后情况并非如此.
我也从MS中看到了这篇文章,似乎在标题中指出:
https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365
然而,需要你注册应用程序,跳过弹出式箍允许访问等等.
由于我的样本正在运行,我已经尝试安排重新认证,希望在获得初始令牌(持续60分钟)后50分钟获得新令牌但是我得到相同的令牌.这意味着在60分钟1秒内,通过客户端的任何调用都会抛出异常(ServiceException,我必须检查里面的文本以查看令牌到期相关信息).对我来说,无法重新验证和刷新令牌以继续使用客户端进行更"无缝"的使用是没有意义的.
这是我的代码的精简版示例:
namespace O365GraphTest
{
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Nito.AsyncEx;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Linq;
using System.Threading;
using System.Security;
public class Program
{
// Just use a single HttpClient under the hood so we don't hit any socket limits.
private static readonly HttpProvider HttpProvider = new HttpProvider(new HttpClientHandler(), false);
public static void Main(string[] args)
{
try
{
AsyncContext.Run(() => MainAsync(args));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
private static async Task MainAsync(string[] args)
{
var tenant = "mytenant.onmicrosoft.com";
var username = $"test.user@{tenant}";
var password = "fooooooo";
var token = await GetAccessToken(username, password, tenant);
var client = GetClient(token)
// Example of graph call
var skusResult = await client.SubscribedSkus.Request().GetAsync();
}
private static async Task<string> GetAccessToken(string username, string password, string tenant = null)
{
var authString = tenant == null ?
$"https://login.microsoftonline.com/common/oauth2/token" :
$"https://login.microsoftonline.com/{tenant}/oauth2/token";
var authContext = new AuthenticationContext(authString);
var creds = new UserPasswordCredential(username, password);
// Generic client ID
var clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
var resource = "https://graph.microsoft.com";
// NOTE: There's no refresh information here, and re-authing for a token pre-expiry doesn't give a new token.
var authenticationResult = await authContext.AcquireTokenAsync(resource, clientId, creds);
return authenticationResult.AccessToken;
}
private static GraphServiceClient GetClient(string accessToken, IHttpProvider provider = null)
{
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.FromResult(0);
});
var graphClient = new GraphServiceClient(delegateAuthProvider, provider ?? HttpProvider);
return graphClient;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人可以帮助我,不用只是说"以不同的方式做",我会非常感激,因为似乎很少有关于这个在线讨论(Freenode,IRC)和博客都很旧,MSDN上的文档简洁以及库的特定先前版本等
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireTokenAsync
现在已删除AuthenticationContext类的refresh方法.
谢谢你的建议.
彼得
UPDATE
首先感谢@Fei Xue回答了我的问题,但是我无法理解他们开始说的话.
与Fei Xue聊天之后,似乎如果你保持auth上下文,你可以使用AcquireTokenSilentAsync来获得新的令牌,你不需要处理任何刷新/ reauth等的安排.只需在支票中烘烤委托处理程序(关于您正在调用的方法)是获取客户端的一部分.
这是我测试过的示例代码的更新版本,似乎可以正常工作.
namespace O365GraphTest
{
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Nito.AsyncEx;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class Program
{
private const string Resource = "https://graph.microsoft.com";
// Well known ClientID
private const string ClientId = "1950a258-227b-4e31-a9cf-717495945fc2";
private static readonly string Tenant = "mytenant.onmicrosoft.com";
private static readonly HttpProvider HttpProvider = new HttpProvider(new HttpClientHandler(), false);
private static readonly AuthenticationContext AuthContext = GetAuthenticationContext(Tenant);
public static void Main(string[] args)
{
try
{
AsyncContext.Run(() => MainAsync(args));
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
private static async Task MainAsync(string[] args)
{
var userName = $"test.user@{Tenant}";
var password = "fooooooo";
var cred = new UserPasswordCredential(userName, password);
// Get the client and make some graph calls with token expiring delays in between.
var client = GetGraphClient(cred);
var skusResult = await client.SubscribedSkus.Request().GetAsync();
await Task.Delay(TimeSpan.FromMinutes(65));
var usersResult = await client.Users.Request().GetAsync();
}
private static AuthenticationContext GetAuthenticationContext(string tenant = null)
{
var authString = tenant == null ?
$"https://login.microsoftonline.com/common/oauth2/token" :
$"https://login.microsoftonline.com/{tenant}/oauth2/token";
return new AuthenticationContext(authString);
}
private static GraphServiceClient GetGraphClient(UserPasswordCredential credential)
{
var delegateAuthProvider = new DelegateAuthenticationProvider(async (requestMessage) =>
{
var result = AuthContext.TokenCache?.Count > 0 ?
await AuthContext.AcquireTokenSilentAsync(Resource, ClientId) :
await AuthContext.AcquireTokenAsync(Resource, ClientId, credential);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
});
return new GraphServiceClient(delegateAuthProvider, HttpProvider);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我也被给了2篇MS文章参考:
我希望这能帮助处于同样情况的其他人,就像之前一样,我把头发拉出来!
在GraphServiceClient类用于操作的Microsoft Graph这是不能够获得或的access_token refresh_token.
正如博客中提到的最新版本的azure-activedirectory-library-for-dotnet库不会将refresh_token暴露给开发人员.您可以从AuthenticationResult.cs类中检查它.如果在调用方法时令牌已过期,此库将帮助刷新access_token AcquireTokenSilentAsync.
因此,在您的方案中,我们应该使用此方法来获取访问令牌GraphServiceClient.然后它将始终提供可用的访问令牌GraphServiceClient.以下是供您参考的代码:
string authority = "https://login.microsoftonline.com/{tenant}";
string resource = "https://graph.microsoft.com";
string clientId = "";
string userName = "";
string password = "";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authContext = new AuthenticationContext(authority);
var result = authContext.AcquireTokenAsync(resource, clientId, userPasswordCredential).Result;
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
var access_token = authContext.AcquireTokenSilentAsync(resrouce, clientId).Result.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", access_token);
return Task.FromResult(0);
}));
var a = graphserviceClient.Me.Request().GetAsync().Result;
Run Code Online (Sandbox Code Playgroud)
string authority = "https://login.microsoftonline.com/adfei.onmicrosoft.com";
string resrouce = "https://graph.microsoft.com";
string clientId = "";
string userName = "";
string password = "";
UserPasswordCredential userPasswordCredential = new UserPasswordCredential(userName, password);
AuthenticationContext authContext = new AuthenticationContext(authority);
var result = authContext.AcquireTokenAsync(resrouce, clientId, userPasswordCredential).Result;
var graphserviceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) =>
{
var access_token = authContext.AcquireTokenSilentAsync(resrouce, clientId).Result.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", access_token);
return Task.FromResult(0);
}));
var a = graphserviceClient.Me.Request().GetAsync().Result;
//simulate the access_token expired to change the access_token
graphserviceClient.AuthenticationProvider=
new DelegateAuthenticationProvider(
(requestMessage) =>
{
var access_token = "abc";
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", access_token);
return Task.FromResult(0);
});
var b = graphserviceClient.Me.Request().GetAsync().Result;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11872 次 |
| 最近记录: |