How to access secret key from Azure vault - c#

I have followed the official document and developed the code, but it's not working. I have to get the secret key stored in the vault.
When I debug, it just stops at the mentioned step and doesn't go further. I also tried without debugging. It's the same. Are there any more settings/permissions to make.
private void EncryptFields()
{
string publicKey = GetAzureVaultSecret().Result;
}
public static async Task<string> GetAzureVaultSecret()
{
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetAzureVaultAccessToken));
var secret = await kv.GetSecretAsync(GlobalConstants.AzureVaultURLSecretURI);
return secret.Value;
}
public static async Task<string> GetAzureVaultAccessToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(GlobalConstants.AzureVaultClientId, GlobalConstants.AzureVaultClientSecret);
//STOPS AT THE BELOW STEP AND NOTHING HAPPENS
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the token");
}
return result.AccessToken;
}
Log:
ManagedPoolThread #3 00:23:46 WARN Memory usage exceeded the
MemoryMonitor threshold. ManagedPoolThread #3 00:23:46 WARN Memory
usage: 3,21,95,91,168 ManagedPoolThread #3 00:23:46 WARN Number of
suppressed logs due to the minimum time between log entries: 10

Related

AcquireTokenAsync expiring after 1 or 2 hours

I am trying to call AcquireTokenAsync it is working properly but after sometime it is not responding and it is not providing any result.
please refer the below code how to solve my issue
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(WebConfigurationManager.AppSettings["ClientId"],
WebConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
public static string GetKeyVaultSecret(string secretName)
{
try
{
var secretUri = WebConfigurationManager.AppSettings["SecretUri"];
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
var secret = kv.GetSecretAsync(secretUri, secretName).Result;
return secret.Value;
}
catch(Exception ex)
{
return null;
}
}
For access token, the default time is 1 hour. After 1 hour, the client must use the refresh token to (usually silently) acquire a new refresh token and access token.
You can change access token lifetime to the maximum to one day with this tutorial.
New-AzureADPolicy -Definition #('{"TokenLifetimePolicy":{"Version":1,"AccessTokenLifetime":"24:00:00","MaxAgeSessionSingleFactor":"02:00:00"}}') -DisplayName "WebPolicyScenario" -IsOrganizationDefault $false -Type "TokenLifetimePolicy"
For more details about token lifetime you can refer to this article.

Microsoft Graph api code in C# displays only limited number of users

I am running below Microsoft Graph Api code:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace AADConsole2
{
class Program
{
private const string aadInstance = "https://login.microsoftonline.com/{0}";
// private const string ResourceUrl = "https://graph.windows.net";
private const string resource = "https://graph.microsoft.com";
private const string GraphServiceObjectId = "XXX";
private const string TenantId = "XXX";
private const string tenant = "XXXX.onmicrosoft.com";
private const string ClientId = "XXX";
private static string appKey= "XXXX";
static string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, aadInstance, tenant);
private static HttpClient httpclient = new HttpClient();
private static AuthenticationContext context = null;
private static ClientCredential credential = null;
static void Main(string[] args)
{
context = new AuthenticationContext(authority);
credential = new ClientCredential(ClientId, appKey);
Task<string> token = GetToken();
token.Wait();
Console.WriteLine(token.Result);
Task<string> users = GetUsers(token.Result);
users.Wait();
Console.WriteLine(users.Result);
Console.ReadLine();
}
private static async Task<string> GetUsers(string result) {
//throw new NotImplementedException();
string users = null;
var uri = "https://graph.microsoft.com/v1.0/users";
httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result);
var getResult = await httpclient.GetAsync(uri);
if(getResult.Content != null)
{
users = await getResult.Content.ReadAsStringAsync();
}
return users;
}
private static async Task<string> GetToken()
{
AuthenticationResult result = null;
string token = null;
result = await context.AcquireTokenAsync(resource, credential);
token = result.AccessToken;
return token;
}
}
}
I am getting the results of user detail printed on console ,but only limited number of users are printed. i.e only whose name starts with letter 'a'.
And also some user details are missing. How to get all user details .Am i missing some api in this code?
Thanks.
Most Microsoft Graph endpoints return paged result sets. Your initial request only returns the first page of data. To retrieve the next page, you follow the URI provided in the #odata.nextLink property. Each subsequent page will return the next page's #odata.nextLink until you the last page of data (denoted by the lack of a #odata.nextLink in the result). There is a step-by-step walkthrough of how this works at Paging Microsoft Graph data in your app.
The single most important tip I can give you here is to not use $top to force it to return large pages of data. This is an extremely inefficient method for calling the API and inevitably leads to network errors and request throttling. It also doesn't eliminate the need to handle paging since even $top=999 (the maximum) can still return multiple pages.
Implement paging, keep your page sizes small, and process the results after each page is returned before moving on to the next page. This will ensure you capture all of the data and allow your application to pick up where it left off should it encounter any errors during processing.
Something like this will get all of your users. Also if you want properties outside of the default you need need to specify them with a select. Not all properties are returned by default.
String Properties = "Comma Separated List of Properties You actaully Need";
List<User> AllUsers = new List<User>();
IGraphServiceUsersCollectionPage users = graphServiceClient.Users
.Request()
.Select(Properties)
.GetAsync()
.Result;
do
{
QueryIncomplete = false ;
AllUsers.AddRange(users);
if (users.NextPageRequest != null)
{
users = users.NextPageRequest.GetAsync().Result;
QueryIncomplete = true;
}
}while (QueryIncomplete);
return AllUsers;

