How to choose cached credential with Microsoft Graph Service Client - c#

I have MSAL identity client app that I am able to use to get access token for a user.
I am now trying to use Microsoft.Graph.Auth instead to send requests. Below code shows how MSAL IndentityClientApp is passed to Graph.Auth.
DeviceCodeProvider authProvider = new DeviceCodeProvider(IdentityClientApp, Scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
I cannot figure how to specify which cached credential to use, if more than one credential is cached.
When I was using MSAL on its own, I was using below code to specify the user account. How do I do this with MS.Graph?
authResult = await IdentityClientApp.AcquireTokenSilent(Scopes, account).ExecuteAsync();

Using this approach to create GraphClient allows me to control the account that is used:
GraphServiceClient graphClient = new GraphServiceClient(MSGraphURL, new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", OutlookToken);
}));
Source

Related

How to get profile picture with Microsoft.Graph SDK or User.Identity

I have implemented authentication successfully with the quickstart project in Microsoft Azure, but now I'm stuck with getting the profile picture of a logged-in user.
I have tried using Microsoft.Graph SDK, but the Photo keep getting null
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(ClientId)
.WithTenantId(TenantId)
.WithClientSecret(ClientSecret)
.Build();
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(confidentialClientApplication);
// Create an authentication provider.
ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
// Configure GraphServiceClient with provider.
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);
var users = await graphServiceClient.Users.Request().GetAsync();
I also tried getting a specific user with id and .Select("Photo") but the result is the same
var temp = await graphServiceClient.Users[user_id]
.Request()
.Select("Photo")
.GetAsync();
Maybe my implementation was wrong, any help or suggestions will be appreciated.
To get a photo as a stream you can use this code
using (var photoStream = await graphServiceClient.Users[user_id].Photo.Content
.Request()
.GetAsync())
{
// your code
}

Get my user's information using MS Graph API

I'm trying to get my information using the Graph API SDK for C# (the idea is the application running without user interaction), as shows code sample below:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("xxx")
.WithTenantId("xxx")
.WithClientSecret("xxx")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = await graphClient.Me.Request()
.Select(u => new
{
u.DisplayName,
u.Mail,
u.UserPrincipalName
})
.GetAsync();
It's returning 'WaitingForActivation' on Status, I don't know where is the problem.
This is not like you think. If you call the /me endpoint, you must log in the user. It actually obtains the logged-in user information, so you cannot use the client credential flow to obtain the access token. For the client credential flow, it is usually used for server-to-server interactions that must run in the background and do not interact with the user immediately(No user logged in). For the /me endpoint, it needs to accept the user token, because it has user interaction. So, I suggest you use auth code flow to get access token,which requires you to log in to the user and obtain the authorization code, and then use the authorization code to redeem the access token.
Authorization code provider:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret) // or .WithCertificate(certificate)
.Build();
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(confidentialClientApplication, scopes);
By the way, if you want to get user information in a scenario where no user is logged in, you can call the /users endpoint:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("xxx")
.WithTenantId("xxx")
.WithClientSecret("xxx")
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = await graphClient.Users["{userId or userPrincipalName}"].Request()
.Select(u => new
{
u.DisplayName,
u.Mail,
u.UserPrincipalName
})
.GetAsync();

Get user mail from o365 using graph sdk in a console app

I want to read a users mail from a .net console app without user interaction.
I would like to give the app access to read only selected user(s) mail and not as global admin that can read all users mail.
I would like to use the .net Microsoft.Graph library and not raw REST interface.
I think i need more or less step-by-step instructions i this i seven possible
I have created an new application registration and a client secret
If i give Application permission to mail it works but i cant get delegated permission to work.
The code is just one of many i have tried , but i cant really find any that do what i want to do.
var tenantId = "domain123.onmicrosoft.com";
var client_Id = "1234567789";
var client_Secret = "123243456777";
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(client_Id)
.WithClientSecret(client_Secret)
.WithAuthority(new Uri(authority))
.WithLogging(MyLoggingMethod, LogLevel.Verbose,
enablePiiLogging: true,
enableDefaultPlatformLogging: true)
.Build();
// Acquire tokens for Graph API
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["user123#domain123.onmicrosoft.com"].Messages.Request().GetAsync();
Code: NoPermissionsInAccessToken
Message: The token contains no permissions, or permissions can not be understood.
Inner error
Can I suggest rather than trying to roll your own DelegateAuthenticationProvider, that you use one of the provided ones? e.g.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
var graphClient = new GraphServiceClient(authProvider);
Docs: https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#ClientCredentialsProvider
Nuget: https://www.nuget.org/packages/Microsoft.Graph.Auth/1.0.0-preview.1
I did try this earlier with success but since the comment on UsernamePasswordProvider is NOT RECOMMENDED I was hoping for a better solution.
To get this to work I did following steps
Create new App registration with redirect as public client/native = myapp://auth
Go into Authentication and set "Treat application as public client" = Yes
To allow app to access user data, go to:
https://login.microsoftonline.com/[tenant]/oauth2/v2.0/authorize?client_id=[client_id]&response_type=code&redirect_uri=myapp://auth&response_mode=query&scope=user.read&state=12345
Login with user that you want to use in app , this page will hand but it ok.
Is this really the best way to do what i want?
static async Task Main(string[] args)
{
var GraphClient = CreateGraphClient();
User me = await GraphClient.Me.Request()
.WithUsernamePassword("user123#domain123.onmicrosoft.com", new NetworkCredential("", "MyPassword").SecurePassword)
.GetAsync();
Console.WriteLine("OK:" + me.DisplayName);
Console.ReadLine();
}
public static GraphServiceClient CreateGraphClient()
{
string clientId = "1234567-1234-1234-12345-1234567890";
string tenantID = "domain123.onmicrosoft.com";
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, null);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
Another solution could be to make an application permission and then set access policy using PowerShell new-applicationaccesspolicy
https://learn.microsoft.com/en-us/powershell/module/exchange/organization/new-applicationaccesspolicy?view=exchange-ps
I have not tried that one yet, anyone know if this could help?

