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.
Related
I am trying to upload file on onedrive by using microsoft graph onedrive api.
I am using the method for authentication
Client credentials provider
https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider
Like:
// /.default scope, and preconfigure your permissions on the
// app registration in Azure. An administrator must grant consent
// to those permissions beforehand.
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "my-tenantid";
// Values from app registration
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
HttpPostedFileBase file = Request.Files;[0];
int fileSize = file.ContentLength;
string fileName = file.FileName;
string mimeType = file.ContentType;
Stream fileContent = file.InputStream;
var res = await graphClient.Me.Drive.Root.ItemWithPath(fileName).Content
.Request()
.PutAsync<DriveItem>(fileContent);
After executing this code then it gives an error in response.
Message: /me request is only valid with delegated authentication flow.
Inner error:
AdditionalData:
date: 2021-12-29T05:30:08
request-id: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
client-request-id: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
ClientRequestId: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
Client credential flow will generate the token on behalf the app itself, so in this scenario, users don't need to sign in first to generate the token stand for the user and then call the api. And because of the design,when you used Me in the graph SDK, your code/app don't know who is Me so it can't work. You should know the user_id first and use /users/{id | userPrincipalName} instead of /Me, in the SDK, that is graphClient.Users["your_user_id"] instead of graphClient.Me
In your scenario, there're 2 solutions, one way is using delegated authentication flow like what you said in your title, another way is get the user id before calling the graph api so that you can use Users["id"] but not Me
===================== Update=========================
I haven't finished the code yet but I found the correct solution now.
Firstly, we can upload file to one drive by this api, you may check the screenshot if this is one drive or sharepoint:
https://graph.microsoft.com/v1.0/users/user_id/drive/items/root:/testupload2.txt:/content
If it is, then the next is easy, using the code below to get an access token and send http request to calling the api:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "your_azuread_clientid";
var clientSecret = "corresponding_client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var tokenRequestContext = new TokenRequestContext(scopes);
var token = clientSecretCredential.GetTokenAsync(tokenRequestContext).Result.Token;
I know it's complex because the api is not the same as this one which has SDK sample, but I think it also deserves to try if they are similar.
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
I need to know exactly how to login to Azure, using c#.
I basically want to do this, but from the code:
]a link](https://learn.microsoft.com/en-us/azure/sql-database/sql-database-export)
Here is the code I copied from the internet trying to achieve this:
But I don't know how to generate the token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var exportParams = new DacExportParameters()
{
BlobCredentials = new DacExportParameters.BlobCredentialsParameter()
{
StorageAccessKey = storageKey,
Uri = new Uri(baseStorageUri)
},
ConnectionInfo = new DacExportParameters.ConnectionInfoParameter()
{
ServerName = azureSqlServer,
DatabaseName = azureSqlDatabase,
UserName = adminLogin,
Password = adminPassword
}
};
var exportResult = managementClient.Dac.Export(azureSqlServerName, exportParams);
I have a GetToken function, but I have no idea where to take the
tenant + client id + secret
private 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;
}
This question was asked before
Azure Database export with C#
but I need to see the actual code and explanation on how to get the connection info.
I need to see the actual code and explanation on how to get the connection info.
I would recommend you follow this tutorial about registering your AAD application and adding the secret key. Moreover, you could also follow Using the Azure ARM REST API – Get Access Token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
Based on your code, I assumed that you are using the package Microsoft.WindowsAzure.Management.Sql, if you use the TokenCloudCredentials, you may receive the following error response:
AFAIK, Microsoft.WindowsAzure.Management.Libraries requires the X509Certificate2 authentication, you need to construct the CertificateCloudCredentials for your SqlManagementClient. For uploading a management certificate under your subscription, you could follow Upload an Azure Service Management Certificate. For retrieving the X509Certificate2 instance, you could follow the code snippet under the Authenticate using a management certificate section from here.
For token-based authentication, you could use the package Microsoft.Azure.Management.Sql and construct your SqlManagementClient as follows:
var sqlManagement = new SqlManagementClient(new TokenCredentials("{access-token}"));
Moreover, you need to change the resource from https://management.core.windows.net/ to https://management.azure.com/ when invoking the AcquireTokenAsync method.
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 creating demo app that consumes sharepoint REST API. I'm able to successfully retrieve Bearer token (by client ID and certificate). App is registered in Azure AD and all Sharepoint permissions there are checked. I am able to retrieve all lists for example, but I am not able to retrieve a file. The same query works in browser. I assume it is permission problem. Do I have to register this app in SP? https://tenant.sharepoint.com/IT/_layouts/15/appinv.aspx I tried it without success, but I think permissions in Azure AD should be sufficient. My GET query returns
{"error":{"code":"-2130575338, Microsoft.SharePoint.SPException","message":{"lang":"en-US","value":"The file /IT/vystupnidokumentydoc/filename.docx does not exist."}}}
Same query is OK in browser in user context.
REST call:
private async static Task DoStuffInOffice365(string token)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Add("Accept", "application / json; odata = verbose");
string url;
url = "https://tenant.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/IT/vystupnidokumentydoc/filename.docx')";
using (HttpResponseMessage response = await client.GetAsync(url))
{
var contents = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
Console.WriteLine("Fail!");
else
Console.WriteLine("OK.");
}
}
Get token:
private async static Task<string> GetAccessToken()
{
//authentication context
string authority = "https://login.windows.net/tenant.onmicrosoft.com/";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var certPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
certPath = certPath.Substring(0, certPath.LastIndexOf('\\')) + $"\\{CERT_FILE}";
var certfile = System.IO.File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0, (int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
PRIVATE_KEY_PASSWORD,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate(CLIENT_ID, cert);
var authenticationResult = await authenticationContext.AcquireTokenAsync(P_URL, cac);
return token = authenticationResult.AccessToken;
}
Based on the error message, it seems the file was not found on the location provided. Please ensure the file exits on the site.
Do I have to register this app in SP?
If you were developing SharePoint add-in, yes. And you can refer here for the authentication/authorization for the SharePoint add-in.
If you were not developing an SharePoint add-ins, we also can use the Microsoft Graph-GetItem to get the item and download the drive item through this REST.