Failing to get Team details from a c# console app while it works from Graph Explorer

I need to collect Microsoft Teams data from a C# console application using Microsoft Graph.
I am using ADAL and cloned the authentication methods from the https://github.com/microsoftgraph/console-csharp-connect-sample sample.
The only difference is that I am using an HttpClient client and not a GraphServiceClient that does not implement Teams objects.
The list of required permissions have been determined with a Fiddler trace of a request made with Graph Explorer (no need for User.Read.All or User.Write.All) :
User.Read, Mail.Send, Files.ReadWrite, User.ReadWrite, User.ReadBasic.All, Sites.ReadWrite.All, Contacts.ReadWrite, People.Read, Notes.ReadWrite.All, Tasks.ReadWrite, Mail.ReadWrite, Files.ReadWrite.All, Calendars.ReadWrite
Everything works fine with my console app as long as I am not requesting any Teams resource:
I can get the list of groups "that are Teams" with the following
request: https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/any(v:v eq 'Team')&$select=id,displayname,groupTypes,resourceBehaviorOptions,resourceProvisioningOptions
I can successfully get the group details with: https://graph.microsoft.com/beta/groups/{groupId}
But when I try to get the team view of that group (which I am member of) it fails with HTTP
403-Unautorized:
https://graph.microsoft.com/beta/groups/{groupId}/team
Very
frustrating to see that this last step is working well from the
Graph Explorer
My problem is very similiar with Access Denied when querying Teams in Microsoft Graph but in my case I am member of the teams I am trying to access and the request works with Graph Explorer.
Code details:
class AuthenticationHelper
{
// The Client ID is used by the application to uniquely identify itself to the v2.0 authentication endpoint.
static string clientId = Constants.ClientId;
// The list of required permissions have been determined with a Fiddler trace of a request made with Graph Explorer
// e.g. below are the permissions Grap Explorer requires to run the sample requests
public static string[] Scopes = {
"User.Read"
, "Mail.Send"
, "Files.ReadWrite"
, "User.ReadWrite"
, "User.ReadBasic.All"
, "Sites.ReadWrite.All"
, "Contacts.ReadWrite"
, "People.Read"
, "Notes.ReadWrite.All"
, "Tasks.ReadWrite"
, "Mail.ReadWrite"
, "Files.ReadWrite.All"
, "Calendars.ReadWrite"
};
public static PublicClientApplication IdentityClientApp = new PublicClientApplication(clientId);
public static string UserToken = null;
public static DateTimeOffset Expiration;
//-----------------------------------------------------------------------------------------------------------------
public static async Task<HttpClient> GetAuthenticatedHttpClient()
{
HttpClient client = null;
try
{
client= new HttpClient(new HttpClientHandler { UseCookies = true });
var token = await GetTokenForUserAsync();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// This header has been added to identify our sample in the Microsoft Graph service. If extracting this code for your project please remove.
client.DefaultRequestHeaders.Add("SampleID", "TestCSharp-AzureToken");
return client;
}
catch (Exception ex)
{
Debug.WriteLine("Could not create a graph client: " + ex.Message);
}
return client;
}
//-----------------------------------------------------------------------------------------------------------------
public static async Task<string> GetTokenForUserAsync()
{
AuthenticationResult authResult;
try
{
IEnumerable<IAccount> accounts = await IdentityClientApp.GetAccountsAsync();
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await IdentityClientApp.AcquireTokenSilentAsync(Scopes, firstAccount);
UserToken = authResult.AccessToken;
}
catch (Exception)
{
if (UserToken == null || Expiration <= DateTimeOffset.UtcNow.AddMinutes(5))
{
authResult = await IdentityClientApp.AcquireTokenAsync(Scopes );
UserToken = authResult.AccessToken;
Expiration = authResult.ExpiresOn;
}
}
return UserToken;
}
}
//----------------------------------------------------
// Console entry point
class Program
{
//public static GraphServiceClient client;
public static HttpClient _client;
static async Task<string> GetHttpResponse(string url)
{
string responseBody = null;
_client = await AuthenticationHelper.GetAuthenticatedHttpClient();
HttpResponseMessage response = await _client.GetAsync(url);
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
responseBody = await response.Content.ReadAsStringAsync();
logger.Trace(responseBody);
}
return responseBody;
}
static void Main(string[] args)
{
// call 1 is working: list groups that "are Microsoft Teams"
string s;
string url = "https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/any(v:v eq 'Team')&$select=id,displayname,groupTypes,resourceBehaviorOptions,resourceProvisioningOptions";
s = await GetHttpResponse(url);
Console.WriteLine(s);
// call 2 is working: Display details of one of these groups
Console.Write($"Enter the id of the group/teams to search for: ");
string groupId = Console.ReadLine().Trim().ToLower();
url = $"https://graph.microsoft.com/beta/groups/{groupId}";
s = await GetHttpResponse(url);
Console.WriteLine(s);
// call 3 is failing: Display the team view of this groups
url = url + "/team";
s = await GetHttpResponse(url);
Console.WriteLine(s);
}
}
You're missing a scope. You need to have Group.Read.All in order to read a Group or Team.

