Azure AD B2C Custom User Attributes - azure

I'm new to the Azure B2C world. I'm attempting to create a Custom User attribute to store data for our application. I've created it in the Azure portal and assigned it to my Signup/SignIn policy. However, I want to be able to update/read this value programtically. I've been going down the route of using Graph API and registering Extensions. So two questions:
1) Are extensions/custom attributes the same thing?
2) I've tried this code and the returned extensions are always empty:
public void RegisterExtension()
{
string myRegisteredAppObjectId = "<>";
string json = #"{
""name"": ""My Custom Attribute"",
""dataType"": ""String"",
""targetObjects"": [
""User""
]
}";
B2CGraphClient b2CGraphClient = new B2CGraphClient();
b2CGraphClient.RegisterExtension(myRegisteredAppObjectId, json);
var extensions = JsonConvert.DeserializeObject(b2CGraphClient.GetExtensions(myRegisteredAppObjectId).Result);
}
B2CGraphClient.cs
public class B2CGraphClient
{
private string clientId { get; set; }
private string clientSecret { get; set; }
private string tenant { get; set; }
private AuthenticationContext authContext;
private ClientCredential credential;
public B2CGraphClient(string clientId, string clientSecret, string tenant)
{
// The client_id, client_secret, and tenant are pulled in from the App.config file
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenant = tenant;
// The AuthenticationContext is ADAL's primary class, in which you indicate the direcotry to use.
this.authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);
// The ClientCredential is where you pass in your client_id and client_secret, which are
// provided to Azure AD in order to receive an access_token using the app's identity.
this.credential = new ClientCredential(clientId, clientSecret);
}
public async Task<string> DeleteUser(string objectId)
{
return await SendGraphDeleteRequest("/users/" + objectId);
}
public async Task<string> RegisterExtension(string objectId, string body)
{
return await SendGraphPostRequest("/applications/" + objectId + "/extensionProperties", body);
}
public async Task<string> GetExtensions(string appObjectId)
{
return await SendGraphGetRequest("/applications/" + appObjectId + "/extensionProperties", null);
}
private async Task<string> SendGraphPostRequest(string api, string json)
{
// NOTE: This client uses ADAL v2, not ADAL v4
AuthenticationResult result = authContext.AcquireToken(Globals.aadGraphResourceId, credential);
HttpClient http = new HttpClient();
string url = Globals.aadGraphEndpoint + tenant + api + "?" + Globals.aadGraphVersion;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("POST " + url);
Console.WriteLine("Authorization: Bearer " + result.AccessToken.Substring(0, 80) + "...");
Console.WriteLine("Content-Type: application/json");
Console.WriteLine("");
Console.WriteLine(json);
Console.WriteLine("");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
string error = await response.Content.ReadAsStringAsync();
object formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine((int)response.StatusCode + ": " + response.ReasonPhrase);
Console.WriteLine("");
return await response.Content.ReadAsStringAsync();
}
public async Task<string> SendGraphGetRequest(string api, string query)
{
// First, use ADAL to acquire a token using the app's identity (the credential)
// The first parameter is the resource we want an access_token for; in this case, the Graph API.
AuthenticationResult result = authContext.AcquireToken("https://graph.windows.net", credential);
// For B2C user managment, be sure to use the 1.6 Graph API version.
HttpClient http = new HttpClient();
string url = "https://graph.windows.net/" + tenant + api + "?" + Globals.aadGraphVersion;
if (!string.IsNullOrEmpty(query))
{
url += "&" + query;
}
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("GET " + url);
Console.WriteLine("Authorization: Bearer " + result.AccessToken.Substring(0, 80) + "...");
Console.WriteLine("");
// Append the access token for the Graph API to the Authorization header of the request, using the Bearer scheme.
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
string error = await response.Content.ReadAsStringAsync();
object formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine((int)response.StatusCode + ": " + response.ReasonPhrase);
Console.WriteLine("");
return await response.Content.ReadAsStringAsync();
}
}
Of course, myRegisteredAppObjectId has a valid GUID in it.
Thanks

