Exchange Web Services Managed API: Accessing other users items - c#

Is it possibly to access the folders and items of other Exchange accounts other than the one of the logged in user?
Can I do this via Exchange Web Services Managed API?

Yes it is possible, but you should know the password of the other user or grab in some ways this credentials (NetworkCredential object). The typical first lines of you code could be
ExchangeService myService = new ExchangeService (ExchangeVersion.Exchange2007_SP1);
myService.Credentials = new NetworkCredential ("user#mycorp.local", "P#ssword00");
so you can access Exchange Server Web Services with the account which is other as the current user. See ExchangeService object description for more information.
If you are an admin you can make user impersonation by SMTP address.

Here's how you do it without impersonation or knowing credentials.
ExchangeService _service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
//CREDENTIALS OF AN ACCOUNT WHICH HAS READ ACCESS TO THE CALENDAR YOU NEED
_service.Credentials = new WebCredentials(username, password);
_service.Url = new Uri(serviceURL);
SearchFilter.SearchFilterCollection searchFilter = new SearchFilter.SearchFilterCollection();
searchFilter.Add(new SearchFilter.IsGreaterThanOrEqualTo(AppointmentSchema.Start, DateTime.Now.AddDays(-1)));
searchFilter.Add(new SearchFilter.IsLessThanOrEqualTo(AppointmentSchema.Start, DateTime.Now.AddDays(2)));
ItemView view = new ItemView(50);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.AppointmentType, AppointmentSchema.End);
//THIS NEXT LINE!!!
var calendarSearch = new FolderId(WellKnownFolderName.Calendar, new Mailbox("email#ofsomemailbox.com"));
var appointments = _service.FindItems(calendarSearch, searchFilter, view);

I suggest to use impersonation instead of login for each user.
Via impersonation you can impersonate users. Its not the same like full access. Full access is on behave of, impersonation is act as.
A pre of impersonation is you have one username and password instead of having x usernames and passwords.
You can use impersonation like this way:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
service.Credentials = new NetworkCredential(appName, appPassword, emailDomain);
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, userToImpersonate);
when a user has delegate access to someone else, you can access the folder of the other user. For example: Person A will be impersonated and is able to access Person B

Related

Exception when use EWS for create a appointment

I am get a exception when use my service EWS, this is my code
public class ExchangeHelper
{
ExchangeService exchangeService;
public ExchangeHelper()
{
//Instantiate a new ExchangeService object
exchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
//Set the exchange WebService URL
exchangeService.Url = new Uri("https://hostname/EWS/Exchange.asmx");
//exchangeService.Url = new Uri("https://hostname/EWS/Exchange.asmx");
//Set the credentials of the service to the credentials
//that are associated with the impersonating account.
exchangeService.Credentials = new NetworkCredential(
"user",
"pass",
"Domain.com"
);
}
public void CreateAppointment()
{
var emailAddress = "user#doamin.com";
//Set the ImpersonatedUserId property of the ExchangeService object to identify the impersonated user (target account).
//This example uses the user's SMTP email address.
exchangeService.AutodiscoverUrl(emailAddress);
exchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, emailAddress);
//create a new appointment object
Appointment appointment = new Appointment(exchangeService);
//set appointment properties
appointment.Subject = "test";
appointment.Body = "testBody";
//In MSDN it says that if you dont specify the timezone, it will use the UTC timezone
//but in reality it is not working that way.
//so explicity setting the EST timezone
appointment.StartTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
appointment.Start = DateTime.Now;
appointment.EndTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
appointment.End = DateTime.Now.AddHours(1);
//add required participants
appointment.RequiredAttendees.Add(emailAddress);
newFolder.Save(WellKnownFolderName.Inbox);
appointment.Save(new FolderId(WellKnownFolderName.Drafts, "emailAddress"));
//Set it back to null so that any actions that will be taken using the exchange service
//applies to impersonating account (i.e.account used in network credentials)
exchangeService.ImpersonatedUserId = null;
return the unique identifier that is created
return appointment.Id.UniqueId;
}
The exception is "The account does not have permission to impersonate the requested user"
It's not 100% clear from your post whether the account you use for the credentials on the ExchangeService and for the ImpersonationUserId are the same. If they are, then you would not need to use impersonation and should not set that property. It seems like one should be able to impersonate oneself, but there's no need to do so, hence EWS may throw you back that exception.
If they are different, then it's pretty much as the message says: the account you use for the credentials must have the right to impersonate the user you set. This typically involves some PowerShell magic by the Exchange admin to set this up.

