We have set the Azure AD as a identity provider in our application. We want to display profile picture that should come from Azure AD, in the application.
In order to test, I have added one Windows Live Id account (which has a profile picture) in the Azure AD. We then tried it using Graph Explorer, but no luck.
How do we get the profile picture from Azure AD?
You can use Azure Active Directory Graph Client to get user thumbnail photo
var servicePoint = new Uri("https://graph.windows.net");
var serviceRoot = new Uri(servicePoint, "<your tenant>"); //e.g. xxx.onmicrosoft.com
const string clientId = "<clientId>";
const string secretKey = "<secretKey>";// ClientID and SecretKey are defined when you register application with Azure AD
var authContext = new AuthenticationContext("https://login.windows.net/<tenant>/oauth2/token");
var credential = new ClientCredential(clientId, secretKey);
ActiveDirectoryClient directoryClient = new ActiveDirectoryClient(serviceRoot, async () =>
{
var result = await authContext.AcquireTokenAsync("https://graph.windows.net/", credential);
return result.AccessToken;
});
var user = await directoryClient.Users.Where(x => x.UserPrincipalName == "<username>").ExecuteSingleAsync();
DataServiceStreamResponse photo = await user.ThumbnailPhoto.DownloadAsync();
using (MemoryStream s = new MemoryStream())
{
photo.Stream.CopyTo(s);
var encodedImage = Convert.ToBase64String(s.ToArray());
}
Azure AD returns user's photo in binary format, you need to convert to Base64 string
Getting photos through Graph Explorer is not supported. Assuming that "signedInUser" already contains the signed in user entity, then this code snippet using the client library should work for you...
#region get signed in user's photo
if (signedInUser.ObjectId != null)
{
IUser sUser = (IUser)signedInUser;
IStreamFetcher photo = (IStreamFetcher)sUser.ThumbnailPhoto;
try
{
DataServiceStreamResponse response =
photo.DownloadAsync().Result;
Console.WriteLine("\nUser {0} GOT thumbnailphoto", signedInUser.DisplayName);
}
catch (Exception e)
{
Console.WriteLine("\nError getting the user's photo - may not exist {0} {1}", e.Message,
e.InnerException != null ? e.InnerException.Message : "");
}
}
#endregion
Alternatively you can do this through REST and it should look like this:
GET https://graph.windows.net/myorganization/users//thumbnailPhoto?api-version=1.5
Hope this helps,
According to the Azure AD Graph API Docs, Microsoft recommends transitioning to Microsoft Graph, as the Azure AD Graph API is being phased out.
However, to easily grab the photo via Azure AD, for now, use this URL template:
https://graph.windows.net/myorganization/users/{user_id}/thumbnailPhoto?api-version={version}
For example (this is a fake user id):
https://graph.windows.net/myorganization/users/abc1d234-01ab-1a23-12ab-abc0d123e456/thumbnailPhoto?api-version=1.6
The code below assumes you already have an authenticated user, with a token. This is a simplistic example; you'll want to change the return value to suit your needs, add error checking, etc.
const string ThumbUrl = "https://graph.windows.net/myorganization/users/{0}/thumbnailPhoto?api-version=1.6";
// Attempts to retrieve the thumbnail image for the specified user, with fallback.
// Returns: Fully formatted string for supplying as the src attribute value of an img tag.
private string GetUserThumbnail(string userId)
{
string thumbnail = "some base64 encoded fallback image";
string mediaType = "image/jpg"; // whatever your fallback image type is
string requestUrl = string.Format(ThumbUrl, userId);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", GetToken());
HttpResponseMessage response = client.GetAsync(requestUrl).Result;
if (response.IsSuccessStatusCode)
{
// Read the response as a byte array
var responseBody = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
// The headers will contain information on the image type returned
mediaType = response.Content.Headers.ContentType.MediaType;
// Encode the image string
thumbnail = Convert.ToBase64String(responseBody);
}
return $"data:{mediaType};base64,{thumbnail}";
}
// Factored out for use with other calls which may need the token
private string GetToken()
{
return HttpContext.Current.Session["Token"] == null ? string.Empty : HttpContext.Current.Session["Token"].ToString();
}
Related
I'm working on a multilanguage project for accademic purpose. I've written a simple Python Client that make requests to an API server written in ASP.NET. The server retrives spotify info about users. The server interacts with a DB filled by a Golang server that only makes scraping on API's exposed from Spotify. I'm aware that it's a misuse and there are better solutions
Clearly, Golang server, in order to make requests to Spotify API's, needs to know the access token returned from spotify Authorization Code Flow. Overlooking about spotify token expire time, the idea is: after user authentication through Identity module of ASP.NET server (using JWT token), associate the access token obtained calling https://accounts.spotify.com/api/token to user's informations. So, i expose an API in ASP.NET server like this
[AllowAnonymous]
[HttpPost("token")]
public async Task<ContentResult> getTokenAsync(string? code = null)
{
//to retrive information about who is the user that making call -> need later for associate spotifytoken
string accessToken = Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", "");
JwtSecurityTokenHandler t = new JwtSecurityTokenHandler();
var token = t.ReadJwtToken(accessToken);
var user = _userManager.FindByIdAsync(token.Subject).Result;
string s = "https://accounts.spotify.com/api/token";
if (code == null)
{
var qb = new QueryBuilder();
qb.Add("response_type", "code");
qb.Add("client_id", _config["SpotiSetting:clientId"]);
qb.Add("scope", "user-read-private user-read-email user-library-read");
qb.Add("redirect_uri", _config["SpotiSetting:redirectUser"]);
qb.Add("show_dialog", "true");
return new ContentResult
{
ContentType = "text/html",
Content = "https://accounts.spotify.com/authorize/" + qb.ToQueryString().ToString()
//Content = JsonConvert.SerializeObject(user.Result)
};
} else
{
//if i'm here, api is the callback designed for spotify
var qb = new QueryBuilder();
qb.Add("grant_type", "authorization_code");
qb.Add("code", code);
qb.Add("redirect_uri", "https://localhost:44345/spotify/token");
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, s);
req.Content = new FormUrlEncodedContent(qb);
req.Headers.Authorization = new AuthenticationHeaderValue("Basic", "here_my_secret_encoded_CLIENTID:CLIENT_SECRET");
var response = await client.SendAsync(req);
var result = response.Content.ReadAsStringAsync().Result;
AccessToken json = JsonConvert.DeserializeObject<AccessToken>(result);
user.spotifyInformation.authToken = code;
user.spotifyInformation.accessToken = json;
var res = _userManager.UpdateAsync(user);
if (res.IsCompletedSuccessfully)
{
return Content("ok");
}
else
{
Content("Problem");
}
} return Content("");
}
The problem is that the second time that API is invoked, it's spotify that is sending the first authorization token (needed to request access_token), so I lost user information retrived in the first request. Should be better write two distinct API and separate callback from user request?
It's my first question here, so please to have mercy
Need to store the image from a private git repository to a blob using C#. Tried with below code but getting 404 errors.
I am using the below code from
C# example of downloading GitHub private repo programmatically
var githubToken = "[token]";
var url =
"https://github.com/[username]/[repository]/archive/[sha1|tag].zip";
var path = #"[local path]";
using (var client = new System.Net.Http.HttpClient())
{
var credentials = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}:", githubToken);
credentials = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(credentials));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentials);
var contents = client.GetByteArrayAsync(url).Result;
System.IO.File.WriteAllBytes(path, contents);
}
Note: Able to fetch from the public repository
How to fix :
The URL is changed to GET /repos/:owner/:repo/:archive_format/:ref. See https://developer.github.com/v3/repos/contents/#get-archive-link
For private repositories, these links are temporary and expire after five minutes.
GET /repos/:owner/:repo/:archive_format/:ref
You should not pass the credentials using basic authentication. Instead, you should create a token by following the official docs. see https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
Finally, you need pass an extra User-Agent header. It is required by GitHub API. See https://developer.github.com/v3/#user-agent-required :
All API requests MUST include a valid User-Agent header.
Demo
public class GitHubRepoApi{
public string EndPoint {get;} = "https://api.github.com/repos";
public async Task DownloadArchieveAsync(string saveAs, string owner, string token, string repo,string #ref="master",string format="zipball")
{
var url = this.GetArchieveUrl(owner, repo, #ref, format);
var req = this.BuildRequestMessage(url,token);
using( var httpClient = new HttpClient()){
var resp = await httpClient.SendAsync(req);
if(resp.StatusCode != System.Net.HttpStatusCode.OK){
throw new Exception($"error happens when downloading the {req.RequestUri}, statusCode={resp.StatusCode}");
}
using(var fs = File.OpenWrite(saveAs) ){
await resp.Content.CopyToAsync(fs);
}
}
}
private string GetArchieveUrl(string owner, string repo, string #ref = "master", string format="zipball")
{
return $"{this.EndPoint}/{owner}/{repo}/{format}/{#ref}"; // See https://developer.github.com/v3/repos/contents/#get-archive-link
}
private HttpRequestMessage BuildRequestMessage(string url, string token)
{
var uriBuilder = new UriBuilder(url);
uriBuilder.Query = $"access_token={token}"; // See https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
var req = new HttpRequestMessage();
req.RequestUri = uriBuilder.Uri;
req.Headers.Add("User-Agent","My C# Client"); // required, See https://developer.github.com/v3/#user-agent-required
return req;
}
}
Test :
var api = new GitHubRepoApi();
var saveAs= Path.Combine(Directory.GetCurrentDirectory(),"abc.zip");
var owner = "newbienewbie";
var token = "------your-----token--------";
var repo = "your-repo";
var #ref = "6883a92222759d574a724b5b8952bc475f580fe0"; // will be "master" by default
api.DownloadArchieveAsync(saveAs, owner,token,repo,#ref).Wait();
According to the message you provide, you use the wrong url to download. Regarding how to get the download url, please refer to the following steps:
Use the following url to get the download url
Method: GET
URL: https://api.github.com/repos/:owner/:repo/contents/:path?ref:<The name of the commit/branch/tag>
Header:
Authorization: token <personal access token>
The repose body will tell you the download url
For example :
Download file
For more details, please refer to https://developer.github.com/v3/repos/contents/#get-contents.
I have tried the following code to create a new storage account in Azure:
Getting the token (success - I received a token):
var cc = new ClientCredential("clientId", "clientSecret");
var context = new AuthenticationContext("https://login.windows.net/subscription");
var result = context.AcquireTokenAsync("https://management.azure.com/", cc);
Create cloud storage credentials:
var credential = new TokenCloudCredentials("subscription", token);
Create the cloud storage account (fails):
using (var storageClient = new StorageManagementClient(credentials))
{
await storageClient.StorageAccounts.CreateAsync(new StorageAccountCreateParameters
{
Label = "samplestorageaccount",
Location = LocationNames.NorthEurope,
Name = "myteststorage",
AccountType = "RA-GRS"
});
}
Error:
ForbiddenError: The server failed to authenticate the request. Verify
that the certificate is valid and is associated with this
subscription.
I am not sure if this is one of those misleading messages or if I misconfigured something in Azure?
As far as I know, Azure provides two types of storage management library now.
Microsoft.Azure.Management.Storage
Microsoft.WindowsAzure.Management.Storage
Microsoft.Azure.Management.Storage is used to create new ARM storage.
Microsoft.WindowsAzure.Management.Storage is used to create classic ARM storage.
I guess you want to create the new arm storage but you used the "Microsoft.WindowsAzure.Management.Storage" library. Since the "Microsoft.WindowsAzure.Management.Storage" uses the certificate to auth requests, you will get the error. If you want to know how to use "Microsoft.WindowsAzure.Management.Storage" to create classic storage, I suggest you refer to this article.
I assume you want to create new ARM storage, I suggest you install the "Microsoft.Azure.Management.Storage" Nuget package.
More details, you could refer to the following code.
static void Main(string[] args)
{
var subscriptionId = "your subscriptionId";
var clientId = "your client id";
var tenantId = "your tenantid";
var secretKey = "secretKey";
StorageManagementClient StorageManagement = new StorageManagementClient(new Microsoft.Azure.TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var re= StorageManagement.StorageAccounts.CreateAsync("groupname", "sotrage name",new Microsoft.Azure.Management.Storage.Models.StorageAccountCreateParameters() {
Location = LocationNames.NorthEurope,
AccountType = Microsoft.Azure.Management.Storage.Models.AccountType.PremiumLRS
},new CancellationToken() { }).Result;
Console.ReadKey();
}
static string GetAccessToken(string tenantId, string clientId, string secretKey)
{
var authenticationContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
var credential = new ClientCredential(clientId, secretKey);
var result = authenticationContext.AcquireTokenAsync("https://management.core.windows.net/",
credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.Result.AccessToken;
return token;
}
I am developing a web API to handle realtime updates (Lead information) from Facebook to integrate with my CRM.
In the WEB API POST request, I am able to capture the leadID but the problem comes when I get leadinfo by invoking FB.Get(LeadID). To make a Graph API GET request(for lead information) I need to have a user access token and this is where i have been struggling for quite sometime. I have looked up several posts online but havent got any solution for my problem.
In my sample implementation, GetLoginUrl(parameters) returns a uri and when when I request the uri in a browser, I see that the access token gets generated in the redirected Uri. But how to do this programatically ? I have tried the following
string FBAccessUrl = "https://graph.facebook.com/oauth/authorize?client_id=XXXXXXXXXXXX&response_type=token&redirect_uri=https://www.facebook.com/connect/login_success.html";
var accessTokenRequest = System.Net.HttpWebRequest.Create(FBAccessUrl);
HttpWebResponse response = (HttpWebResponse)accessTokenRequest.GetResponse();
but then i get this ResponseUri = {https://www.facebook.com/unsupportedbrowser} which is not what I want.
Can someone help me with how the user access token can be generated using C# in the Web API (without the facebook login dialog)
[HttpPost]
[ActionName("Complex")]
public void PostComplex(RootObject root)
{
FacebookClient fb = new FacebookClient();
// Get leadID from the inital HTTP POST request
long leadgen_id = root.entry[0].changes[0].value.leadgen_id;
string leadID = leadgen_id.ToString();
// to get user access token
dynamic parameters = new ExpandoObject();
parameters.client_id = "XXXXXXXXXXXXXXX";
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
// generate the login url
Uri uri = fb.GetLoginUrl(parameters);
// reuest the uri in browser and uri2 is the redirected uri
Uri uri2 = "****************"
string accesstoken = "";
FacebookOAuthResult oauthResult;
if (fb.TryParseOAuthCallbackUrl(uri, out oauthResult))
{
if (oauthResult.IsSuccess)
{
accesstoken = oauthResult.AccessToken;
}
else
{
var errorDescription = oauthResult.ErrorDescription;
var errorReason = oauthResult.ErrorReason;
}
}
fb.AccessToken = accesstoken;
string me = fb.Get(leadID).ToString();
// Then fetch required lead information
}
I am using the code below to fetch user from the azure AD using the graph API, but somehow I am getting the token access issue while doing so.
static async void MakeRequest()
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
/* OAuth2 is required to access this API. For more information visit:
https://msdn.microsoft.com/en-us/office/office365/howto/common-app-authentication-tasks */
// Specify values for the following required parameters
queryString["api-version"] = "1.6";
// Specify values for path parameters (shown as {...})
// var uri = "https://graph.windows.net/microsoft.onmicrosoft.com/users/{v-sidmis#microsoft.com}?" + queryString;
var uri = "https://graph.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/users?api-version=1.6";
var response = await client.GetAsync(uri);
if (response.Content != null)
{
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
}
This code is taken up from TechNet.
It depends on how you want to acquire the token. There are lots of scenario to integrate the application with Azure AD. You can refer it from here.
For example, if you want to use the Azure AD Graph in a daemon or service application, we can use the Client Credential flow.
1 . First we need to register an web application on the portal( detail steps refer here) and grant the permission to read the directory data like figure below:
2 . And then we can get the clientId, secret, tenantId from the portal and use the code below to acquire token(need to install the Active Directory Authentication Library)
string authority = "https://login.microsoftonline.com/{tenantId}";
string clientId = "";
string secret = "";
string resrouce = "https://graph.windows.net";
var credential = new ClientCredential(clientId, secret);
AuthenticationContext authContext = new AuthenticationContext(authority);
var token = authContext.AcquireTokenAsync(resrouce, credential).Result.AccessToken;
Console.WriteLine(token);
3 . Then we can use this token to call the Azure AD Graph REST directly or we can use the graph client library for Azure AD to retrieve the users. Here is the code samples for your reference:
//use the Azure AD client library
string accessToken = "";
string tenantId = "";
string graphResourceId = "https://graph.windows.net";
Uri servicePointUri = new Uri(graphResourceId);
Uri serviceRoot = new Uri(servicePointUri, tenantId);
ActiveDirectoryClient client = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(accessToken));
foreach(var user in client.Users.ExecuteAsync().Result.CurrentPage)
Console.WriteLine(user.DisplayName);
//using the HTTP request
var client = new HttpClient();
var tenantId = "";
var uri = $"https://graph.windows.net/{tenantId}/users?api-version=1.6";
var token = "";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
var response = client.GetAsync(uri).Result;
var result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
Update
The secrecy is available for the web application/web API when you create an application. Then you can generate the key by keys section like figure below. After you save the app, you can copy the secrect now.