3

Basically I'm implementing an SSO for an employee portal that I'm making for us, but I'd also like to be able to access the graph API (At the least, the AzureAD REST API items, like adding/removing/getting users information) without having to be signed in through an SSO.

This way, I can use what I'm thinking to be some kind of API key/secret sort of setup and schedule cron jobs that interact with the AD in some way. Technically I could signin and set my account to be the one running this, but that seems kind of hacky and unreliable (as in, if something happens to my account, password expires, changes, etc... then the refresh token will no longer be valid and I'll have to sign in again and the task could be temporarily broken.)

I could have sworn I've seen documentation for this somewhere when I was looking into implementing this a few months ago, but I can't for the life of me find it now.

Really hope this isn't a duplicate, I just can't think of wording to search for that doesn't keep coming up with SSO-based API information.

Update - Alright, it looks like I figured this out (with the help of Shaun Luttin's answer posted below: https://stackoverflow.com/a/32618417/3721165 [link for convenience])

So, all of the info Shaun brought together was really helpful. Initially the docs were fairly confusing due to how they word things, as well as the complexity of some of their examples. But once I got to digging through some of the examples, plus some of the info Shaun provided, and some experimenting/research on my own, I was able to come up with this (basic demo/concept):

using System;
using System.Globalization;
using System.Threading.Tasks;

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Azure.ActiveDirectory.GraphClient;


namespace AzureADGraphApi
{
    class Program
    {
        private static string tenant = "...tenant id...";
        private static string clientid = "...client id...";
        private static string appkey = "...app key...";
        private static string aadinstance = "https://login.microsoftonline.com/{0}";
        private static string graphResourceUrl = "https://graph.windows.net";

        static void Main(string[] args)
        {
            Uri serviceRoot = new Uri(graphResourceUrl + "/" + tenant);
            ActiveDirectoryClient adc = new ActiveDirectoryClient(serviceRoot, async () => await GetToken());

            IPagedCollection<IUser> Users = adc.Users.ExecuteAsync().Result;

            bool pagesLeft = false;
            do
            {
                foreach (IUser user in Users.CurrentPage)
                {
                    Console.WriteLine(user.DisplayName);
                }
                pagesLeft = Users.MorePagesAvailable;
                Users = Users.GetNextPageAsync().Result;
                Console.WriteLine("--- Page Break ---");
            } while (pagesLeft);
            Console.ReadLine();
        }

        private static async Task<string> GetToken()
        {
            AuthenticationContext authContext = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, aadinstance, tenant));
            AuthenticationResult result = authContext.AcquireToken(graphResourceUrl, new ClientCredential(clientid, appkey));
            return result.AccessToken;
        }
    }
}

I found through further research that in order to use the Graph API the way I'm intending, you have to provide the graph resource URL (https://graph.windows.net) to the AquireToken method, instead of your app ID/URL.

So, I'm accepting Shaun's answer, but I also wanted to give my working result of that answer.

Thanks for the help, guys!

4

2 回答 2

16

您需要的是无需进行单点登录的访问令牌。Rick Rainey 提供的链接将帮助您入门。

您可以通过调用以编程方式获取访问令牌AcquireTokenAsyncAcquireTokenAsync根据您构建的应用程序的类型以及您希望如何进行身份验证,有很多方法可以调用。您将需要决定是创建本机客户端应用程序还是 Web 应用程序。

Web 应用程序或本机客户端应用程序

根据应用程序类型,身份验证的方式会有很大差异。听起来您想在没有用户提示的情况下进行身份验证(即不要求输入用户名/密码。)以下内容对我有用,根本不涉及用户提示。它们中的每一个都使用以下常量,您需要从 manage.windowsazure.com 门户获取这些常量。如果您需要帮助找到其中任何一个,请在评论中告诉我。

private const string

    DIRECTORY_TENANT_NAME = "mytenant.onmicrosoft.com",
    DIRECTORY_TENANT_ID = "xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx",
    RESOURCE_URL = "https://graph.windows.net",
    SUBSCRIPTION_ID = "xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx",
    AUTHORITY = "https://login.microsoftonline.com/" + DIRECTORY_TENANT_NAME,

    // web application
    CLIENT_ID_WEB = "xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx",
    CLIENT_SECRET_WEB = "xxxxxxxx/xxxxxxxxxxxx/xxxxxxxx/xxxxxxxx=",

    // native client application
    CLIENT_ID_NATIVE = "xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxx",

    // adminstrator
    USER_NAME = "myuser@mytenant.onmicrosoft.com",
    USER_PASSWORD = "xxxxxxxxxx";