Read Mails from Outlook Web App using .net

I have code that works for office 365 Exchange web service https://outlook.office365.com/EWS/Exchange.asmx. However I am now trying to read mail from outlook web app say https://mail.company.com/owa
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Credentials = new NetworkCredential(i_EmailID, i_Password);
exchangeService.Url = new Uri(exchangeURL);
exchangeService.KeepAlive = true;
Mailbox mailbox = new Mailbox(email);
FolderId folder = new FolderId(WellKnownFolderName.Inbox, mailbox);
ItemView view = new ItemView(1);
FindItemsResults<Item> items = exchangeService.FindItems(folder, view);
If this code works only for Exchange Web Service then what method should I be using to access Outlook Web Access?
Please share me any article or code sample that can help me understand in getting this implemented.
UPDATE: Exchange Online deprecating Basic Authentication (Basic Auth)
Basic Authentication is no longer supported by Exchange Online. Use the
OAuth 2.0 authorization code flow
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
var confidentialClientApplicationBuilder = ConfidentialClientApplicationBuilder.Create(appId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.Build();
string[] ewsScopes = new string[] { "https://outlook.office365.com/.default" };
var authResult = await confidentialClientApplicationBuilder.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
return authResult.AccessToken;
and then use the token to pass to OAuthCredentials
exchangeService.Credentials = new OAuthCredentials(accessToken);

EWS Oauth Exception: The request failed. The remote server returned an error: (401) Unauthorized

I'm trying to create a bot that can book meetings. In order to do that i need to access the calendar of an employee to get FreeBusy info to ultimately book a meeting. I'm trying to avoid hardcoding the email and password and for that I want to use an access token from Azure AD to call EWS.
I set the properties for
public static ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
using this method:
public static async System.Threading.Tasks.Task UseExchangeService(IDialogContext context, string userEmailAddress, SecureString userPassword)
{
string authority = ConfigurationManager.AppSettings["authority"];
string clientID = ConfigurationManager.AppSettings["clientID"];
string resource = ConfigurationManager.AppSettings["resource"];
string appKey = ConfigurationManager.AppSettings["appkey"];
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
ClientCredential clientCred = new ClientCredential(clientID, appKey);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientCred);
service.Url = new Uri(ConfigurationManager.AppSettings["serverName"] + "/ews/exchange.asmx");
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
// USING THIS LINE IT WORKS FINE!
// service.Credentials = new NetworkCredential(userEmailAddress, userPassword); // VIRKER
}
I do get the access token from Azure AD and I have granted the permission for the application in Azure AD.
I use this method to extract the freebusytimes, it contains other more code to display the times as buttons on a herocard, but this is the call to EWS:
List<AttendeeInfo> attendees = new List<AttendeeInfo>();
attendees.Add(new AttendeeInfo()
{
SmtpAddress = "MyEMAIL",
AttendeeType = MeetingAttendeeType.Organizer
});
attendees.Add(new AttendeeInfo()
{
SmtpAddress = BookersEmail,
AttendeeType = MeetingAttendeeType.Required
});
//DateTime date1 = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day.Ad, 7, 0, 0)
// Specify options to request free/busy information and suggested meeting times.
AvailabilityOptions availabilityOptions = new AvailabilityOptions();
availabilityOptions.GoodSuggestionThreshold = 49;
availabilityOptions.MaximumNonWorkHoursSuggestionsPerDay = 0;
availabilityOptions.MaximumSuggestionsPerDay = 20;
// Note that 60 minutes is the default value for MeetingDuration, but setting it explicitly for demonstration purposes.
availabilityOptions.MeetingDuration = 60;
availabilityOptions.MinimumSuggestionQuality = SuggestionQuality.Excellent;
//TimeWindow hej = new TimeWindow();
DateTime StartDay = DateTime.Now.AddDays(1);
TimeSpan ts = new TimeSpan(9, 0, 0);
DateTime StartTime = StartDay.Date + ts;
availabilityOptions.DetailedSuggestionsWindow = new TimeWindow(StartTime, DateTime.Now.AddDays(4));
availabilityOptions.RequestedFreeBusyView = FreeBusyViewType.FreeBusy;
// Return free/busy information and a set of suggested meeting times.
// This method results in a GetUserAvailabilityRequest call to EWS.
GetUserAvailabilityResults results = service.GetUserAvailability(attendees,
availabilityOptions.DetailedSuggestionsWindow,
AvailabilityData.FreeBusyAndSuggestions,
availabilityOptions);
I have created the application in Azure AD and I have granted the following permissions:
Office 365 Exchange Online:
Use Exchange Web Services with full access to all mailboxes
Read and write calendars in all mailboxes
Read and write user and shared calendars
Access mailboxes as the signed-in user via Exchange Web Services
I've tried other answers i found on stackoverflow, however they do not do the trick for me.
Hope You can help
I am not familiar with EWS, however as far as I know that the Microsoft Graph also provide the similar feature for find the available meeting time using the rest below( refer here):
POST /me/findMeetingTimes
And if you want to using this REST for the web application so that your web app can delegate the sign-in user to perform the operation for Exchange, we can use the OAuth 2.0 code grant flow. And for how to use this flow to integrate with Microsoft Graph, you can refer the links below:
Get started with Microsoft Graph in an ASP.NET 4.6 MVC app
And here is the detail for this flow:
Authorize access to web applications using OAuth 2.0 and Azure Active Directory
Hope it is helpful.

