Skip to content

Instantly share code, notes, and snippets.

@rytmis
Created December 1, 2012 14:04
Show Gist options
  • Save rytmis/4182440 to your computer and use it in GitHub Desktop.
Save rytmis/4182440 to your computer and use it in GitHub Desktop.
Windows Azure AD Authentication: Querying the Graph API
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.WindowsAzure.ActiveDirectory.Authentication;
using WAADDemo.ActiveDirectoryGraph.Microsoft.WindowsAzure.ActiveDirectory;
namespace WAADDemo
{
/// <summary>
/// This class uses the Windows Azure Graph API to retrieve a given user's direct group memberships.
/// Except for the <see cref="GetUserGroups"/> method, the code in this class is heavily based on public
/// Graph API examples from Microsoft.
/// </summary>
public class GraphClient
{
private readonly DirectoryDataService dataService;
private readonly string tenantDomainName;
private readonly string servicePrincipalSymmetricKey;
private readonly string tenantId;
private readonly string appPrincipalId;
// ReSharper disable InconsistentNaming
/// <summary>
/// Name of the HTTP header that contains the unique ID of a client request.
/// Used when making Graph API requests
/// </summary>
private const string HeaderName_ClientRequestId = "client-request-id";
/// <summary>
/// Authorization header used when making Graph API requests
/// </summary>
private const string HeaderName_Authorization = "Authorization";
/// <summary>
/// Version of the data contract, used when making Graph API requests
/// </summary>
private const string HeaderName_DataContractVersion = "x-ms-dirapi-data-contract-version";
// ReSharper restore InconsistentNaming
private const string DataContractVersion = "0.8";
public GraphClient(string tenantDomainName, string tenantId, string appPrincipalId, string servicePrincipalSymmetricKey)
{
this.tenantDomainName = tenantDomainName;
this.tenantId = tenantId;
this.servicePrincipalSymmetricKey = servicePrincipalSymmetricKey;
this.appPrincipalId = appPrincipalId;
dataService = new DirectoryDataService(GetConnectionUri());
dataService.SendingRequest += (sender, args) =>
{
var webRequest = ((HttpWebRequest)args.Request);
webRequest.Headers.Add(HeaderName_Authorization, GetAuthorizationHeader());
webRequest.Headers.Add(HeaderName_DataContractVersion, DataContractVersion);
webRequest.Headers.Add(HeaderName_ClientRequestId, Guid.NewGuid().ToString());
};
}
/// <summary>
/// Identifier of the issuing resource for the symmetric key.
/// Used for getting an access token for requests.
/// Looks something like this:
/// A4423C7C-35F7-4679-99F8-5921A0482B63@tenantdomain.onmicrosoft.com
/// (the GUID here is an example, not the actual value)
/// </summary>
private string GetSymmetricKeyIssuingResource()
{
return appPrincipalId + "@" + tenantDomainName;
}
/// <summary>
/// Realm identifier for the service we want to access. Which is the tenant domain
/// hosted at Windows Azure AD.
/// 00000002-0000-0000-c000-000000000000/graph.windows.net@yourtenantdomain.onmicrosoft.com
/// (the GUID here is the actual identifier)
/// </summary>
private string GetServiceRealm()
{
return AzureAdPrincipalId + "/" + GraphApiServiceHost + "@" + tenantDomainName;
}
/// <summary>
/// URI of the Graph API we're accessing.
/// Looks something like this:
/// https://graph.windows.net/EA314D90-8751-4534-A96E-77A5DF0E72AB
/// (the GUID here is an example, not the actual value)
/// </summary>
private Uri GetConnectionUri()
{
return new Uri(string.Format(@"https://{0}/{1}", GraphApiServiceHost, tenantId));
}
/// <summary>
/// URI of the WAAD tenant domain.
/// Looks something like this:
/// https://accounts.accesscontrol.windows.net/yourtenantdomain.onmicrosoft.com
/// </summary>
private string GetFullTenantUri()
{
return "https://accounts.accesscontrol.windows.net/" + tenantDomainName;
}
/// <summary>
/// This is the known identifier of the WAAD service principal
/// </summary>
private const string AzureAdPrincipalId = "00000002-0000-0000-c000-000000000000";
/// <summary>
/// This is the domain name of the WAAD Graph API service
/// </summary>
private const string GraphApiServiceHost = "graph.windows.net";
/// <summary>
/// Method to get the Oauth2 Authorization header from WAAD. This basically authenticates
/// the calling application to WAAD and gets a token with which further requests can be made.
/// </summary>
private string GetAuthorizationHeader()
{
var authContext = new AuthenticationContext(GetFullTenantUri());
var credential = new SymmetricKeyCredential(GetSymmetricKeyIssuingResource(), Convert.FromBase64String(servicePrincipalSymmetricKey));
AssertionCredential assertionCredential = authContext.AcquireToken(GetServiceRealm(), credential);
return assertionCredential.CreateAuthorizationHeader();
}
/// <summary>
/// Queries the Graph API for security groups in which an active user with the given
/// <paramref name="userPrincipalName"/> is a direct member.
/// </summary>
public IList<Group> GetUserGroups(string userPrincipalName)
{
var user = dataService.Users
.Where(u => u.AccountEnabled == true && u.UserPrincipalName == userPrincipalName)
.AsEnumerable()
.SingleOrDefault();
var groupReferences = dataService.LoadProperty(user, "MemberOf")
.OfType<ReferencedObject>()
.Where(r => r.ObjectType == "Group")
.Select(g => g.ObjectId);
return dataService.Groups
.AsEnumerable()
.Where(g => groupReferences.Contains(g.ObjectId))
.ToList();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment