Thursday, 19 February 2015

Creating Your Own Helper for the Azure AD Graph API

If you want to implement Azure AD in an Enterprise Solution, you will probably looking for of adding extensions (attributes) to users, so you can add things like Company Name or Twitter Account. I agree you could save that into a nice table in a Database, but why do you need to do that if you can use Azure AD as a database to store all these details.

This technique will eliminate dependencies and will make the application more scalable. At the end of the day, Azure AD is a black box we can’t “invade” but we can can manipulate.

In this post you will see few things which you will need in order to make the whole Azure AD something useful, so things like:

  • Adding/Removing Extensions.
  • Override the claims so we can extract the extensions.

The first thing we are going to do it is to Create an out-of-the-box MVC application which will include our UX to perform the actions. Below there we will create our helper with the actions and models.

So let’s go to start… Add a new MVC project, and click OK. Then click on Change Authentication:
image 

Select Organizational Accounts and in the Domain box enter your tenant, it could be somekindofcompany.onmicrosoft.com (If you don’t have a domain you will need to create one on Azure, this is a nice post if you don’t have a clue). Select “Sign on read and write …” at the end of the day you are going to be manipulating the Azure AD tenant, so you need permissions. You the will be promtend to login, just login with the admin account, and go for it!, click ok.
 image

Now! if you run the application you will be prompted with the login screen, just login and you will see the default page. At this stage, if you manage to login, you will realize that you have a nice little token to perform RESTFul operations with you Application. This is what you should get after doing the Login.
image

Ok! So far so good…

Let’s go to add a project in the solution. This project will be the one in charge of keeping the Models for our Application and doing the Restful operations. It will not care about the tenant, clientid etc… we will be passing that. So Ok, Add a new project to the solution and call it AADGraphAPIManager, when you finish remove Class1.cs. This is how our solution should look like:
image

Now we are going to create two folders in the AADGraphAPIManager project, one it will be called Models and the other called Services.
Under Models we are going to add a class called AADGraphApiModel.cs
image

Inside this class let’s go to copy the model from the Graph API version 1.5, I assume that in few months time we will have a new version. Okp, don’t you worry apparently Microsoft is going to be supporting all the versions for a while. (the code is below)

 

using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel;

namespace AADGraphAPIManager.Models
{
public class UserContext
{
[JsonProperty(
"odata.metadata")]
public string userContext;

[JsonProperty(
"value")]
public List<UserDetails> value;
}

public class UserDetails
{
[JsonProperty(
"objectId")]
public string objectId { get; set; }

[DisplayName(
"Display Name")]
public string displayName { get; set; }
[DisplayName(
"Given Name")]
public string givenName { get; set; }
[DisplayName(
"Surname")]
public string surname { get; set; }
[DisplayName(
"Job Title")]
public string jobTitle { get; set; }
[DisplayName(
"Department")]
public string department { get; set; }
[DisplayName(
"Mobile")]
public string mobile { get; set; }
[DisplayName(
"City")]
public string city { get; set; }
[DisplayName(
"Street Address")]
public string streetAddress { get; set; }
[DisplayName(
"Country")]
public string country { get; set; }
[DisplayName(
"Postal Code")]
public string postalCode { get; set; }
[DisplayName(
"Phone Number")]
public string telephoneNumber { get; set; }
[DisplayName(
"Email Address")]
public string mail { get; set; }
[DisplayName(
"UPN")]
public string userPrincipalName { get; set; }
[DisplayName(
"Last DirSync")]
public string lastDirSyncTime { get; set; }

//## THIS ONES ARE NEW ONES ... SO EXTENSIONS
[DisplayName("Client Id")]
public string ClientId { get; set; }
[DisplayName(
"Client Name")]
public string ClientName { get; set; }
[DisplayName(
"Client Address")]
public string ClientAddress { get; set; }
[DisplayName(
"Client Post Code")]
public string ClientPostCode { get; set; }
[DisplayName(
"Client Country")]
public string ClientCountry { get; set; }
}

public class AppContext
{
[JsonProperty(
"odata.metadata")]
public string metadata { get; set; }
public List<AppDetails> value { get; set; }
}

public class AppDetails
{
public string objectId { get; set; }
public string appId { get; set; }
}

public class ExtensionPropertiesContext
{
[JsonProperty(
"odata.metadata")]
public string metadata { get; set; }
public List<ExtensionProperty> value { get; set; }
}

public class ExtensionProperty
{
public string objectId { get; set; }
public string objectType { get; set; }
public string name { get; set; }
public string dataType { get; set; }
[JsonProperty(
"odata.metadata")]
public string odataMetadata { get; set; }
[JsonProperty(
"odata.type")]
public string odataType { get; set; }
public List<string> targetObjects { get; set; }
}
}


As you probably noticed, JsonProperty is highlighted, that is because we need to a add Newtonsoft Json Package to recognise the attributes. You can use the windows ones, but you get use to Newtonsoft I suspect. Ok in order to add the Nuget package go to your package console, select this project and type:
Install-Package Newtonsoft.Json
It should be something like this:
image 


Let’s go to add under our folder “Services” a new class called GraphAPIQuery.cs and add the code below.
You will need to install the following Nuget packages under your project


Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory


 



using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using AADGraphAPIManager.Authentication;
using AADGraphAPIManager.Authentication.ExtensionMethods;
using AADGraphAPIManager.Models;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace AADGraphAPIManager.Services
{
public class GraphAPIQuery : IGraphAPIQuery
{
private string _graphUrl; //## https://graph.windows.net
private string _apiVersion; //## 1.5
private string _tenantIdClaimType; //## http://schemas.microsoft.com/identity/claims/tenantid
private string _LoginUrl; //## https://login.windows.net/{0}
private string _appPrincipalId; //## ConfigurationManager.AppSettings["ida:ClientID"];
private string _appKey; //## ConfigurationManager.AppSettings["ida:Password"];
private string _tenantId;
private string _appObjectId;

private ClaimsPrincipal _claim;

private AzureADAuthentication _aadAuthentication;

#region [ Enums ]
public enum ExtensionTarget {
User,
Group,
TenantDetail,
Application,
ServicePrincipal
}
#endregion

#region [ Properties ]
public bool IsGraphApiHasBeenInitializedProperly { get; set; }
public string ApiVersion
{
get { return _apiVersion; }
set { _apiVersion = value; }
}
public ClaimsPrincipal Claim
{
get { return _claim; }
}
#endregion

#region [ Constructor ]
public GraphAPIQuery(string AppPrincipalId, string AppKey, ClaimsPrincipal claim)
{
this.IsGraphApiHasBeenInitializedProperly = false;

try
{
//## DEFAULT Microsoft Azure AD Values
_graphUrl = @"https://graph.windows.net";
_tenantIdClaimType
= @"http://schemas.microsoft.com/identity/claims/tenantid";
_apiVersion
= "api-version=1.5";

//## Other vars
_claim = claim;
_tenantId
= _claim.FindFirst(_tenantIdClaimType).Value;
_LoginUrl
= String.Format(CultureInfo.InvariantCulture, "https://login.windows.net/{0}", _tenantId);
_appPrincipalId
= AppPrincipalId;
_appKey
= AppKey;

//## INITIALIZING AADAUTH
_aadAuthentication = new AzureADAuthentication();

//## GET AUTHENTICATION RESULT
_aadAuthentication.GetAuthenticationResult(_graphUrl, _tenantId, _LoginUrl, _appPrincipalId, _appKey);

//## GETTING THE APPLICATION OBJECT ID
_appObjectId = this.GetAppObjectId();

this.IsGraphApiHasBeenInitializedProperly = true; ;
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
}
}
#endregion

#region [ Methods ]

#region [ Public Methods ]
public UserDetails GetUserByObjectId(string userId)
{
try
{

// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/users/" + userId + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
string responseString = reader.ReadToEnd();
UserDetails getUser
= JsonConvert.DeserializeObject<UserDetails>(responseString);

return getUser;
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}

public UserDetails GetUserByName(string name)
{
try
{
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/users/" + name + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
string responseString = reader.ReadToEnd();
UserDetails getUser
= JsonConvert.DeserializeObject<UserDetails>(responseString);

return getUser;
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}

public AadGroups GetGroups()
{
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

if (this._aadAuthentication.AadAuthenticationResult == null)
return null;

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId +"/groups" + "?" + this.ApiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.StatusDescription));
else
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
string responseString = reader.ReadToEnd();
AadGroups groupList
= JsonConvert.DeserializeObject<AadGroups>(responseString);

return groupList;
}
}
}

catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}

public IList<object> GetUserGroups(string name)
{
//AadGroups groups = null;
IList<object> groups = new List<object>();
try
{
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/users/" + name + "/memberOf" +"?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
string responseString = reader.ReadToEnd();
dynamic _groups
= JsonConvert.DeserializeObject<dynamic>(responseString);
foreach (object item in _groups.value)
{
groups.Add(item);
}
Newtonsoft.Json.Linq.JObject groups1
= Newtonsoft.Json.Linq.JObject.Parse(responseString);

}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}

return groups;
}

private ExtensionRepresentation CreateExtension(ExtensionRepresentation extension)
{
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this.aadAuthentication.AadAuthenticationResult = this.aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

if (this._aadAuthentication.AadAuthenticationResult == null)
return null;

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/applications/" + this._appObjectId + "/extensionProperties" + "?" + this.ApiVersion;

JsonSerializerSettings jsonSettings
= new JsonSerializerSettings();
jsonSettings.NullValueHandling
= NullValueHandling.Ignore;
jsonSettings.DefaultValueHandling
= DefaultValueHandling.Ignore;
JsonSerializer serializer
= JsonSerializer.CreateDefault(jsonSettings);
string body = JsonConvert.SerializeObject(JObject.FromObject(extension, serializer), Formatting.None, jsonSettings);

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "POST";
request.ContentType
= "application/json";
byte[] data = encoding.GetBytes(body);
request.ContentLength
= data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data,
0, data.Length);
}

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.Created)
{
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.StatusDescription));
}
else
using (var stream = response.GetResponseStream())
{
string payload;
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
{
payload
= reader.ReadToEnd();
}

return JObject.Parse(payload).ToObject<ExtensionRepresentation>();
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}

public AadGroup CreateNewGroup(string groupName)
{
AadGroup groupObject;

GroupCreationModel group
= new GroupCreationModel();
group.displayName
= groupName;
group.mailNickname
= groupName;
group.mailEnabled
="false";
group.securityEnabled
= "true";


if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this.aadAuthentication.AadAuthenticationResult = this.aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

if (this._aadAuthentication.AadAuthenticationResult == null)
return null;

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/groups" + "?" + this.ApiVersion;

JsonSerializerSettings jsonSettings
= new JsonSerializerSettings();
jsonSettings.NullValueHandling
= NullValueHandling.Ignore;
jsonSettings.DefaultValueHandling
= DefaultValueHandling.Ignore;
JsonSerializer serializer
= JsonSerializer.CreateDefault(jsonSettings);
string body = JsonConvert.SerializeObject(JObject.FromObject(group, serializer), Formatting.None, jsonSettings);

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "POST";
request.ContentType
= "application/json";
byte[] data = encoding.GetBytes(body);
request.ContentLength
= data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data,
0, data.Length);
}

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.Created)
{
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.StatusDescription));
}
else
using (var stream = response.GetResponseStream())
{
string payload;
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
{
payload
= reader.ReadToEnd();
groupObject
= JsonConvert.DeserializeObject<AadGroup>(payload);
}
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}

return groupObject;
}

public bool RegisterNewExtension(string strExtension, ExtensionTarget extensionTarget)
{
string target = "";

if (CheckExtensionRegistered(strExtension) == null)
{
// setup the extension definition
ExtensionRepresentation extension = new ExtensionRepresentation();
extension.name
= strExtension;
extension.dataType
= "String";
switch (extensionTarget)
{
case ExtensionTarget.User: target = "User"; break;
case ExtensionTarget.Group: target = "Group"; break;
case ExtensionTarget.TenantDetail: target = "TenantDetail"; break;
case ExtensionTarget.Application: target = "Application"; break;
case ExtensionTarget.ServicePrincipal: target = "ServicePrincipal"; break;
default: target = "User"; break;
}
extension.targetObjects.Add(target);

// Execute the POST to create new extension
ExtensionRepresentation returnedExtension = this.CreateExtension(extension);
return returnedExtension != null;
}
else
return true;
}