现在,您可以使用以下三个选项(尽管可能还有其他选项)来获取令牌。我已经测试了以下各项,只要您正确配置了 Azure Active Directory 租户、用户和应用程序,它们都可以正常工作。

AcquireToken(string resource, ClientAssertionCertificate clientCertificate)适用于 Web 应用程序。这种方法的缺点是初始设置比较困难(PowerShell、证书创建)。优点是一旦设置就很容易使用。对于本机客户端应用程序,它不起作用并引发此错误:AADSTS50012: Client is public so a 'client_assertion' should not be presented.

var authContext = new AuthenticationContext(AUTHORITY);
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certs = store
    .Certificates
    .Find(X509FindType.FindByIssuerName, "mvp2015", false);
var clientCertificate = new ClientAssertionCertificate(CLIENT_ID_WEB, certs[0]);
var result = authContext
    .AcquireTokenAsync(RESOURCE, clientCertificate)
    .Result;

AcquireToken(string resource, ClientCredential clientCredential)适用于 Web 应用程序。它更容易设置,并且一旦设置就易于使用。本机客户端应用程序不支持这种方法,因为它们缺少客户端密码。

var authContext = new AuthenticationContext(AUTHORITY);
var clientCredential = new ClientCredential(CLIENT_ID_WEB, CLIENT_SECRET_WEB);
var result = authContext
    .AcquireTokenAsync(RESOURCE, clientCredential)
    .Result;

AcquireToken(string resource, string clientId, UserCredential userCredential)适用于本机客户端应用程序。Web 应用程序因此错误而失败:AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'.一个问题是您登录的用户必须在您的 Active Directory 中正确配置。

var authContext = new AuthenticationContext(AUTHORITY);
var userCredential = new UserCredential(USER_NAME, USER_PASSWORD);
var result = authContext
    .AcquireTokenAsync(RESOURCE, CLIENT_ID_NATIVE, userCredential)
    .Result;

使用首选方法获得令牌后,您就可以像这样使用 Active Directory Graph。为了便于阅读,Func您可以将其accessTokenGetter放入自己的方法中。

Func<Task<string>> accessTokenGetter = async () =>
{
    var authContext = new AuthenticationContext(AUTHORITY, false);
    var clientCredential = new ClientCredential(CLIENT_ID_WEB, CLIENT_SECRET_WEB);
    var result = await authContext
        .AcquireTokenAsync(RESOURCE_URL, clientCredential);
    var token = result.AccessToken;
    return token;
};

var uriRoot = new Uri(RESOURCE_URL);
var uriTenant = new Uri(uriRoot, DIRECTORY_TENANT_ID);
var client = new ActiveDirectoryClient(uriTenant, accessTokenGetter);
foreach (var u in client.Users.ExecuteAsync().Result.CurrentPage)
{
    var n = u.DisplayName;
}

评论

  • DIRECTORY_TENANT_ID我们在 Azure Active Directory 租户的仪表板中时,它位于 Web 浏览器的地址栏中。manage.windowsazure.com > Active Directory > 一些租户。
  • 在 Azure Active Directory 中创建应用程序后,我们需要配置其权限。这是我们应用程序配置选项卡的底部。manage.windowsazure.com > Active Directory > 某些租户 > 应用程序 > 某些应用程序 > 配置。
  • 上面的演示使用了两个 NuGet 包:
    • Microsoft.Azure.ActiveDirectory.GraphClient版本 2.1.0
    • Microsoft.IdentityModel.Clients.ActiveDirectory版本 2.19.208020213

也可以看看

这里有一个例子:https ://github.com/AzureADSamples/ConsoleApp-GraphAPI-DotNet

于 2015-09-16T20:49:50.150 回答
0

Azure AD服务主体是我相信您所追求的。这是有关如何为您的应用创建一个的文档。

https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-service-principal-portal/

于 2015-09-16T20:44:58.003 回答