6

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.

enter image description here

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?

Detilium
  • 2,868
  • 9
  • 30
  • 65
  • I would suggest hard set "exchange.Credentials" until you have worked out the authentication.... as its seems like your problem is there.. – Seabizkit Oct 21 '15 at 09:01
  • exchange.Credentials = new WebCredentials(authEmailAddress, authEmailPassword, "DOMAINIFYOUHAVEONE"); – Seabizkit Oct 21 '15 at 09:03
  • @Seabizkit That works. That's what I did to start with, which works just fine, but I want to use OAuth – Detilium Oct 21 '15 at 09:06
  • @Detilium refer this [link](https://github.com/jasonjoh/office365-azure-guides/blob/master/ValidatingYourToken.md) Np with the `aud` check for remaining items – Krsna Kishore Oct 21 '15 at 09:08
  • @Webruster Im not sure what you mean? The `aud`is `https://outlook.office365.com/` which is correct? – Detilium Oct 21 '15 at 09:12
  • Yes its correct !! and i'm asking you to verify for the other params mentioned over there and from your error ,you're trying to do the client credential auth flow, which requires the auth strength of 2. That requires that you authenticate with a certificate rather than a client secret. Client credential auth flow is more for service apps that require access to all mailboxes in the organization. If you're just trying to access the logged-in user's mailbox, you should use the code grant flow. – Krsna Kishore Oct 21 '15 at 09:16
  • please check [this](http://blogs.msdn.com/b/exchangedev/archive/2015/01/22/building-demon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow.aspx) and [this](https://github.com/mattleib/o365api-as-apponly-webapp) links . Are you accessing Mail, Calendar, Contacts APIs or Files API – Krsna Kishore Oct 21 '15 at 09:16
  • @Detilium Instead of `ClientSecret` you need a `client_assertion` for support of my statement here is the [link](http://stackoverflow.com/questions/28351558/office-365-rest-api-daemon-week-authentication) – Krsna Kishore Oct 21 '15 at 09:26
  • Personally I don't think you will get it to work that way(please prove me wrong, in a good way!). I think you will need to pass the e-mail of the logged in user to the exchange.AutodiscoverUrl(autoDiscoverEmailAddress); – Seabizkit Oct 21 '15 at 09:31
  • @Seabizkit Would you please explain more? :) – Detilium Oct 21 '15 at 09:32
  • If i understand correctly; you have an App(website?), and you basically want the logged in user to be able to access their own mailbox through EWS or something like that? – Seabizkit Oct 21 '15 at 09:36
  • @Seabizkit Im creating an add-in for office365, and I'm currently just testing the server-side of things so basically... Yes – Detilium Oct 21 '15 at 09:40
  • @Seabizkit Allow me to explain. I want to find a specific email based on the ItemId, then convert this email to a `.msg`, and then bla bla bla. but whenever I try to retreive this email, the error happens. I'm not using any frontend at the moment. This is just a simple MVC rest service, whereas I'm testing the conversion anhd retreival of the mail – Detilium Oct 21 '15 at 10:01
  • @Webruster I was hoping you could explain more to me about how to create this certificate? The link you sent wasn't really that helpful (I can't seem to understand exactly how he does this) – Detilium Oct 21 '15 at 10:36
  • @Detilium, seems like it may be possible have you read. https://msdn.microsoft.com/en-us/library/office/dn903761(v=exchg.150).aspx – Seabizkit Oct 21 '15 at 10:43
  • @Seabizkit Yes i have. And what they do there, is exactly what I do. The only difference is that mine doesn't work :) – Detilium Oct 21 '15 at 10:45
  • @Detilium Just want to make sure that at the time of creating the MVC project what is the Authentication you provided for the MVC ? – Krsna Kishore Oct 21 '15 at 10:55
  • @Webruster I downloaded the MVC API project that the Microsoft Development team put on [GitHub](https://github.com/OfficeDev/O365-ASPNETMVC-Start) – Detilium Oct 21 '15 at 11:44

1 Answers1

6

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

Trysior
  • 173
  • 8
  • Could you edit your answer and explain more about why this certificate is needed? Is it because the App needs to trust the PC running the service since the access is so broad for the service? – Detilium Oct 22 '15 at 08:50
  • I'm not sûre of the reason other than establishing an even more secure trust than the one with the app secret only. I think that "stealing/knowing" à secret (string) is easier than à certificate. That's they only way that EWS will let you make calls. – Trysior Oct 22 '15 at 17:33
  • You get my +50 rep, as this is the only answer, and somewhat the correct answer though I wish you could tell me more about the certificate. I managed to change the error from `401: Unauthorized`, to `403: Forbidden` and I beleive this has something to do with the permission from AAD. Thanks anyway for the answer, and enjoy your 50 rep :) – Detilium Oct 23 '15 at 12:35
  • Hi @Trysior / Detilium I am trying to do the same certificate based validation of the client but getting "Invalid provider type specified" while calling await authenticationContext.AcquireTokenAsync("https://outlook.office365.com", MyClientAssertionCertificate);. Any idea how to get over it? – tavier Aug 14 '17 at 06:39