public bool UnRegisterExtension(string strExtension)
{
string extensionObjectId = this.CheckExtensionRegisteredAndReturningExtensionObjectId(strExtension);

if (extensionObjectId != null)
{
return this.RemoveExtensionRegistered(extensionObjectId);
}
else
return true;
}

public bool RegisterClientExtensionsInGroups()
{
bool result = false;

if (RegisterNewExtension("ClientId", AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget.Group))
{
if (RegisterNewExtension("ClientName", AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget.Group))
{
if (RegisterNewExtension("ClientAddress", AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget.Group))
{
if (RegisterNewExtension("ClientPostCode", AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget.Group))
{
if (RegisterNewExtension("ClientCountry", AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget.Group))
{
result
= true;
}
}
}
}
}

return result;
}
#endregion

#region [ Private Methods ]
public string CheckExtensionRegistered(string extensionName)
{
string responseString = string.Empty;
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/applications/" + this._appObjectId + "/extensionProperties" + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
responseString
= reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}

//##
var extensionproperties = JsonConvert.DeserializeObject<ExtensionPropertiesContext>(responseString);

if (extensionproperties.value.Count == 0)
return null;
else
{
var extensions
= extensionproperties.value;
for (int i = 0; i < extensionproperties.value.Count; i++)
{
if (extensionproperties.value[i].name.Contains(extensionName))
return extensionproperties.value[i].name;
}
}

return null;
}

private string CheckExtensionRegisteredAndReturningExtensionObjectId(string extensionName)
{
string responseString = string.Empty;
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/applications/" + this._appObjectId + "/extensionProperties" + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
responseString
= reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}

//##
var extensionproperties = JsonConvert.DeserializeObject<ExtensionPropertiesContext>(responseString);

if (extensionproperties.value.Count == 0)
return null;
else
{
var extensions
= extensionproperties.value;
for (int i = 0; i < extensionproperties.value.Count; i++)
{
if (extensionproperties.value[i].name.Contains(extensionName))
return extensionproperties.value[i].objectId;
}
}

return null;
}

private bool RemoveExtensionRegistered(string extensionObjectId)
{
string responseString = string.Empty;
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/applications/" + this._appObjectId + "/extensionProperties/" + extensionObjectId + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();
request.Headers.Add(authnHeader);
request.Method
= "DELETE";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.NoContent)
{
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.StatusDescription));

return false;
}
else
{
return true;
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return false;
}

//##
}

public bool SetExtensionValue(string entityName, string extensionName, string extensionValue, ExtensionTarget extensionTarget)
{
string target = string.Empty;

switch (extensionTarget)
{
case ExtensionTarget.User: target = "users"; break;
case ExtensionTarget.Group: target = "groups"; break;
case ExtensionTarget.TenantDetail: target = "tenantdetails"; break;
case ExtensionTarget.Application: target = "applications"; break;
case ExtensionTarget.ServicePrincipal: target = "servicePrincipals"; break;
default: target = "users"; break;
}
////Get the objectId for this particular app
//string requestUrl = String.Format(
// CultureInfo.InvariantCulture,
// GraphExtensionValueUrl,
// HttpUtility.UrlEncode(tenantId),
// HttpUtility.UrlEncode(upn));

//HttpClient client = new HttpClient();
//client.DefaultRequestHeaders.ExpectContinue = false;
////PATCH isn't a default method
//HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUrl);
//request.Headers.TryAddWithoutValidation("Authorization", authHeader);

//string extensionProperty = "{\"" + extensionName + "\":\"" + extensionValue + "\"}";

//request.Content = new StringContent(extensionProperty, System.Text.Encoding.UTF8, "application/json");

//HttpResponseMessage response = await client.SendAsync(request);
//string responseString = await response.Content.ReadAsStringAsync();
//if (response.StatusCode == HttpStatusCode.NoContent)
//{
// return true;
//}
//else
// return false;

//##
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/" + target + "/" + entityName + "" + "?" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

string body = "{\"" + extensionName + "\":\"" + extensionValue + "\"}";

request.Headers.Add(authnHeader);
request.Method
= "PATCH";
request.ContentType
= "application/json";
byte[] data = encoding.GetBytes(body);
request.ContentLength
= data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data,
0, data.Length);
}

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.NoContent)
return true;
else
return false;
}
}

catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return false;
}
}

public bool CheckIfGroupExists(string groupName)
{
string responseString = string.Empty;
AadGroups groupList;
// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/groups?$filter=displayName eq '" + groupName+"'"+ "&" + this._apiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
responseString
= reader.ReadToEnd();
groupList
= JsonConvert.DeserializeObject<AadGroups>(responseString);
}
}
}
catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return false;
}

return groupList.group.Count>0?true:false;
}

private string GetAppObjectId()
{
string appObjectId = string.Empty;

// check if token is expired or about to expire in 2 minutes
if (this._aadAuthentication.AadAuthenticationResult.IsExpired() || this._aadAuthentication.AadAuthenticationResult.WillExpireIn(2))
{
//this._aadAuthentication.AadAuthenticationResult = this._aadAuthentication.GetNewAuthenticationResult(ref strErrors);
}

if (this._aadAuthentication.AadAuthenticationResult == null)
return null;

string authnHeader = "Authorization: " + this._aadAuthentication.AadAuthenticationResult.AccessToken;

string uri = this._graphUrl + "/" + this._tenantId + "/applications" + "?" + this.ApiVersion;

try
{
HttpWebRequest request
= (HttpWebRequest)WebRequest.Create(uri);
System.Text.ASCIIEncoding encoding
= new System.Text.ASCIIEncoding();

request.Headers.Add(authnHeader);
request.Method
= "GET";

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(String.Format(
"Server error (HTTP {0}: {1}).",
response.StatusCode,
response.StatusDescription));
else
using (var stream = response.GetResponseStream())
{
StreamReader reader
= new StreamReader(stream);
string responseString = reader.ReadToEnd();
AppContext appContext
= JsonConvert.DeserializeObject<AppContext>(responseString);

//Iterate through the list to find the correct application,
//and retrieve it's object id
for (int i = 0; i < appContext.value.Count; i++)
{
if (appContext.value[i].appId == this._appPrincipalId)
{
appObjectId
= appContext.value[i].objectId;

}
}
}
}
}

catch (WebException ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}


return appObjectId;
}

#endregion

#endregion

}
}


 


Let’s go to add under our folder “Services” a new interface called IGraphAPIQuery.cs and add the code below.


using AADGraphAPIManager.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AADGraphAPIManager.Services
{
public interface IGraphAPIQuery
{
UserDetails GetUserByObjectId(
string userId);
UserDetails GetUserByName(
string name);
AadGroups GetGroups();
IList
<object> GetUserGroups(string name);
AadGroup CreateNewGroup(
string groupName);
bool RegisterNewExtension(string strExtension, AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget extensionTarget);
bool UnRegisterExtension(string strExtension);
bool RegisterClientExtensionsInGroups();
string CheckExtensionRegistered(string extensionName);
bool SetExtensionValue(string entityName, string extensionName, string extensionValue, AADGraphAPIManager.Services.GraphAPIQuery.ExtensionTarget extensionTarget);
bool CheckIfGroupExists(string groupName);
}
}

All these operations are the basic ones, we will extend the code doing a wrapper around the GraphAPIQuery. I am not going to explain all the process, but you have to be focus in adding extensions. To add an extension to a group or user you need to create the group or the user first. That basically means you have to do a double call. Unfortunately there is no way around you.


Another issue comes when you go to Azure AD to edit the groups or users, don’t expect to find the extensions them on the Azure portal, so you need to create a User Interface for that with CRUD operations via Web.


I am going to post the code, so you can have a look, but remember this is a beta version Smile , there is plenty of stuff it needs to be cleaned. At least you will be able to see how you can create extensions for Groups and Users, so you can extend it for your own purposes. I have mixed different ways to do the calls as well as serialization, so you can grab your favourites.