Azure Blob timeouts after encryption was applied

I started encrypting my Azure blobs. Now I am occasionally getting 500 - The request timed out on blob operations (on both downloading and uploading blobs) after the request halts for about 30 seconds or so. After one of these timeouts, no other blob operation will work through the app, unless I restart my Azure website. Once restarted, everything runs as expected for a while.
Example: If I access an encrypted image through my application (I'm using the WebAPI to pull it and display it to the user) it shows up fine, but then if I try and access the same file hours later, the request halts and eventually times out. After that, I get the same issue while accessing any other file through my web app. However, if I access the direct url of the blob, then I can access the file (even though it's encrypted and therefore useless).
I cannot say with certainty what is causing this and when would the issue start occurring as I am not the only person accessing the app, so there's a good chance that the issue might have started before my failed request. Also, I never had issues as such before encryption was applied, nor did I have issues while testing encryption locally.
Any idea why this is happening, or maybe how can I prevent this? I am attaching my code below if it helps:
public async Task<Tuple<string, string>> UploadToStorage(CloudBlobContainer container, Stream stream, string reference, string contentType, byte[] byteArray = null) {
var blockBlob = container.GetBlockBlobReference(reference);
blockBlob.Properties.ContentType = contentType;
var cloudResolver = new KeyVaultKeyResolver(GetToken);
var rsa = await cloudResolver.ResolveKeyAsync(new BlobConfig().BlobKeyVault, CancellationToken.None);
var policy = new BlobEncryptionPolicy(rsa, null);
var options = new BlobRequestOptions { EncryptionPolicy = policy };
if (byteArray != null) blockBlob.UploadFromByteArray(byteArray, 0, byteArray.Length, null, options);
else blockBlob.UploadFromStream(stream, stream.Length, null, options);
return Tuple.Create(new Config().BaseUrl + "/api/blobs/" + container.Name + "/" + reference, blockBlob.Properties.ContentType);
}
public BlobDto DownloadBlob(string container, string filename) {
var account = CloudStorageAccount.Parse(new BlobConfig().StorageConnectionString);
var blobClient = account.CreateCloudBlobClient();
var blobContainer = blobClient.GetContainerReference(container);
var blob = blobContainer.GetBlockBlobReference(filename);
var cloudResolver = new KeyVaultKeyResolver(GetToken);
var policy = new BlobEncryptionPolicy(null, cloudResolver);
var options = new BlobRequestOptions { EncryptionPolicy = policy };
var m = new MemoryStream();
blob.DownloadToStream(m, null, options);
return new BlobDto { Blob = m.ToArray(), BlobContentType = blob.Properties.ContentType };
}
private async static Task<string> GetToken(string authority, string resource, string scope) {
var config = new BlobConfig();
var clientCredential = new ClientCredential(config.BlobClientId, config.BlobClientSecret);
var authContext = new AuthenticationContext(authority);
var result = await authContext.AcquireTokenAsync(resource, clientCredential);
if (result == null) throw new InvalidOperationException("Failed to obtain the access token");
return result.AccessToken;
}
I replaced all the synchronous blob related calls with asynchronous calls, particularly the downloading call. Also changed from stream to bytearray, but I don't think that's relevant:
await blob.DownloadToByteArrayAsync(byteArray, 0, null, options, null);

Fetching User by Id does not return

I have made a successful connection to Azure AD (at least I hope so, unable to tell the details), and now I am trying to get some user's details. But the step-through debugger does not proceed over the following line:
IUser result = azureDirectoryClient.Users.GetByObjectId(someId).ExecuteAsync().Result;
That I got until here, no error is thrown and the function does not return - what does that tell me? How can I debug the issue further?
What do you catch when you debug this:
try
{
IUser result = azureDirectoryClient.Users.GetByObjectId(objectId).ExecuteAsync().Result;
}
catch(exception e)
{
console.white(e.message)
}
How do you connect to Azure AD? Make sure you get the accurate Accesstoken:
public static string GetTokenForApplication()
{
AuthenticationContext authenticationContext = new AuthenticationContext(Constants.AuthString, false);
// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(Constants.ClientId, Constants.ClientSecret);
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(Constants.ResourceUrl, clientCred);
string token = authenticationResult.AccessToken;
return token;
}
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(new Uri(https://graph.windows.net, TenantId),
async () => await GetTokenForApplication());
Don't forget to wait for the result somewhere using the await Keyword, also specify that the method is async so it runs asynchronously
public async Task<ActionResult> SomeAction()
{
IUser result = await azureDirectoryClient.Users.GetByObjectId(someId).ExecuteAsync();
return View(result);
}

Categories

Resources