Are extensions/custom attributes the same thing?
Based on my test, extensions is the same thing as custom attributes.
I've tried this code and the returned extensions are always empty:
I add the custom propery MyCustomAttribute following this tutorial and use a custom attribute in my policy. You could refer to my test steps.
I download the B2C-GraphAPI-DotNet project from Github. Using following code to the custom attribute
var applications = client.GetApplications("$filter=startswith(displayName, 'b2c-extensions-app')").Result
var extension = client.GetExtensions(objectId).Result //objectId from the applications result.
Then we could get the custom properties from the extension.
Then you can then treat that attribute the same way you treat any other property on a user object
Such as create a user:
var jsonObject = new JObject
{
{"accountEnabled", true},
{"country", "US"},
{"creationType", "LocalAccount"},
{"displayName", "Tomsun"},
{"passwordPolicies", "DisablePasswordExpiration,DisableStrongPassword"},
{ "extension_42ba0de8530a4b5bbe6dad21fe6ef092_MyCustomAttribute","test2"}, //custom propery
{"passwordProfile", new JObject
{
{"password", "!QAZ1234wer"},
{"forceChangePasswordNextLogin", true}
} },
{"signInNames", new JArray
{
new JObject
{
{"value","tom1#example.com"},
{"type", "emailAddress"}
}
}
}
};
string user = client.CreateUser(jsonObject.ToString()).Result;
Query a user
var user = client.GetUserByObjectId(objectId).Result; //user objectId
Update a user
var jsonUpdate = new JObject
{
{ "extension_42ba0de8530a4b5bbe6dad21fe6ef092_MyCustomAttribute","testx"}
};
var updateuser = client.UpdateUser("objectId", jsonObject2.ToString()).Result; //UserObject Id

Related

C# Azure Management REST API - Bind Certificate to App Service Custom Domain

