I am using the C# package to Microsoft Graph API. I can read messages from the Graph API. Now I'd like to translate the message IDs like shown here:
https://learn.microsoft.com/de-de/graph/api/user-translateexchangeids?view=graph-rest-1.0&tabs=csharp
var translatedIds = client.Users[firstMailboxElement.SourcePostbox]
.TranslateExchangeIds(toBeTranslated, ExchangeIdFormat.RestImmutableEntryId, ExchangeIdFormat.RestId)
.Request()
.PostAsync()
.Result;
When I do so I get the following Exception:
System.AggregateException
One or more errors occurred.
(Code: Request_BadRequest Message: Specified HTTP method is not allowed for the request target.
Inner error: AdditionalData: date: 2021-12-15T06:52:45 [...])
Which does not seem to make sense, since I cant change the HTTP Method.
Any ideas how to fix this?
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_client_id";
var clientSecret = "client_secret";
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var inputIds = new List<String>()
{
"asdf"
};
var sourceIdType = ExchangeIdFormat.RestId;
var targetIdType = ExchangeIdFormat.RestImmutableEntryId;
var res = graphClient.Users["user_id"].TranslateExchangeIds(inputIds, targetIdType, sourceIdType).Request().PostAsync();
var a = res.Result;
I figured out, that the firstMailboxElement.SourcePostbox was null, when the exception occoured.
So the call went to client.Users[null], so the request URL was incomplete.
Related
I tried to use MS Graph API to implement a backend API to access other users email setting (for getting out-of-office message). As it is backend API, client credential flow is used. I already granted the permissions "MailboxSettings.Read" and "MailboxSettings.ReadWrite" with application type.
I used my free Azure account for testing. Assume my login account is test#hotmail.com, then my Azure domain is testhotmail.onmicrosoft.com.
I created one more user client#testhotmail.onmicrosoft.com
I can get the result using Graph Explorer as below
https://graph.microsoft.com/v1.0/users/test#hotmail.com
https://graph.microsoft.com/v1.0/users/test#hotmail.com/mailboxSettings
https://graph.microsoft.com/v1.0/users/client#testhotmail.onmicrosoft.com
But it return error for below using Graph Explorer
{
"error": {
"code": "ErrorInvalidUser",
"message": "The requested user 'client#testhotmail.onmicrosoft.com' is invalid."
} }
https://graph.microsoft.com/v1.0/users/client#testhotmail.onmicrosoft.com/mailboxSettings
3a. If call by MS Graph SDK to get the user info for client#testhotmail.onmicrosoft.com as below, it is success
var scopes = new[] { "https://graph.microsoft.com/.default" };
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var user = await graphClient.Users["client#testhotmail.onmicrosoft.com"].Request().GetAsync();
3b. If call by MS Graph SDK to get the user info for test#hotmail.com, it returns error
Microsoft.Graph.ServiceException: 'Code: Request_ResourceNotFound
Message: Resource 'test#hotmail.com' does not exist or one of its
queried reference-property objects are not present.
var user = await graphClient.Users["test#hotmail.com"].Request().GetAsync();
If call by MS Graph SDK to get the mailbox setting as below, it returned error
Microsoft.Graph.ServiceException: 'Code: ErrorInvalidUser Message: The
requested user 'test#hotmail.com' is invalid.
var scopes = new[] { "https://graph.microsoft.com/.default" };
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var mail = await graphClient.Users["test#hotmail.com"].Request().Select("MailboxSettings").GetAsync();
Or returned error for below
Microsoft.Graph.ServiceException: 'Code: ResourceNotFound Message:
Resource could not be discovered.
var mail = await graphClient.Users["client#testhotmail.onmicrosoft.com"].Request().Select("MailboxSettings").GetAsync();
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 user = await graphClient.Users["xx#xx.onmicrosoft.com"]
.Request()
.Select("MailboxSettings")
.GetAsync();
var automaticRepliesSetting = user.MailboxSettings.AutomaticRepliesSetting;
Could you pls try this? By the way you may also try to add the 2 application permissions which mentioned in the document: MailboxSettings.Read, MailboxSettings.ReadWrite. And the most important is, your error message is invalid user, so I'm afraid you can use user_PrincipalName instead of myuser#hotmail.com. You can try to get the user_id in Azure AD potal or from the result for await graphClient.Users["myuser#hotmail.com"].Request().GetAsync();.
You are using hotmail.com , as per the doc you should also have either a personal Microsoft account with a mailbox on Outlook.com, or a Microsoft work or school account.
Hope this helps
Thanks
I'm working to send a message to Teams by using Graph API.
my application is a daemon application that sends a message automatically in the background.
I have written code like an official reference link below:
https://learn.microsoft.com/en-gb/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider
in my case, I use the client-credentials-provider but, I still can't send a message, and always get the below error message.
surely I have already registered my application in Azure and set for the grant of scope
How can I fix this?
Following this api document, you need to give Application api permission Teamwork.Migrate.All, and try this code below:
using Azure.Identity;
using Microsoft.Graph;
public void sendMesgAsync()
{
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_client_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var chatMessage = new ChatMessage
{
Body = new ItemBody
{
Content = "Hello World"
}
};
await graphClient.Teams["{team-id}"].Channels["{channel-id}"].Messages
.Request()
.AddAsync(chatMessage);
}
I am trying to create Online Meeting using microsoft graph api without login into AzureActiveDirectory with asp.net web application.For this my app has below permissions which are required as per documentation https://learn.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=csharp with client credential auth flow https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow without immediate interaction with a user.I am able to retrive access token successfully as per client-creds-grant-flow.
I tried Micosoft.Graph and Micosoft.Graph.Beta still getting 404 error.
Create online meeting code
var graphClient = GetAuthenticatedClientCredential();
var onlineMeeting = new OnlineMeeting
{
StartDateTime = DateTimeOffset.Parse("2020-10-01T10:30:34.2444915+00:00"),
EndDateTime = DateTimeOffset.Parse("2020-10-01T11:00:34.2464912+00:00"),
Subject = "Create Online Meeting-Without user login to Office 365"
};
return await graphClient.Me.OnlineMeetings
.Request()
.AddAsync(onlineMeeting);
Access Token code
public static async Task<string> GetUserAccessTokenAsyncByCc()
{
IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder.Create(appId)
.WithTenantId(appTenantId)
.WithClientSecret(appSecret)
.Build();
string[] scopes1 = new string[] { "https://graph.microsoft.com/.default" };
//string[] scopes1 = new string[] { "https://graph.microsoft.com/OnlineMeetings.ReadWrite.All" };
// string[] scopes1 = new string[] { "https://graph.microsoft.com/beta/OnlineMeetings.Read.All" };
//string[] scopes1 = new string[] { "https://graph.microsoft.com/beta/.default" };
var result = await cca.AcquireTokenForClient(scopes1).ExecuteAsync();
return result.AccessToken;
}
and Auth Provider code
public static GraphServiceClient GetAuthenticatedClientCredential()
{
DelegateAuthenticationProvider provider = new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string accessToken = await GetUserAccessTokenAsyncByCc();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
});
GraphServiceClient graphClient = new GraphServiceClient(provider);
return graphClient;
}
app permission image
below are the necessary app permission
You can only use delegated permissions to create an onlineMeeting, so you must log in as a user, and you cannot use the client credential flow. You need to use the auth code flow to obtain the token.
I'm trying to upgrade one of the projects I'm working on to use the Microsoft.Identity.Web nuget package. So far really working well but I'm having trouble figuring out how to add additional claims which I was previously doing by the following:
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Events.OnAuthorizationCodeReceived = async ctx =>
{
var serviceProvider = services.BuildServiceProvider();
var distributedCache = serviceProvider.GetRequiredService<IDistributedCache>();
var identifier = ctx.Principal.FindFirst(ObjectIdentifierType)?.Value;
var cca = ConfidentialClientApplicationBuilder.CreateWithApplicationOptions(new ConfidentialClientApplicationOptions()
{
ClientId = "ClientId",
RedirectUri = "RedirectUri",
ClientSecret = "ClientSecret"
})
.WithAuthority(ctx.Options.Authority)
.Build();
var tokenCache = new SessionTokenCache(identifier, distributedCache);
tokenCache.Initialize(cca.UserTokenCache);
var token = await cca.AcquireTokenByAuthorizationCode(scopes, ctx.TokenEndpointRequest.Code).ExecuteAsync();
ctx.HandleCodeRedemption(token.AccessToken, token.IdToken);
// get the claims
var claimService = serviceProvider.GetRequiredService<ClaimService>();
var response = await apiClient.GetUserAdditionalClaimsAsync(token.AccessToken);
// add the claims
};
Now when I try to use the ITokenAcquisition.GetAccessTokenForUserAsync() method instead of using the ConfidentialClientApplicationBuilder
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftWebApp(options =>
{
Configuration.Bind("AzureAD", options);
options.Events.OnAuthorizationCodeReceived = async ctx =>
{
var serviceProvider = services.BuildServiceProvider();
var tokenAcquisition = serviceProvider.GetRequiredService<ITokenAcquisition>();
var token = await tokenAcquisition.GetAccessTokenForUserAsync("scopes");
// get the claims
var claimService = serviceProvider.GetRequiredService<ClaimService>();
var response = await apiClient.GetUserAdditionalClaimsAsync(token);
// add the claims
};
})
.AddMicrosoftWebAppCallsWebApi(Configuration, new[] { "scopes" })
.AddDistributedTokenCaches();
Any help would be so much appreciated on how best to handle this.
Thanks!
I get the following error:
You use the client credentials flow when using ConfidentialClientApplicationBuilder. I don't know why you use the ITokenAcquisition.GetAccessTokenForUserAsync() to instead.
You can use the below code sample for client credential flow :
// Even if this is a console application here, a daemon application is a confidential client application
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithTenantId("{tenantID}")
.WithClientSecret(config.ClientSecret)
.Build();
// With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal or by PowerShell), and then granted by
// a tenant administrator
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
}
catch(MsalServiceException ex)
{
// Case when ex.Message contains:
// AADSTS70011 Invalid scope. The scope has to be of the form "https://resourceUrl/.default"
// Mitigation: change the scope to be as expected
}
In the authorization stage of my application I'm requesting access via:
var req = new Google.Apis.Auth.OAuth2.Requests.GoogleAuthorizationCodeRequestUrl(new Uri(string.Format(Settings.Google.OAuth.Url, "auth")));
req.ClientId = Settings.Google.OAuth.ClientId;
req.ResponseType = "code";
req.Scope = "https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtubepartner";
req.RedirectUri = string.Format(Settings.Integration.HandshakeUrl, "youtube");
req.AccessType = "offline"; // required to get refreshToken.
req.ApprovalPrompt = "force";
req.State = Application.Cryptography.Encrypt(Application.JSON.SerializeToString<State>(new State { UID = userId, PROFILEID = profileId, SUCCESS = request.SuccessUrl, FAIL = request.FailUrl }), Settings.Cryptography.SymetricKey);
// Return the url that the requesting application should redirect to in order to perform the authorization.
return req.Build().ToString();
This successfully gets me an access token and refresh token. Now I wanted to insert a new stream based on the information in the google api docs
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse { RefreshToken = refreshToken, AccessToken = accessToken };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "<id>",
ClientSecret = "<secret>",
}
}), string.Empty, token);
var service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials
});
var streamResource = new LiveStreamsResource(service);
var result = streamResource.Insert(new LiveStream
{
Snippet = new LiveStreamSnippet
{
Title = "Stream"
},
Cdn = new CdnSettings
{
Format = "1080p",
IngestionType = "rtmp"
}
}, "id, snippet, cdn, status");
var returnedStream = result.Execute();
When this runs Execute() gives the following exception:
Google.Apis.Requests.RequestError
Request is not authorized [403]
Errors [
Message[Request is not authorized] Location[ - ] Reason[insufficientLivePermissions] Domain[youtube.liveStream]
]
I can't figure out what I'm doing wrong in this process. Even the API explorer
Apparently, I was looking at this all wrong (and Google's API documentation should really describe this).
The response isn't that I don't have access to the proper scopes, it's that even though I was authorizing myself for the youtube scope, my youtube account did not have live streams enabled (separate option).
After enabling live streaming on my account this code worked properly.