We have a problem with the new authentication PicasaWeb
We are using this code in C # .NET 2012 ( Framework 4.5.1 )
const string ServiceAccountEmail = "xxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"C:\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
var serviceAccountCredentialInitializer =
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://picasaweb.google.com/data/"}
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
throw new InvalidOperationException("Access token request failed.");
var requestFactory = new GDataRequestFactory(null);
requestFactory.CustomHeaders.Add("Authorization: Bearer " + credential.Token.AccessToken);
requestFactory.CustomHeaders.Add("Gdata-version: 2");
PicasaService service = new PicasaService("api-project");
service.RequestFactory = requestFactory;
PhotoQuery query = new PhotoQuery(PicasaQuery.CreatePicasaUri(_IdUsuari, _albumid));
PicasaFeed feed = service.Query(query);
We have an error to retrieve the PicasaFeed :
Unhandled Exception: Google.GData.Client.GDataRequestException: Execution of aut hentication request returned unexpected result: 404
We've done every step of the link : Google.GData.Client.GDataRequestException - Authentication suddenly fails in old code
But it has not worked , is that we are using 4.5.1 and not 4.5 ?
We have done some testing generating the token from the page of Google : https://developers.google.com/oauthplayground
We selected Picasa Web API v2 with the scope: https://picasaweb.google.com/data/
This has generated a token. We have marked the "Auto -refresh the token before it expires" option as it expires in 3600 seconds .
The question is whether this token changes after 3600 seconds? .
With the token generated from this link we have replaced the previous code , where " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " is the token generated :
var requestFactory = new GDataRequestFactory(null);
requestFactory.CustomHeaders.Add("Authorization: Bearer " + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
requestFactory.CustomHeaders.Add("Gdata-version: 2");
PicasaService service = new PicasaService("api-project");
service.RequestFactory = requestFactory;
PhotoQuery query = new PhotoQuery(PicasaQuery.CreatePicasaUri(_IdUsuari, _albumid));
PicasaFeed feed = service.Query(query);
And with this token if it works.
Any thoughts that the first code generated by the token code is not working properly for generating tokens and readings to Picasa.
Does anyone have any solution?
Thank you very much
I only wanted to add, that I have the same problem since May, 25.
Since then the api worked correctly and afterwords I get the 404 (page not found error) too.
Maybe google has changed something.
Because my code look similar! and I don't see any error in your code.
Greetings
Mike
Related
Background
I have a back end application that has a Twitter app setup and I can query and pull user tweet/post data. This is great, however, right now on the front end I don't have full Twitter integration setup. What I mean by this is that on the front end the user can enter any Twitter username and I want to know for sure that the Twitter username entered actually belongs to the user. With a Twitter application key you can pull public Twitter data for any twitter account which works well for large scale data ingestion and in my case proof of concept kind of work. At the point I am now, I need to have the assumption enforced in the back end that the data being analyzed for a particular Twitter screen name is also owned by the user of the account on my web application.
The proposed Twitter Solution
Here is a bunch of reference documentation I have been trying to follow.
https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter
https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token
https://oauth.net/core/1.0/#anchor9
https://oauth.net/core/1.0/#auth_step1
I have been trying to follow this and I have had different permutations to the code posted below (one without the callback URL as parameters, one with etc.) but at this point, not very different. I have not had any success and it's been more than a couple of days, which is killing me.
The code
This is my attempt to follow the OAuth specification proposed above in the documentation. Note that this is ASP.NET Core 2.2 + code. Also, this is the code for just Step 1 in the Twitter guide for OAuth authentication and authorization.
public async Task<string> GetUserOAuthRequestToken()
{
int timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(timestamp.ToString()));
string consumerKey = twitterConfiguration.ConsumerKey;
string oAuthCallback = twitterConfiguration.OAuthCallback;
string requestString =
twitterConfiguration.EndpointUrl +
OAuthRequestTokenRoute;
string parameterString =
$"oauth_callback={WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}&" +
$"oauth_consumer_key={twitterConfiguration.ConsumerKey}&" +
$"oauth_nonce={nonce}&" +
$"oauth_signature_method=HMAC_SHA1&" +
$"oauth_timestamp={timestamp}" +
$"oauth_version=1.0";
string signatureBaseString =
"POST&" +
WebUtility.UrlEncode(requestString) +
"&" +
WebUtility.UrlEncode(parameterString);
string signingKey =
twitterConfiguration.ConsumerSecret +
"&" + twitterConfiguration.AccessTokenSecret;
byte[] signatureBaseStringBytes = Encoding.ASCII.GetBytes(signatureBaseString);
byte[] signingKeyBytes = Encoding.ASCII.GetBytes(signingKey);
HMACSHA1 hmacSha1 = new HMACSHA1(signingKeyBytes);
byte[] signature = hmacSha1.ComputeHash(signatureBaseStringBytes);
string authenticationHeaderValue =
$"oauth_nonce=\"{nonce}\", " +
$"oauth_callback=\"{WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}\", " +
$"oauth_signature_method=\"HMAC_SHA1\", " +
$"oauth_timestamp=\"{timestamp}\", " +
$"oauth_consumer_key=\"{twitterConfiguration.ConsumerKey}\", " +
$"oauth_signature=\"{Convert.ToBase64String(signature)}\", " +
$"oauth_version=\"1.0\"";
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(
baseUri: new Uri(twitterConfiguration.EndpointUrl),
relativeUri: OAuthRequestTokenRoute);
request.Content = new FormUrlEncodedContent(
new Dictionary<string, string>() {
{ "oauth_callback", twitterConfiguration.OAuthCallback }
});
request.Headers.Authorization = new AuthenticationHeaderValue("OAuth",
authenticationHeaderValue);
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode)
{
return await httpResponseMessage.Content.ReadAsStringAsync();
}
else
{
return null;
}
}
Notes
I have tried to remove the callback URL from the parameters as well and that didn't work. I have tried all sort of slightly different permutations (urlencoded my signature, added the callback URL in the query string, removed it etc), but I have lost track at this point the one's I have tried and haven't (encodings, quotes etc.).
Ignore the fact that I am not serializing the response into a model yet as the goal is to first hit a success status code!
I have an integration test setup for this method as well and I keep getting 400 Bad Request with no additional information (which makes sense), but is absolutely not helping with debugging.
[Fact]
public async Task TwitterHttpClientTests_GetOAuthRequestToken_GetsToken()
{
var result = await twitterHttpClient.GetUserOAuthRequestToken();
Assert.NotNull(result);
}
As an aside I had some other questions as well:
Is there a way to verify a user's Twitter account without going
through the OAuth flow? The reason I ask this is because getting
through OAuth flow is proving to be difficult
Is it safe to do the first step of the Twitter login workflow on the back end and return the response to the front end? The response
would carry a sensitive token and token secret. (If I were to answer
this myself I would say you have to do it this way otherwise you
would have to hard code app secrets into front end configuration
which is worse). I ask this because this has been on my conscious
since I have started this and I'm worried a bit.
Is there an OAuth helper library for C# ASP.NET Core that can make this easier?
I solved this by writing unit tests and working through the Twitter documentation on Creating A Signature. Since that example provides keys and results, it's possible to verify that your code is correct.
Since you asked about libraries - I wrote LINQ to Twitter with the hope of helping others like myself with this difficult task.
In addition to to signature, the page navigation can be challenging as your code works through the OAuth flow. Please review the Twitter documentation on Obtaining user access tokens to understand this better. I've also documented this in the LINQ to Twitter Wiki on Securing your Applications. Here's how this will work with LINQ to Twitter:
First, I have an OAuthController with a Begin action to redirect a user to for kicking off the authentication process:
public async Task<ActionResult> Begin()
{
//var auth = new MvcSignInAuthorizer
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore(HttpContext.Session)
{
ConsumerKey = configuration["Twitter:ConsumerKey"],
ConsumerSecret = configuration["Twitter:ConsumerSecret"]
}
};
string twitterCallbackUrl = Request.GetDisplayUrl().Replace("Begin", "Complete");
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
}
Notice that it's using an MvcSignInAuthorizer, passing in credentials via the CredentialStore property. If you were using your own raw code, you would be setting up the HTTP request with the Authorization header.
Next, notice that I'm modifying the current URL so that it will reference the same controller, but with the Complete endpoint. That is the oauth_callback that gets sent to Twitter authorization.
That process redirects the user to the Twitter web site, they authorize your app, and then it uses the oauth_callback to redirect the user back to your site. Here's how you handle that:
public async Task<ActionResult> Complete()
{
var auth = new MvcAuthorizer
{
CredentialStore = new SessionStateCredentialStore(HttpContext.Session)
};
await auth.CompleteAuthorizeAsync(new Uri(Request.GetDisplayUrl()));
// This is how you access credentials after authorization.
// The oauthToken and oauthTokenSecret do not expire.
// You can use the userID to associate the credentials with the user.
// You can save credentials any way you want - database,
// isolated storage, etc. - it's up to you.
// You can retrieve and load all 4 credentials on subsequent
// queries to avoid the need to re-authorize.
// When you've loaded all 4 credentials, LINQ to Twitter will let
// you make queries without re-authorizing.
//
//var credentials = auth.CredentialStore;
//string oauthToken = credentials.OAuthToken;
//string oauthTokenSecret = credentials.OAuthTokenSecret;
//string screenName = credentials.ScreenName;
//ulong userID = credentials.UserID;
//
return RedirectToAction("Index", "Home");
}
Again, you can see that I'm using MvcAuthorizer and completing the request. After completing the request, you'll be able to pull out the oauth_token and oauth_token_secret, as well as screen_name and user_id. You can save these artifacts and re-use them for all subsequent activity by this user, making their experience better because they don't have to log in every time you need to make a request.
On your question about verification, there is a Verify Credentials endpoint.
LINQ to Twitter has an ASP.NET Core Sample, API Samples with 100% API coverate, and full documentation on the Wiki if you want to learn more.
After hours and hours of going through the documentation I found the answer out. Turns out I missed some small details from the guides.
When making a request to oauth/request_token, when you sign the
request, you don't use the access token secret (for this specific request). Also, see the "Getting Signing Key" section of the signing a request guide and read the last few paragraphs. Therefore the signing key
does not have the access token secret
You must UrlEncode every single key and value. You must UrlEncode the authorization header as well.
I will post the updated code for you all here in case you need this in C#. Note that this code is not clean. You should separate OAuth functionality into some other class. This was my attempt to just get it to work.
public async Task<string> GetUserOAuthRequestToken()
{
int timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
string nonce = Convert.ToBase64String(Encoding.ASCII.GetBytes(timestamp.ToString()));
string consumerKey = twitterConfiguration.ConsumerKey;
string oAuthCallback = twitterConfiguration.OAuthCallback;
string requestString =
twitterConfiguration.EndpointUrl +
OAuthRequestTokenRoute;
string parameterString =
$"oauth_callback={WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}&" +
$"oauth_consumer_key={WebUtility.UrlEncode(twitterConfiguration.ConsumerKey)}&" +
$"oauth_nonce={WebUtility.UrlEncode(nonce)}&" +
$"oauth_signature_method={WebUtility.UrlEncode(OAuthSigningAlgorithm)}&" +
$"oauth_timestamp={WebUtility.UrlEncode(timestamp.ToString())}&" +
$"oauth_version={WebUtility.UrlEncode("1.0")}";
string signatureBaseString =
"POST&" +
WebUtility.UrlEncode(requestString) +
"&" +
WebUtility.UrlEncode(parameterString);
string signingKey =
WebUtility.UrlEncode(twitterConfiguration.ConsumerSecret) +
"&";
byte[] signatureBaseStringBytes = Encoding.ASCII.GetBytes(signatureBaseString);
byte[] signingKeyBytes = Encoding.ASCII.GetBytes(signingKey);
HMACSHA1 hmacSha1 = new HMACSHA1(signingKeyBytes);
byte[] signature = hmacSha1.ComputeHash(signatureBaseStringBytes);
string base64Signature = Convert.ToBase64String(signature);
string authenticationHeaderValue =
$"oauth_nonce=\"{WebUtility.UrlEncode(nonce)}\", " +
$"oauth_callback=\"{WebUtility.UrlEncode(twitterConfiguration.OAuthCallback)}\", " +
$"oauth_signature_method=\"{WebUtility.UrlEncode(OAuthSigningAlgorithm)}\", " +
$"oauth_timestamp=\"{WebUtility.UrlEncode(timestamp.ToString())}\", " +
$"oauth_consumer_key=\"{WebUtility.UrlEncode(twitterConfiguration.ConsumerKey)}\", " +
$"oauth_signature=\"{WebUtility.UrlEncode(base64Signature)}\", " +
$"oauth_version=\"{WebUtility.UrlEncode("1.0")}\"";
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(
baseUri: new Uri(twitterConfiguration.EndpointUrl),
relativeUri: OAuthRequestTokenRoute);
request.Headers.Authorization = new AuthenticationHeaderValue("OAuth",
authenticationHeaderValue);
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode)
{
string response = await httpResponseMessage.Content.ReadAsStringAsync();
return response;
}
else
{
return null;
}
}
I am wondering if in .NET, if it possible to send over the credentials of the identity running an application pool in IIS to an API that uses Basic Auth. I have successfully been able to retrieve the identity context from the application pool. However, in every example i see for using Basic Auth. They all seem to require to manually add the Authorization header to the request. This is a problem since i do not directly have access to the password of the windows identity thus i can't manually create the Basic Auth Token. I have been trying to use the .DefaultCredentials property but it fails to generate the Auth header thus the response fails with 401. If this isn't possible then i'll take a different approach but wanted to make sure before i do so. The full code sample is below...i have tried multiple ways but all end up with the same 401.
using (var impersonationContext = WindowsIdentity.Impersonate(IntPtr.Zero))
{
HttpWebRequest request1 = (HttpWebRequest)WebRequest.Create("url");
HttpClient request2 = new HttpClient();
WebClient request3 = new WebClient();
WebRequest request4 = WebRequest.Create("url");
try
{
// this code is now using the application pool indentity
try
{
//Method 1
//request1.UseDefaultCredentials = true;
//request1.PreAuthenticate = true;
//string encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(WindowsIdentity.GetCurrent().Name + ":" + "No password :("));
//request1.Headers.Add("Authorization", "Basic " + WindowsIdentity.GetCurrent().Token.ToString());
//HttpWebResponse response = (HttpWebResponse)request1.GetResponse();
//using (var reader = new StreamReader(response.GetResponseStream()))
//{
// JavaScriptSerializer js = new JavaScriptSerializer();
// var objText = reader.ReadToEnd();
// Debug.WriteLine(objText.ToString());
//}
////Method 2
//client.DefaultRequestHeaders.Accept.Clear();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", WindowsIdentity.GetCurrent().Token.ToString());
//HttpResponseMessage response2 = client.GetAsync("url").Result; //.Result forces sync instead of async.
//var result = response2.Content.ReadAsStringAsync().Result;
//Debug.WriteLine(result);
//Method 3
//client2.Credentials = CredentialCache.DefaultNetworkCredentials;
//var result2 = client2.DownloadString("url");
//Debug.WriteLine(result2);
//Method 4
//request4.Credentials = CredentialCache.DefaultCredentials;
//string result4;
//using (var sr = new StreamReader(request4.GetResponse().GetResponseStream()))
//{
// result4 = sr.ReadToEnd();
//}
//Debug.WriteLine(result4);
}
catch (Exception ex)
{
throw new Exception("API Call Failed: " + ex.ToString() + " for " + WindowsIdentity.GetCurrent().Name + " request: " + request4.Headers.ToString());
}
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
App Pool Identity and Basic Auth serves two different purpose and I suggest not to mix those. As you also mentioned that you don't know the password of app pool identity and it's self explanatory. App pool identity also allows the API's to access system resources for example, accessing a file share.
Whereas Basic Auth allows you to secure your API as a whole from being wide open and anyone accessing it. Except the ones who knows UserName:Password which needs to be passed with each HttpRequest (containing HttpHeader with UserName:Password in Base64).
Considering these facts, when API developer needs to share UserName and Password with all the parties, it's advisable not to share App Pool Identity credentials.
I have worked with both App Pool Identity and Basic Auth and I recommend to keep these separate.
im very new with Facebook apps and read several threads for creating them, but I have some problems with it.
First of all what I want to do: I want to create a web application that is able to post pictures, text and links on a facebook page that is managed by me.
I used the Facebook C# SDK: here!
What I have:
string facebookPageId = "<my page id>";
string app_id = "<my app id>";
string app_secret = "<my app secret>";
string scope = "publish_stream,manage_pages";
var fb = new FacebookClient();
dynamic res = fb.Get("oauth/access_token", new
{
client_id = app_id,
client_secret = app_secret,
grant_type = "client_credentials"
});
var access_token = res.access_token;
dynamic messagePost = new ExpandoObject();
messagePost.access_token = access_token;
messagePost.link = "http://www.test.at";
messagePost.name = "Testbot";
messagePost.caption = "{*actor*} " + "hello this is a test";
messagePost.description = "[SOME_DESCRIPTION]";
FacebookClient app = new FacebookClient(access_token);
app.AppId = app_id;
app.AppSecret = app_secret;
try
{
var result = app.Post("/hrechttest" + "/feed", messagePost);
}
catch (Exception e)
{
}
Well the code runs without any exceptions but in the output window I get the following:
Exception thrown: 'Facebook.FacebookOAuthException' in Facebook.dll
The next problem is:
As I understood it you must link your facebook app with your facebook page, but when I want to do that I cant select the page:
So what I did wrong or missed?
publish_stream is deprecated since many years, publish_pages is the correct permission to post to a Page (as Page).
API reference: https://developers.facebook.com/docs/graph-api/reference/page/feed#publish
Make sure you are using a Page Token, not a User Token:
https://developers.facebook.com/docs/facebook-login/access-tokens
http://www.devils-heaven.com/facebook-access-tokens/
How to create Page Apps is explained in the docs too: https://developers.facebook.com/docs/pages/tabs
I'm trying to find a single item fronm all items in the current context, but I seem to constantly get this error message:
The request failed. The remote server returned an error: (401) Unauthorized.
First, I set everything up to access the exchange service:
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationResult authenticationResult = null;
AuthenticationContext authenticationContext = new AuthenticationContext(
SettingsHelper.Authority, new model.ADALTokenCache(signInUserId));
authenticationResult = authenticationContext.AcquireToken(
SettingsHelper.ServerName,
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret));
ExchangeService exchange = new ExchangeService(ExchangeVersion.Exchange2013);
exchange.Url = new Uri(SettingsHelper.ServerName + "ews/exchange.asmx");
exchange.TraceEnabled = true;
exchange.TraceFlags = TraceFlags.All;
exchange.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
And then I define what Item I want to receive (by ID):
ItemView view = new ItemView(5);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly);
var tempId = id.Replace('-', '/').Replace('_', '+');
SearchFilter.IsEqualTo searchid = new SearchFilter.IsEqualTo(ItemSchema.Id, tempId);
And last but not least, I try to search for this item, within my items:
FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> results = exchange.FindItems(WellKnownFolderName.Inbox, searchid, view);
And this is where my error happens. I've tried various other ways of doing this, but no matter what I do, I get unauthorized.
Could someone maybe guide me in the correct way, in order to solve this issue?
EDIT
I do receive the access token from the:
authenticationResult = authenticationContext.AcquireToken(
SettingsHelper.ServerName,
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret));
as I can see by debugging the code.
No refresh token is present though, and I don't know if this has something to say?
EDIT
I just managed to debug all my way into the exchange.ResponseHeaders in where I saw this:
The access token is acquired using an authentication method that is
too weak to allow access for this application. Presented auth
strength was 1, required is 2
I decoded the JWT, as this is my result:
{
typ: "JWT",
alg: "RS256",
x5t: "MnC_VZcATfM5pOYiJHMba9goEKY",
kid: "MnC_VZcATfM5pOYiJHMba9goEKY"
}.
{
aud: "https://outlook.office365.com/",
iss: "https://sts.windows.net/d35f5b06-f051-458d-92cc-2b8096b4b78b/",
iat: 1445416753,
nbf: 1445416753,
exp: 1445420653,
ver: "1.0",
tid: "d35f5b06-f051-458d-92cc-2b8096b4b78b",
oid: "c5da9088-987d-463f-a730-2706f23f3cc6",
sub: "c5da9088-987d-463f-a730-2706f23f3cc6",
idp: "https://sts.windows.net/d35f5b06-f051-458d-92cc-2b8096b4b78b/",
appid: "70af108f-5c8c-4ee4-a40f-ab0b6f5922e0",
appidacr: "1"
}.
[signature]
Where to go from here?
I already got this error while using EWS in the past "The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2"
What you need to do is to enforce your authentication with a certificate.
AuthenticationContext authContext = new AuthenticationContext(authority);
exchangeService.Credentials = new OAuthCredentials(authContext.AcquireToken("https://outlook.office365.com", new ClientAssertionCertificate(ConfigurationManager.AppSettings["ida:ClientId"], certificate)).AccessToken);
The key part is to define a new ClientAssertionCertificate as you ClientAssertion.
You will also have to modify the manifest of your Azure Active Directory Application.
Looks at this reference (the part about "Configuring a X.509 public cert for your application") : https://msdn.microsoft.com/en-us/office/office365/howto/building-service-apps-in-office-365
I am trying to use EWS managed Api with Office 365 Api via Azure AD. I have done the following tasks so far.
I have the admin privilege in Azure AD.
I have successfully registered my application in Azure AD.
I got Client ID, App key and resource ID from Azure AD.
I have enabled "Have full access to user's mailbox. as suggested by Jason.
I have successfully created a MVC5 web application.
I have followed this blog post of Jeremy.
Here the link of the blog I have followed :
http://www.jeremythake.com/2014/08/using-the-exchange-online-ews-api-with-office-365-api-via-azure-ad/#comment-280653
Code in my controller:
var outlookClient = await AuthHelper.EnsureOutlookServicesClientCreatedAsync("Mail");
IPagedCollection<IMessage> messagesResults = await outlookClient.Me.Messages.ExecuteAsync();
string messageId = messagesResults.CurrentPage[0].Id;
string tokenx = AuthHelper.GetSessionToken();
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.HttpHeaders.Add("Authorization", "Bearer " + tokenx);
service.PreAuthenticate = true;
service.SendClientLatencies = true;
service.EnableScpLookup = false;
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ExFolder rootfolder = ExFolder.Bind(service, WellKnownFolderName.MsgFolderRoot);
Edited : I am getting accessToken Successfully and using it to make call against EWS managed Api, but it fails with 403:Forbidden exception. Your help will be highly appreciated.
best regards,
Jason Johnston helped me solve my problem.
The link:
Office 365 / EWS Authentication using OAuth: The audience claim value is invalid
I checked the EWS trace, I learned that EWS was complaining about invalid token and insufficient privileges. I re-registered my application to Azure AD and enabled full access to mailbox.
I commented this below code.
//var outlookClient = await AuthHelper.EnsureOutlookServicesClientCreatedAsync("Mail");
//try
//{
// IPagedCollection<IMessage> messagesResults = await outlookClient.Me.Messages.ExecuteAsync();
// string messageId = messagesResults.CurrentPage[0].Id;
//}
//catch
//{
// System.Diagnostics.Debug.WriteLine("Something bad happened. !!");
//}
I am getting access token from this below link sample.
https://github.com/OfficeDev/Office-365-APIs-Starter-Project-for-ASPNETMVC
Here is the complete code of controller which does the main task of authentication.
string resourceUri = "https://outlook.office365.com";
var signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Settings.Authority, new NaiveSessionCache(signInUserId));
string tokenx = await AuthHelper.AcquireTokenAsync(authContext, resourceUri, Settings.ClientId, new UserIdentifier(userObjectId,UserIdentifierType.UniqueId));
System.Diagnostics.Debug.WriteLine("Token:" + tokenx);
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.TraceListener = new EwsTrace();
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.HttpHeaders.Add("Authorization", "Bearer " + tokenx);
service.PreAuthenticate = true;
service.SendClientLatencies = true;
service.EnableScpLookup = false;
service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
ExFolder rootfolder = ExFolder.Bind(service, WellKnownFolderName.MsgFolderRoot);
Console.WriteLine("The " + rootfolder.DisplayName + " has " + rootfolder.ChildFolderCount + " child folders.");
The important thing I noticed is I can't use the same token to access office365 api and EWS managed Api as EWS works with full mailbox access while office365 doesn't. I request the developer to confirm this,maybe I am doing something wrong, however my problem is solved for now.
Yep, that's right. The scope required for EWS isn't compatible with the Office 365 APIs, and vice versa.