I can create a custom domain using the Azure Management REST API, and I can create an App Service Managed certificate, which is associated with the custom domain (I think). Maybe not. However, under custom domains in my app service, it shows that I need to add a binding.
Here is the certificate:
https://management.azure.com/subscriptions/xxx-xxx-479a-bb9f-4c7e01d9a379/resourceGroups/MyResourceGroup/providers/Microsoft.Web/sites/xxx20211028195113/hostNameBindings/my.site?api-version=2016-08-01
and
https://management.azure.com/subscriptions/xxx-xxx-479a-bb9f-4c7e01d9a379/resourceGroups/MyResourceGroup/providers/Microsoft.Web/certificates/my.site?api-version=2021-02-01
I used the first end-point to create the custom domain, and the second end-point to create the certificate. I'm not sure how to bind the certificate to the custom domain. I expected the call to create certificate to do that for me since I included the serverFarm in the request body, but it didn't work.
I want to use the Azure Management API to bind the certificate to the custom domain. How can I do that? which endpoint should I use and what values need to be set in the request body?
Any help would be appreciated. Thanks.
For my full code reference, see my other post here:
C# .Net Azure Management REST API - Add App Service Managed Certificate - Response = Not Found
EDIT: Showing the Complete Answer
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MyShoppingCart.Helpers.ManagementLibrarySample
{
public class ManagementLibrarySample
{
static string _ClientId = Startup.StaticConfig.GetValue<string>("Azure:ClientId");
static string _ClientKey = Startup.StaticConfig.GetValue<string>("Azure:ClientSecret");
static string _TenantId = Startup.StaticConfig.GetValue<string>("Azure:TenantId");
static string _SubscriptionId = Startup.StaticConfig.GetValue<string>("Azure:SubscriptionId");
static string _ResourceGroupName = Startup.StaticConfig.GetValue<string>("Azure:ResourceGroupName");
static string _AlternateResourceGroupName = Startup.StaticConfig.GetValue<string>("Azure:AlternateResourceGroupName");
static string _AppName = Startup.StaticConfig.GetValue<string>("Azure:AppName");
static string _AppServicePlanName = Startup.StaticConfig.GetValue<string>("Azure:AppServicePlanName");
static Uri _baseURI = new Uri($"https://management.azure.com/");
private static string GetAccessToken()
{
var context = new AuthenticationContext("https://login.windows.net/" + _TenantId);
ClientCredential clientCredential = new ClientCredential(_ClientId, _ClientKey);
var tokenResponse = context.AcquireTokenAsync(_baseURI.ToString(), clientCredential).Result;
return tokenResponse.AccessToken;
}
public static async Task<bool> CreateCustomDomainAndCertificate(string sHostName)
{
bool ret = false;
HttpResponseMessage responseMessage = await CreateCustomDomain(sHostName);
if (responseMessage.IsSuccessStatusCode)
{
responseMessage = await CreateAppManagedCertificate(sHostName);
/*
it can take a good 5 minutes to create the certificate
but you get the 202 status code right away.
You cannot bind the certificate to the custom domain
name until after the certificate actually exists.
*/
if ((long)responseMessage.StatusCode == 202)// Accepted
{
DateTime dtStart = DateTime.Now;
while ((long)responseMessage.StatusCode != 200 && DateTime.Now < dtStart.AddMinutes(10))
{//Wait until the certificate has been created, up to 10 minutes
Thread.Sleep(60000);//1 minute
responseMessage = await BindCertificateToCustomDomain(sHostName);
}
if ((long)responseMessage.StatusCode == 200)
ret = true;
}
}
return ret;
}
private static async Task<HttpResponseMessage> CreateCustomDomain(string sHostName)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
string requestURl = _baseURI + $"subscriptions/{_SubscriptionId}/resourceGroups/{_ResourceGroupName}/providers/Microsoft.Web/sites/{_AppName}/hostNameBindings/{sHostName}?api-version=2016-08-01";
string body = $"{{\"properties\": {{\"azureResourceName\": \"{_AppName}\"}}}}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
return await client.PutAsync(requestURl, stringContent);
}
}
private static async Task<HttpResponseMessage> CreateAppManagedCertificate(string sHostName)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
string requestURl = _baseURI + $"subscriptions/{_SubscriptionId}/resourceGroups/{_ResourceGroupName}/providers/Microsoft.Web/certificates/{sHostName}?api-version=2021-02-01";
string serverFarm = $"/subscriptions/{_SubscriptionId}/resourceGroups/{_AlternateResourceGroupName}/providers/Microsoft.Web/serverfarms/{_AppServicePlanName}";
string body = $"{{\"location\": \"West US\", \"properties\": {{\"canonicalName\": \"{sHostName}\", \"hostNames\": [\"{sHostName}\"], \"serverFarmId\": \"{serverFarm}\"}}}}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
return await client.PutAsync(requestURl, stringContent);
}
}
private static async Task<HttpResponseMessage> BindCertificateToCustomDomain(string sHostName)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
string requestURl = _baseURI + $"subscriptions/{_SubscriptionId}/resourceGroups/{_ResourceGroupName}/providers/Microsoft.Web/sites/{_AppName}?api-version=2016-08-01";
string serverFarm = $"/subscriptions/{_SubscriptionId}/resourceGroups/{_AlternateResourceGroupName}/providers/Microsoft.Web/serverfarms/{_AppServicePlanName}";
string body = $"{{\"location\": \"West US\", \"properties\": {{\"HostNameSslStates\": [ {{ \"SslState\" : \"1\", \"ToUpdate\" : \"True\", \"Name\": \"{sHostName}\"}}]}}, \"kind\": \"app\", \"location\": \"West US\", \"tags\" : {{\"hidden-related:{serverFarm}\": \"empty\"}}}}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
return await client.PutAsync(requestURl, stringContent);
}
}
}
}
How can I use the Management API to secure the custom domain with the
app service managed certificate?
Thanks #David.Warwick for the confirmation,
As we have discussed to achieve the above requirement we have to use the below Rest API .
You can try with PUT method for binding SSL certificate with Custom domain.
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/Microsoft.Web/sites/{snapshotName}?api-version={api-version}
For more information please refer this SO THREAD

Change AuthenticationScheme to force another AuthenticationHandler to be fired

We need to be able to support both Basic Authentication and Bearer Authentication. Bearer Authentication being the preferred / default method. Basic is for some clients that cannot support Bearer Authentication.
We want to be able to manage privileges from a since source. The idea is that if a user is using Basic Auth, that user would actually send us his clientId/clientSecret and we will get the token for him.
In order to keep this hole as closed as possible, clientId needs to be validated versus a whitelist first. How can I do that without have to completely re-implement the Bearer Authentication. Even if we whitelisted the clientId we still want to validate the token exactly like if they would have sent it.
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string AuthTypeName = "Basic ";
private const string _authHeaderName = "Authorization";
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers[_authHeaderName]);
var (clientId, clientSecret) = DecodeUserIdAndPassword(authHeader.Parameter);
if (!clientIdIsWhiteListed.IsSatisfiedBy(clientId))
return AuthenticateResult.Fail("Invalid Credentials");
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
string[] scopes = new string[] { "MyScope" };
var result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
// Here is where I have no clue how to continue
Request.Headers[_authHeaderName] = $"Bearer {result.AccessToken}"; //?
return AuthenticateResult.NoResult(); //?
}
}
I was so close to the correct answer, well here is what I did and it works.
1 line change
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string AuthTypeName = "Basic ";
private const string _authHeaderName = "Authorization";
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers[_authHeaderName]);
var (clientId, clientSecret) = DecodeUserIdAndPassword(authHeader.Parameter);
if (!clientIdIsWhiteListed.IsSatisfiedBy(clientId))
return AuthenticateResult.Fail("Invalid Credentials");
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
string[] scopes = new string[] { "MyScope" };
var result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
Request.Headers[_authHeaderName] = $"Bearer {result.AccessToken}";
return await Context.AuthenticateAsync("Bearer");
}
}

How to call an Azure Function App API with Easy-Auth Enables using Active Directory from a C# Client

I have an Azure Function App with Azure Active Directory configured but when I call if from my client I keep getting an Unauthorized response.
I have tried a couple different scenarios but nothing worked. Below is a snippet of the last bit of code that I tried.
///
var #params2 = new NameValueCollection
{
{"grant_type", "client_credentials"},
{"client_id", $"{ClientId}"},
{"client_secret", $"{ClientSecret}"},
{"username", userId},
{"resource", "https://management.azure.com/"}
};
var queryString2 = HttpUtility.ParseQueryString(string.Empty);
queryString2.Add(#params2);
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{"grant_type", "client_credentials"},
{"client_id", ClientId},
{"client_secret", ClientSecret},
{"username", userId}
});
var authorityUri2 = $"{string.Format(CultureInfo.InvariantCulture, AadInstance, Tenant).TrimEnd('/')}/oauth2/token";
//var authorityUri2 = $"https://login.microsoftonline.com/{Tenant}/v2.0/.well-known/openid-configuration";
var authUri2 = String.Format("{0}?{1}", authorityUri2, queryString2);
var client2 = new HttpClient();
var message = client2.PostAsync(authorityUri2, content).Result;
//var message = client2.GetAsync(authorityUri2).Result;
var response = message.Content.ReadAsStringAsync().Result;
dynamic values=null;
try
{
values = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
}
catch
{
values = response;
}
var AuthToken2 = values["access_token"];
client2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken2);
HttpResponseMessage response2 = await client2.GetAsync(AppBaseAddress.TrimEnd('/') + "/api/AADIntegration");
if (response.IsSuccessStatusCode)
{
// Read the response and data-bind to the GridView to display To Do items.
string s = await response.Content.ReadAsStringAsync();
log.LogInformation($"Success while getting / api / AADIntegration : {s}");
return (ActionResult)new OkObjectResult(s);
}
else
{
string failureDescription = await response.Content.ReadAsStringAsync();
log.LogInformation($"An error occurred while getting / api / AADIntegration : {response.ReasonPhrase}\n {failureDescription}");
return (ActionResult)new OkObjectResult(failureDescription);
}
Data should returned from the Function App.
For client_credentials grant flow your code seems little different. Here I am giving you exact sample for azure function. Just plug and play :))
Example contains:
How would you get token using client_credentials flow
Getting user list From Azure Active Directory tenant using above
token
Access Token Class:
public class AccessTokenClass
{
public string token_type { get; set; }
public string expires_in { get; set; }
public string resource { get; set; }
public string scope { get; set; }
public string access_token { get; set; }
}
Reference To Add:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;
Azure Function Body:
public static class FunctionGetUserList
{
[FunctionName("FunctionGetUserList")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
try
{
log.LogInformation("C# HTTP trigger function processed a request.");
//Token Request endpoint Just replace yourTennantId/Name
string tokenUrl = $"https://login.microsoftonline.com/yourTennantId/Name.onmicrosoft.com/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "b603c7bead87-Your_client_id-e6921e61f925",
["client_secret"] = "Vxf1SluKbgu4P-Your_client_Secret-F0Nf3wE5oGl/2XDSeZ=",
["resource"] = "https://graph.microsoft.com"
});
dynamic json;
AccessTokenClass results = new AccessTokenClass();
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
results = JsonConvert.DeserializeObject<AccessTokenClass>(json);
var accessToken = results.access_token;
//Create Request To Server
using (HttpClient clientNew = new HttpClient())
{
//Pass Token on header
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Get Data from API
var requestToAzureEndpoint = await client.GetAsync("https://graph.microsoft.com/v1.0/users");
if (requestToAzureEndpoint.IsSuccessStatusCode)
{
var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
dynamic responseResults = JsonConvert.DeserializeObject<dynamic>(result_string);
return new OkObjectResult(responseResults);
}
else
{
var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
return new OkObjectResult(result_string);
}
}
}
catch (Exception ex)
{
return new OkObjectResult(ex.Message);
}
}
}
Point To Remember
For Azure Active Directory List users access make sure you have following permission:
User.Read.All
Permission Type: Application
You can check here. See the screen shot for better understanding; make sure you have clicked "Grant admin consent for yourTenant" after adding permission.
Note: This is how you can access Azure Active Directory Token using Azure Function after that how to access resource using that token to a specific API endpoint efficiently.
Are you sure you have properly implemented this properly? It looks like a few of your parameters are wrong for the client credential flow. Please double check that you are properly following the client credential flow.
The client credential grant flow is documented here : https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
But for more information on getting this properly working in your function app, please refer to the blog below for more information/help on implementing this.
https://blogs.msdn.microsoft.com/ben/2018/11/07/client-app-calling-azure-function-with-aad/
The value of resource is not correct.
Replace {"resource", "https://management.azure.com/"} with {"resource", $"{ClientId}"}

