Microsoft Graph Api - upload file and invalidRequest - c#

I have a problem uploading files to sharepoint using the graph api.
With the token downloaded from https://developer.microsoft.com/en-us/graph/graph-explorer everything works fine. I get a response with a status of 200.
However, when I want to upload a file with a token received from AD I get an invalid request.
In the scope of my token there is: "Files.ReadWrite Files.ReadWrite.All Group.Read.All Group.ReadWrite.All GroupMember.Read.All GroupMember.ReadWrite.All openid profile Sites.Read.All Sites.ReadWrite.All User.Read email".
Reading the file list works without any problems
Below is the code on how I generate the token to Graph Api

Firstly, pls don't show your code in your picture because we can't copy code from picture directly so that we can't test your code and reproduce your issue.
In your screenshot, I can see you used https://graph.microsoft.com/.default as the scope, and since you used a console application, so you should use client credential flow to generate the author provider so that you can generate a correct access token. And this can also explain why you used the token obtained from graph explorer can work. When we use graph explorer, it will ask us to sign in first so that it can generate an access token which containing delegate api permission. And in your code you used var authProvider = new DelegateAuthenticationProvider.
You also shared the api permissions, but when you used client credential flow, you have to set the application api permission but not the delegate api permission. For this upload file api, the permission should be Files.ReadWrite.All, Sites.ReadWrite.All. Pls don't forget the give the api permission.
By the way, since you've used the graph SDK, you can use my code snippet to call graph api.
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://graph.microsoft.com/.default" };
string tenantId = "TenantId";
string clientId = "ClientId";
string clientSecret = "ClientSecret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var uploadFile = await graphClient.Drives[drives].Root.xxxx;

Related

Not able to get all users from Azure Active Directory

I am using solution mentioned here to get all users from Active Directory however I suspect the code is pulling disabled users from our old Active Directory. The new one is Azure Active Directory. Please let me know what change is required to get below details of only active users from Azure Active Directory:
First Name
Last Name
Email
Enterprise ID
Getting all users in Azure AD can use Microsoft Graph API. Here's the API for listing users. But it doesn't support personal Microsoft account, it only supports work or school accounts. By the way, I'm not sure what is Enterprise ID, could you pls take a look at this section to check if this API contained it?
I assume you have an asp.net core WEB API which is used to getting user list. So you should use code like below.
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "aad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var users = await graphClient.Users.Request().GetAsync();
Then an Azure AD application is required for the variables in code above. Pls follow this document to register the Azure AD app. Since my assumption is based on a web API, no need to add redirect URL here. Now we can get tenantId , clientId in Overview blade, and created the client secret. We also need to modify API permissions blade and add required API permissions. What we need is Application permission User.Read.All,User.ReadWrite.All,Directory.Read.All, Directory.ReadWrite.All.

Presence endpoint returns Forbidden with application permissions