To make this work, you need to instantiate UserOperations and pass the appPrincipalId (which is the ClientID of the Azure Application Instance (ie: string like this "8353c878-c925-4567-b900-0985b0805e6a")), the appKey (which is the unique key of the Azure Application Instance (ie: string like this "GJu+cHCkinvK9HvSY60LDH7347x4CgMvXJz2udiEzes=")) and the ClaimsPrincipal (you just need to pass ClaimPrincipal.Current).


UserOperations useroperations = new UserOperations("8353c878-c925-4567-b900-0985b0805e6a","GJu+cHCkinvK9HvSY60LDH7347x4CgMvXJz2udiEzes=",ClaimsPrincipal.Current);

Download Code Here

4 comments:

ninest123 said...

replica watches, louis vuitton, air jordan pas cher, longchamp pas cher, ray ban sunglasses, nike air max, oakley sunglasses, ugg boots, oakley sunglasses, nike free, nike roshe run, louboutin shoes, michael kors, louboutin, longchamp outlet, louis vuitton outlet, polo ralph lauren outlet, longchamp outlet, tiffany jewelry, tiffany and co, jordan shoes, tory burch outlet, ugg boots, air max, louis vuitton, replica watches, nike free, louis vuitton, uggs on sale, sac longchamp, burberry, chanel handbags, oakley sunglasses, polo ralph lauren outlet, louboutin outlet, longchamp, louis vuitton outlet, prada outlet, cheap oakley sunglasses, oakley sunglasses, christian louboutin outlet, louboutin pas cher, ralph lauren pas cher, ray ban sunglasses, kate spade outlet, ray ban sunglasses, nike air max, prada handbags, gucci outlet, nike outlet

ninest123 said...

burberry outlet online, nike air max, coach purses, nike air max, michael kors, timberland, new balance pas cher, true religion jeans, kate spade handbags, nike free run uk, north face, true religion jeans, ralph lauren uk, air force, hollister pas cher, ray ban uk, converse pas cher, lacoste pas cher, michael kors outlet, michael kors outlet, ugg boots, michael kors outlet, michael kors outlet, coach outlet, vans pas cher, hollister, burberry, coach outlet, nike blazer, oakley pas cher, vanessa bruno, mulberry, ugg boots, lululemon, hermes, tn pas cher, ray ban pas cher, sac guess, hogan, true religion jeans, abercrombie and fitch, true religion outlet, michael kors, nike air max, michael kors outlet, replica handbags, michael kors, nike roshe, north face, michael kors

ninest123 said...

longchamp, iphone 5s cases, hollister, ferragamo shoes, herve leger, jimmy choo shoes, giuseppe zanotti, north face outlet, vans shoes, mont blanc, nike huarache, celine handbags, iphone cases, iphone 6s cases, nike trainers, lululemon, louboutin, ralph lauren, iphone 6 cases, babyliss, s5 cases, hollister, north face outlet, nfl jerseys, bottega veneta, soccer shoes, oakley, baseball bats, abercrombie and fitch, ghd, chi flat iron, valentino shoes, beats by dre, asics running shoes, wedding dresses, reebok shoes, timberland boots, ipad cases, mac cosmetics, mcm handbags, nike roshe, nike air max, insanity workout, iphone 6s plus cases, birkin bag, p90x workout, hollister, instyler, iphone 6 plus cases, soccer jerseys, new balance

ninest123 said...

barbour jackets, toms shoes, lancel, supra shoes, juicy couture outlet, moncler, moncler, louis vuitton, thomas sabo, hollister, canada goose uk, canada goose, louis vuitton, doudoune canada goose, converse outlet, canada goose outlet, replica watches, bottes ugg, canada goose outlet, nike air max, gucci, moncler, pandora jewelry, canada goose, karen millen, marc jacobs, converse, moncler, ray ban, wedding dresses, montre pas cher, ugg,ugg australia,ugg italia, moncler, pandora charms, canada goose, moncler, canada goose, ugg,uggs,uggs canada, moncler, vans, swarovski crystal, sac louis vuitton pas cher, pandora jewelry, juicy couture outlet, louis vuitton, louis vuitton, moncler outlet, coach outlet, swarovski, ugg boots uk, links of london, ugg pas cher, pandora charms