How to find the SharePoint Path from Office 365 using OAuth2 Client Credential Flow

Problem
I have been trying to figure out how to find a SharePoint path for a user when using OAuth2 Client Credential Flow (where an application has permission to read all users' SharePoint files using an Office 365 administrator's one-time acceptance)
I have my client application setup in Azure and am able to read files if I hard-code the SharePoint URL - so I know it is setup correctly.
But I need to "discover" the SharePoint URL so it will be change-tolerant and reusable across customers.
Related Articles:
Different OAuth2 Flows
Using OAuth2 Flow for Exchange
Code
var azureAdAuthority = "https://login.windows.net/{tenant-id}/oauth2/authorize".Replace("{tenant-id}", tenantId);
var discoveryUri = "https://api.office.com/discovery/v1.0/me/";
var discoveryResourceUri = "https://api.office.com/discovery/";
// discover contact endpoint
var cert = new X509Certificate2(certFilePath, certFilePassword, X509KeyStorageFlags.MachineKeySet);
var clientAssertion = new ClientAssertionCertificate(clientId, cert);
var userIdentifier = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
var userAssertion = new UserAssertion(userObjectId);
// create auth context
var authContext = new AuthenticationContext(azureAdAuthority, false);
// create O365 discovery client
var discovery = new DiscoveryClient(new Uri(discoveryUri),
() => authContext.AcquireTokenSilent(discoveryResourceUri, clientAssertion, userIdentifier).AccessToken);
// query discovery service for endpoint for 'calendar' endpoint
var dcr = await discovery.DiscoverCapabilityAsync("MyFiles");
This and many other variations throw exceptions from the AcquireTokenSilent function.
If I don't use a "userIdentifier" and call the AcquireToken function it succeeds, but the DiscoverCapabilityAsync function fails.

The account does not have permission to impersonate the requested user

I am getting this error while try to accessin the resource mailbox. pls any one help me on this . I am new to EWS.
I am able to access the resource mailbox through OWA(Outlook web app). But i am not owner of this mailbox as it is shared mailbox.
my code:
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
ExchangeService newExchangeService = new ExchangeService (ExchangeVersion.Exchange2007_SP1);
newExchangeService.Credentials = new NetworkCredential(username, password, domain);
newExchangeService.AutodiscoverUrl(email-id, RedirectionUrlValidationCallback);
newExchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, email_which_i_want_to_access);
Folder visitorsFolder = Folder.Bind(newExchangeService, WellKnownFolderName.Inbox);
foreach (Folder childfolder in visitorsFolder.FindFolders(new FolderView(10)))
{
Console.WriteLine(childfolder.DisplayName);
}
The problem could be that you do not have permissions to impersonate the mailbox but you may have delegate access. Please see my answer to this similar question on how to access a mailbox when you have delegate access:
https://stackoverflow.com/a/9242792/64161

Categories

Resources