MS Graph - 获取应用程序令牌(控制台应用程序)

时间:2017-11-06 21:07:10

标签: microsoft-graph

  • 控制台应用程序 - C#.Net 4.6
  • 专门的管理员用户 - 我无法在每次登录时提示 - 必须作为本机命令行/控制台应用程序无人值守运行。

我只是想让持有者令牌与Graph SDK调用一起发送。 我收到了一个令牌 (每次都是同一个人),但我被告知已经过期了。以下是信息:

  

访问令牌已过期,请使用Access&刷新令牌以验证

由于这是一个控制台应用程序,我不知道如何获取/保持访问权限并刷新令牌来执行此操作。

仅供参考:早些时候我按照无用户访问的步骤进行了工作:https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service我无法通过该页面上的简单令牌HTTP请求:未经授权。

这是我最近的努力。欢迎任何帮助:

using Microsoft.Graph;
using Microsoft.Identity.Client;

public static async Task<string> GetTokenForAppAsync()
{
    if (TokenForApplication == null || TokenForApplicationExpiration <= DateTimeOffset.UtcNow.AddMinutes(5))
    {
        TokenCacheUser = null;
        TokenCacheApplication = null;

        ConfidentialClientApplication cl = new ConfidentialClientApplication(Settings.AuthClientId,
            returnUrl,
            new ClientCredential(Settings.AuthClientSecret),
            TokenCacheUser,
            TokenCacheApplication);

        AuthenticationResult authResult = cl.AcquireTokenForClientAsync(new string[] { "https://graph.microsoft.com/.default" }, true).Result;
        TokenForApplication = authResult.AccessToken;
        Console.WriteLine(authResult.AccessToken);
    }
    return TokenForApplication;
}

我对使用MS Graph和Identity Libs的任何解决方案持开放态度。

1 个答案:

答案 0 :(得分:0)

graph_authentication_example

这是控制台应用程序基于令牌的身份验证的示例。应用程序必须至少运行一次,系统将提示您登录,但一旦完成,将在运行应用程序的计算机上存储身份验证令牌。

我从我们的服务器运行一个控制台应用程序作为任务,并使用Graph端点访问Graph API以获取各种ActiveDirectory数据集 - 通常我需要在发布后登录然后再运行 - 这是在测试阶段现在但似乎运作良好。