Get Azure Active Directory Token with username and password

I'm trying to authenticate my client using AAD and automate this using a Windows Service. In AAD .NET SDK, There's two methods, AcquireTokenAsync and AcquireToken, but i can't use either of these methods, the await call will stay forever with no response, and when i do something like this:
result = authContext.AcquireTokenAsync(resourceHostUri, clientId, new UserCredential(hardcodedUsername, hardcodedPassword)).Result;
The object returns a status of Waiting for Activation & Code 31..
Now, Is there anyway to acquire the token using hardcoded username and password?
My full code:
string hardcodedUsername = "username";
string hardcodedPassword = "password";
string tenant = "tenantId#onmicrosoft.com";
string clientId = "clientId";
string resourceHostUri = "https://management.azure.com/";
string aadInstance = "https://login.microsoftonline.com/{0}";
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
authContext = new AuthenticationContext(authority);
AuthenticationResult result = null;
try
{
result = authContext.AcquireTokenAsync(resourceHostUri, clientId, new UserCredential(hardcodedUsername, hardcodedPassword)).Result;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return result;
I'm trying to get access to Azure API.
UPDATE 1:
I got this in the output when i tried to await the call, i think this might help:
Microsoft.IdentityModel.Clients.ActiveDirectory TokenCache: Looking up cache for a token...
Microsoft.IdentityModel.Clients.ActiveDirectory TokenCache: No matching token was found in the cache
Microsoft.IdentityModel.Clients.ActiveDirectory d__0: Sending user realm discovery request to 'https://login.microsoftonline.com/common/UserRealm/username?api-version=1.0'
Microsoft.IdentityModel.Clients.ActiveDirectory d__4: User with hash '***' detected as 'Federated'
try below link code
https://msdn.microsoft.com/en-in/library/partnercenter/dn974935.aspx
how to get access token after windows azure active directory authentication
How to get current token from Azure ActiveDirectory application
// Get OAuth token using client credentials
string tenantName = "GraphDir1.OnMicrosoft.com";
string authString = "https://login.microsoftonline.com/" + tenantName;
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
string clientId = "118473c2-7619-46e3-a8e4-6da8d5f56e12";
string key = "hOrJ0r0TZ4GQ3obp+vk3FZ7JBVP+TX353kNo6QwNq7Q=";
ClientCredential clientCred = new ClientCredential(clientId, key);
string resource = "https://graph.windows.net";
string token;
try
{
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(resource, clientCred);
token = authenticationResult.AccessToken;
}
catch (AuthenticationException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Acquiring a token failed with the following error: {0}", ex.Message);
if (ex.InnerException != null)
{
// You should implement retry and back-off logic according to
// http://msdn.microsoft.com/en-us/library/dn168916.aspx . This topic also
// explains the HTTP error status code in the InnerException message.
Console.WriteLine("Error detail: {0}", ex.InnerException.Message);
}
}
Please try the following:
static void Main(string[] args)
{
Task<AuthenticationResult> t = getAccessToken();
t.Wait();
var result = t.Result;
Console.WriteLine(result.AccessToken);
Console.WriteLine("Please any key to terminate the program");
Console.ReadKey();
}
public static async Task<AuthenticationResult> getAccessToken()
{
string hardcodedUsername = "username";
string hardcodedPassword = "password";
string tenant = "tenant.onmicrosoft.com";
string clientId = "clientId";
string resourceHostUri = "https://management.azure.com/";
string aadInstance = "https://login.microsoftonline.com/{0}";
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
var authContext = new AuthenticationContext(authority);
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync(resourceHostUri, clientId, new UserCredential(hardcodedUsername, hardcodedPassword));
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return result;
}
What I have done is made getAccessToken() method async and inside that the code is made to wait to get the token when you call authContext.AcquireTokenAsync.

How to Submit Azure AD B2C NEW User Submission in MVC App

Loving Azure AD B2C... Looking forward to when it is out of preview!!!
Have a special case I need help putting my head around.
I have a page where I capture new email addresses via a web form.
After adding that email to my mailing list, I want to then create an AD B2C account automatically without making the user click any additional buttons using my ASP.NET MVC Site.
In reading the Article at: https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-graph-dotnet/
I see that it is possible to add a new user using the Graph API.
However, this example is written using a cmd program.
Does anyone know if there is some sample code to allow me to insert a user into AD B2C inside an MVC controller?
Heres some sample code on how I do it from ASP.Net MVC. Remeber that you need to include ClientId and Clientsecret (they are separate from the ASP.Net webapp) as explained in the article you mention. Code from controller--helperclass:
UserController:
// POST: User/Create
[HttpPost]
public async Task<ActionResult> Create(b2cuser usr)
{
try
{
usr.AlternativeSignInNamesInfo.First().Value = string.Format("{0}_{1}", usr.FirstName, usr.LastName);
usr.DisplayName = string.Format("{0} {1}", usr.FirstName, usr.LastName);
string json = Newtonsoft.Json.JsonConvert.SerializeObject(usr, Formatting.None);
Utils.GraphAPIHelper api = new Utils.GraphAPIHelper(graphAPIClientId, graphAPIClientSecret, tenant);
string res = await api.GraphPostRequest("/users/", json);
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
And in the GraphAPIHelper:
internal async Task<string> GraphPostRequest(string api, string json)
{
AuthenticationResult result = authContext.AcquireToken(graphResourceID, credential);
HttpClient http = new HttpClient();
string url = aadGraphEndpoint + tenant + api + "?" + aadGraphVersion;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
string error = await response.Content.ReadAsStringAsync();
object formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
return await response.Content.ReadAsStringAsync();
}
And finally, some samplecode from the model, pls note JsonProperty(Order:
public class b2cuser
{
[JsonProperty(Order = 0, PropertyName = "accountEnabled")]
public bool AccountEnabled = true;
[JsonProperty(Order = 1, PropertyName = "alternativeSignInNamesInfo")]
public List<AlternativeSignInNamesInfo> AlternativeSignInNamesInfo { get; set; }
[JsonProperty(Order = 2, PropertyName = "creationType")]
public string CreationType = "NameCoexistence";

Resources