With help from a fellow stacker, I was able to make successful calls to thre MS graph api, at least users, but I have been wanting to get the teams status of a user using the Presence function. I have not been able to get around the 403 Forbidden error. I have read much of the prose MS has written on OAuth 2.0 and have tried sample apps, graph explorer, and tried Postman as well.
I have checked my app permissions in Azure portal and according to the display, Presence is application able, not just delegate:
As I can perform a User.Read.All it is confusing that I cannot call Presence without the error. This is the basic function I got help in writing and it fails on the presence call:
static async Task MainAsync()
{
var tenantId = "some giud";
var clientId = "more guid";
var clientSecret = "even more guid";
try
{
string[] scopes = { "https://graph.microsoft.com/.default" };
ClientSecretCredential clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
GraphServiceClient graphClient = new GraphServiceClient(clientSecretCredential, scopes);
//var users = await graphClient.Users.Request().GetAsync();
//foreach (var user in users)
// Console.WriteLine(user.UserPrincipalName);
var userid = await graphClient.Users["my.name#company.com"].Request().GetAsync();
Console.WriteLine(userid.Id);
var presence = await graphClient.Users["{user id}"].Presence.Request().GetAsync();
// Console.WriteLine(presence)
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I scoured google and MS looking for examples and I found one from MS and this is the link to github:
git clone https://github.com/Azure-Samples/active-directory-dotnetcore-devicecodeflow-v2.git
It will ask to perform a login via a browser and code. I got this code working and it does ask that I log in. I had to alter my registered app to this setting:
https://i.stack.imgur.com/Uz9IK.jpg
All this I did and I still get the 403 forbidden error. I am wondering if anyone has a working set of code that calls Presence and can share either what I am missing, or is this only something done in teams.
Why they have it so restricted is beyond reason given I can get more interesting user data just calling users/{emails}
I tried to reproduce the scenario and get the present status of the user .
I got the similar error forbidden when I gave a client secret which is expired and does not have Presence.Read.All delegated permissions .I only had application permissions
Then I added delegated permissions and user.read permissions (also granted admin consent).
I could get the status successfully with below code and query through graph where it uses access token (as authorization header is mandatory parameter to be passed ).
Note: Getting the presence requires users signed in.So while requesting On behalf of user, delegated permissions are must.
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var presence = await graphClient.Users["c4xx3cf2axxxxa6df-d2xxxx391"].Presence
.Request()
.GetAsync();
Presence of some user with Id
Current user’s presence:
Please make sure to go through all the required constraints to Resolve Microsoft Graph authorization errors - Microsoft Graph | Microsoft Learn
It requires Presence.Read or Presence.Read.All Delegated permissions to query the presence of the user .
Reference : Get presence - Microsoft Graph v1.0 | Microsoft Learn

Authenticate Office 365 IMAP Account using Unattended C# Console

I am developing a .NET Core Console Application (C#) that needs to authenticate to an Office 365 IMAP account. The purpose is to retrieve mail and process CSV file attachments unattended.
The app has been registered on Azure as a Mobile/Desktop app with the RedirectUri set as http://localhost.
The following code causes a new tab to open in Chrome web browser and asks for the Outlook account to use for login. I need to stop the browser from opening and handle authentication completely from code.
Current Code:
using var client = new ImapClient(new ProtocolLogger("imap.log"));
var options = new PublicClientApplicationOptions
{
ClientId = _options.ClientId,
TenantId = _options.TenantId,
RedirectUri = "http://localhost"
};
var publicClientApplication = PublicClientApplicationBuilder
.CreateWithApplicationOptions(options)
.Build();
var scopes = new[]
{
"email",
"offline_access",
"https://outlook.office.com/IMAP.AccessAsUser.All" // Only needed for IMAP
//"https://outlook.office.com/POP.AccessAsUser.All", // Only needed for POP
//"https://outlook.office.com/SMTP.AccessAsUser.All", // Only needed for SMTP
};
var cancellationToken = new CancellationToken();
var authToken = await publicClientApplication
.AcquireTokenInteractive(scopes)
.ExecuteAsync(cancellationToken);
await publicClientApplication
.AcquireTokenSilent(scopes, authToken.Account)
.ExecuteAsync(cancellationToken);
SaslMechanism oauth2;
if (client.AuthenticationMechanisms.Contains("OAUTHBEARER"))
{
oauth2 = new SaslMechanismOAuthBearer(authToken.Account.Username, authToken.AccessToken);
}
else
{
oauth2 = new SaslMechanismOAuth2(authToken.Account.Username, authToken.AccessToken);
}
await client.AuthenticateAsync(oauth2);
await client.DisconnectAsync (true);
This line triggers a browser window to open https://login.microsoftonline.com/:
var authToken = await publicClientApplication
.AcquireTokenInteractive(scopes)
.ExecuteAsync(cancellationToken);
This console application will be run unattended. How do I obtain the token and authenticate without a web browser opening up?
This is an answear to your latest comment, as it's my final recommendation. So, first of all, you should decide if you want to acess the data on behalf of user, or as an app granted permissions by admin.
First step is to register your app.
Second step is getting the acess token. This is going to differ based on the method you chose. Tutorial for each: acting on behalf of the user or acting without the user, but granted permission from admin.
Once you have the acess token, you can call the Microsoft Graph API. The important thing is, you always have to call Microsoft Graph API. There is no other official way (as far as I know) of comunicating with Microsoft's services. You can try the requests with the Microsoft Graph Explorer, however it's VERY limited with it's defaul urls/parameters, so I suggest taking a look at the docs.
From what you've described, you first want to obtain UserID. The way of doing this is going to vary based on what type of auth you chose.
If you chose to act on behalf of user, you should be able to get that (ID) using this endpoint: https://graph.microsoft.com/v1.0/me/
If you chose to act as an app with admin consent, you should be able to search for user using the https://graph.microsoft.com/v1.0/me/people/?$search= with search query parameters. Here are the docs for this endpoint
Now, the only thing left, is to supply that ID to one of the Outlook api methods. You can find docs for them here. Specifically, it seems like you want to list all messages and then read a specific message.
Also, keep an eye on what methods you use with which type of auth. On behalf of user, you usually want url's that contain /me, on behalf of app with given admin privelages, you usually want some endpoint that enables you to pass user id.
Hope I helped!
PS: There is no code in this response, because there is a lot of stuff that just cannot be coded without your decisions, actions on Azure and so on. I suggest you read a little bit about auth and graph api using microsoft docs I linked earlier.
This code worked for me using MSAL after registering the app in azure and getting a client secret.
var options = new ConfidentialClientApplicationOptions
{
ClientId = "<ClientID or Application ID>",
TenantId = "<Azure TenantId>",
RedirectUri = "http://localhost"
};
string clientSecret = "<Client Secret Goes here>";
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.CreateWithApplicationOptions(options)
.WithClientSecret(clientSecret)
.Build();
var scopes = new string[] {
"https://outlook.office365.com/.default"
};
var authToken = await confidentialClientApplication.AcquireTokenForClient(scopes).ExecuteAsync();

How do we store a user's GMail OAuth access token with Mailkit in an ASP.NET Core web site?

I've trying to use Mailkit and OAuth to read a user's Gmail inbox, and have followed the sample code found in the Mailkit FAQ. For the record, here is the code I'm using...
Note that I'm currently storing the token in a file in the site's content root, just until I get this working. After that, I'll be implementing an Entity Framework IDataStore, so please don't be concerned about the security issue of the code shown here
private async Task<ImapClient> GetMailClientOAuth(string account, string clientId, string clientSecret) {
ClientSecrets clientSecrets = new() {
ClientId = clientId,
ClientSecret = clientSecret
};
GoogleAuthorizationCodeFlow codeFlow = new(new GoogleAuthorizationCodeFlow.Initializer {
DataStore = new FileDataStore($#"{_env.ContentRootPath}\{account}"),
Scopes = new[] { "https://mail.google.com/" },
ClientSecrets = clientSecrets
});
LocalServerCodeReceiver codeReceiver = new();
AuthorizationCodeInstalledApp authCode = new(codeFlow, codeReceiver);
UserCredential credential = await authCode.AuthorizeAsync(account, CancellationToken.None);
if (authCode.ShouldRequestAuthorizationCode(credential.Token)) {
await credential.RefreshTokenAsync(CancellationToken.None);
}
SaslMechanismOAuth2 oauth2 = new(credential.UserId, credential.Token.AccessToken);
ImapClient client = new();
await client.ConnectAsync("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
await client.AuthenticateAsync(oauth2);
return client;
}
The code worked fine in a test console app, and I'm now trying to integrate the code into my ASP.NET Core web app.
I set up a web project in my Google Cloud dashboard, added the Gmail API and created an OAuth credential, just like I did for the console app.
When I try the code that access Gmail, I get a window pop up in my browser with a message...
Authorization Error
Error 400: redirect_uri_mismatch
The redirect URI in the request, http://localhost:54392/authorize/, does not match the ones authorized for the OAuth client
Now I have no idea where it picked up http://localhost:54392/authorize/, as it doesn't bear any resemblance to anything I'm using (not that I gave it any URL anyway), but I followed the link that was in the message, and set a URL that is on my web site (when running on my local machine).
I have double-checked that this URL has been saved with the credential, but when I try and access the page on my site, I get the same error, with the same URL it thinks should be there.
I have checked the client ID and secret, and I'm definitely using the right ones.
Anyone any idea where it's getting http://localhost:54392/authorize/ from, and how I tell it to use something else?
Thanks
Not sure if this is the right thing to do, but I solved this problem by setting the project type to Desktop Application, which doesn't require a redirect URI.

Authenticate to Azure API App using ADAL

I have an Azure API App marked as "Public (authenticated)" and set up an Azure Active Directory identity in the associated gateway as detailed in Protect an API App.
I then created a native application in the same Azure Active Directory Tenant and added permission to access the Gateway in the delegated permissions.
Using ADAL and the following code, I'm able to successfully authenticate and get an access token, but I can't figure out how to use it to access my API app.
string Tenant = "[xxx].onmicrosoft.com";
string Authority = "https://login.microsoftonline.com/" + Tenant;
string GatewayLoginUrl = "https://[gateway].azurewebsites.net/login/aad";
string ClientId = "[native client id]";
Uri RedirectUri = new Uri("[native client redirect url]");
async Task<string> GetTokenAsync()
{
AuthenticationContext context = new AuthenticationContext(Authority);
PlatformParameters platformParams = new PlatformParameters(PromptBehavior.Auto, null);
AuthenticationResult result = await context.AcquireTokenAsync(GatewayLoginUrl, ClientId, RedirectUri, platformParams);
return result.AccessToken;
}
I've tested the API app manually entering an x-zumo-auth header I get in Chrome and it works then, but not with a token I get using ADAL. I've also tried the browser forms described in their sample code which works but doesn't give me a refresh token.
How do I need to set up my authentication code so I can use a TokenCache and ADAL with my API app?
Generally you pass the access token in the Authorization header when when calling a web api:
Authorization: Bearer ThisIsTheAccessTokenYouRecievedFromADAL
You may want to use AppServiceClient to authenticate the user and invoke a protected API App endpoint. Install Microsoft.Azure.AppService SDK (-pre) Nuget package to your client project.
You can find more details in the AzureCards samples on GitHub - https://github.com/Azure-Samples/API-Apps-DotNet-AzureCards-Sample

Categories