How to fetch all users from Azure Active Directory Group using C#

We have an application developed in ASP.NET MVC. We also have Acitve Directory configured in Azure and it has some Groups into it.
Requirement is, we need to fetch all users from Azure Active Directory's Group and add a new user into it.
We are using code below and it is asking extra authentication I guess. we want to provide all authentication in code it self without giving popup wondow to authenticate. Can you please help with this
// Build a client application.
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create("clientID")
.WithTenantId("tenantId")
.Build();
// Create an authentication provider by passing in a client application and graph scopes.
DeviceCodeProvider authProvider = new DeviceCodeProvider(publicClientApplication, new[] { "User.Read" });
// Create a new instance of GraphServiceClient with the authentication provider.
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var members = await graphClient.Groups["groupId"].Members
.Request()
.GetAsync();
Above code shows a message as
"To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code G9277ULC9 to authenticate."
How can we provide all authentication information in code itself to avoid this step?
Updated
API permissions are as below -
Thank you in advance.
You could use the Microsoft Graph SDK to do that.
List members of a group:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var members = await graphClient.Groups["{id}"].Members
.Request()
.GetAsync();
Add member to a group:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var directoryObject = new DirectoryObject
{
AdditionalData = new Dictionary<string, object>()
{
{"#odata.id","https://graph.microsoft.com/v1.0/directoryObjects/{id}"}
}
};
await graphClient.Groups["{id}"].Members.References
.Request()
.AddAsync(directoryObject);
Update:
If you want a non-interactive way, you need to use the client credential flow, i.e. create the authProvider instance as Client credentials provider.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);

How do I use OpenID and Microsoft Graph to enumerate Azure ActiveDirectory groups?

I have the following values:
OpenID App Key
OpenID Audience
OpenID Client ID
OpenID Login URL/Domain
Token Endpoint (https://login.windows.net/<tenant-id>/oauth2/token)
Resource URL (https://graph.windows.net)
How do I use these values to create a Microsoft Graph service client?
var graphClient = new GraphServiceClient(
// What goes here?
);
I need the client to enumerate AAD groups.
Based on your description, I assumed that you are using the AAD v1.0, for using the Microsoft Graph client SDK, you need to add Required permissions to the Microsoft Graph API with the application permissions or delegated permissions for your AAD application on Azure Portal. Differences between application permissions and delegated permissions, you could follow here.
For web application and use the user-based authentication flow, you could follow the samples below:
Calling the Azure AD Graph API in a web application
Microsoft Graph Snippets Sample for ASP.NET 4.6
Note: For your scenario, you need to combine the code in the above two samples. Or you could just create the AAD v2.0 application and just use the second sample.
For server to server scenario, you could just use ADAL to retrieve the access token to initialize your GraphServiceClient:
private static async Task<string> GetAccessTokenAsync()
{
string tenantId = "<tenantId>";
string clientId = "<clientId>";
string clientSecrets = "<clientSecrets>";
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = null;
var context = new AuthenticationContext(String.Format("https://login.windows.net/{0}", tenantId));
var authParam = new PlatformParameters(PromptBehavior.Never, null);
var result = await context.AcquireTokenAsync(
"https://graph.microsoft.com"
, new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, clientSecrets)
);
return result.AccessToken;
}
//initialize the GraphServiceClient instance
var graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var token = await GetAccessTokenAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}));

Categories