依赖关系:

  • 必须具有Azure Active Directory用户,该用户将用于登录和后续身份验证。一切都发生在这个用户的背景下。
  • 使用以下Nuget包:
    • Microsoft.Graph&gt; = v1.6.2
    • Microsoft.Graph.Core&gt; = v1.6.2
    • Microsoft.Identity.Client&gt; = v1.1.0预览
    • Microsoft.IdentityModel.Clients.ActiveDirectory&gt; = v3.17.1
    • Newtonsoft.Json&gt; = v1.0.3
    • System.Net.Http&gt; = v4.3.3
    • System.Security.Cryptography.Algorithms&gt; = v4.3.0
    • System.Security.Cryptography.Encoding&gt; = v4.3.0
    • System.Security.Cryptography.Primitives&gt; = v4.3.0
  • 您必须在此处[https://apps.dev.microsoft.com/]创建一个应用程序,并在上面创建相同的用户,这将为您提供您的客户/应用ID。
  • 您可以在此处[https://developer.microsoft.com/en-us/graph/graph-explorer/]查看有效的图表,并使用上面创建的用户登录,以针对您自己的Azure Active Directory进行测试。

设置

我使用.ini文件存储设置,但值是有效的,.ini样式的注释 - 请注意某些条目中有{name}样式文本,用于替换字符串。

您将看到Settings.SomeName - 映射到以下内容:

[Endpoint] ; we don't want v1.0 because of our needs but it is valid GraphVersion = beta ; v1.0 or beta ; Common Graph endpoint - we sub version GraphEndpoint = https://graph.microsoft.com/{version} [Auth] ; authentication uri Uri = https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token ; authority uri Authority = https://login.microsoftonline.com/{tenant} ; if we need to login or re-login RedirectUri = https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fnativeclient ; you may have a GUID style tenant but 'common' worked fine since it auth's back to Azure anyway Tenant = common ; the scopes we needed with Graph, yours may vary Scopes = { User.ReadBasic.All, User.Read.All, User.ReadWrite.All, Directory.AccessAsUser.All, Directory.Read.All, Directory.ReadWrite.All, Group.ReadWrite.All } ; the id of your azure application - guid ClientId = xxxx###-2##8-4##9-b##1-ec#########8f2 GrantType = client_credentials

Code Snippets

我尝试包含完整的功能,并通过指示它们来自哪些文件来指示分离。这些是令牌认证的主要部分 - 主要是代码完成,但“私有”代码除外。

起点:让我们说在我的Program.cs中我调用了以下函数,所有内容都从这里开始进行身份验证:

// we are just getting a group by id - CreateAuthenticatedClient() is called before every call to Graph
public static async Task<Group> GetGroupAsync(string groupId)
{
    var graphClient = AuthenticationHelper.CreateAuthenticatedClient();

    try
    {
        var group = await graphClient.Groups[groupId].Request().GetAsync();
        if (group == null) return null;
        return group;
    }
    catch (ServiceException e)
    {
        ConsoleHelper.WriteException($"GetGroupAsync.{ServiceErrorString(e)}");
        return null;
    }
}

AuthenticationHelper.cs - 完整的课程

public class AuthenticationHelper
{
    public static string TokenForUser = null;
    public static DateTimeOffset TokenForUserExpiration;

    // this is the 'magic' where we get the user cache
    public static PublicClientApplication IdentityClientApp = new PublicClientApplication(Settings.AuthClientId, Settings.AuthAuthority, TokenCacheHelper.GetUserCache());

    private static GraphServiceClient graphClient = null;

    // Get an access token for the given context and resourceId. An attempt is first made to 
    // acquire the token silently. If that fails, then we try to acquire the token by prompting the user.
    public static GraphServiceClient CreateAuthenticatedClient()
    {
        if (graphClient == null)
        {
            try
            {
                graphClient = new GraphServiceClient(
                    Settings.GraphEndpoint,
                    new DelegateAuthenticationProvider(
                        async (requestMessage) =>
                        {
                            var token = await GetTokenForUserAsync();
                            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
                            requestMessage.Headers.Add("azure-graph-test", "manage group membership");
                        }));
                return graphClient;
            }

            catch (ServiceException sex)
            {
                Console.WriteLine($"Could not create a graph client service: {sex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Could not create a graph client: {ex.Message}");
            }
        }
        return graphClient;
    }

    /// <summary>
    ///     get Token for User
    /// </summary>
    public static async Task<string> GetTokenForUserAsync()
    {
        if (TokenForUser == null || TokenForUserExpiration <= DateTimeOffset.UtcNow.AddMinutes(5))
        {
            AuthenticationResult authResult;
            try
            {
                authResult = await IdentityClientApp.AcquireTokenSilentAsync(Settings.AuthScopes, IdentityClientApp.Users.FirstOrDefault());
                TokenForUser = authResult.AccessToken;
            }
            catch (Exception)
            {
                if (TokenForUser == null || TokenForUserExpiration <= DateTimeOffset.UtcNow.AddMinutes(5))
                {
                    authResult = await IdentityClientApp.AcquireTokenAsync(Settings.AuthScopes);
                    TokenForUser = authResult.AccessToken;
                    TokenForUserExpiration = authResult.ExpiresOn;
                }
            }
        }

        return TokenForUser;
    }
}

TokenCacheHelper.cs - 这是获取/设置令牌缓存的关键的微软类

// Copyright (c) Microsoft Corporation.
// All rights reserved.
// This code is licensed under the MIT License.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

static class TokenCacheHelper
{

    /// <summary>
    /// Get the user token cache
    /// </summary>
    public static TokenCache GetUserCache()
    {
        if (usertokenCache == null)
        {
            usertokenCache = new TokenCache();
            usertokenCache.SetBeforeAccess(BeforeAccessNotification);
            usertokenCache.SetAfterAccess(AfterAccessNotification);
        }
        return usertokenCache;
    }

    static TokenCache usertokenCache;

    /// <summary>
    /// Path to the token cache
    /// </summary>
    public static string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + "msalcache.txt";
    private static readonly object FileLock = new object();

    public static void BeforeAccessNotification(TokenCacheNotificationArgs args)
    {
        lock (FileLock)
        {
            args.TokenCache.Deserialize(File.Exists(CacheFilePath)
                ? File.ReadAllBytes(CacheFilePath)
                : null);
        }
    }

    public static void AfterAccessNotification(TokenCacheNotificationArgs args)
    {
        // if the access operation resulted in a cache update
        if (args.TokenCache.HasStateChanged)
        {
            lock (FileLock)
            {
                // reflect changes in the persistent store
                File.WriteAllBytes(CacheFilePath, args.TokenCache.Serialize());
                // once the write operationtakes place restore the HasStateChanged bit to filse
                args.TokenCache.HasStateChanged = false;
            }
        }
    }
}