Consume Office 365 REST API Without UI - c#

I need to push calendar entries in to a client's Outlook account. This is fairly straight forward with Exchange. You just authenticate with a user that has access, and then you can push entries in to other user's accounts. It seems to be completely different in Office 365.
I tried to follow the instructions here:
https://dev.outlook.com/restapi/getstarted
I created the app and got the app's client ID. But, all of the documentation is around oAuth. Generally speaking, oAuth is designed for scenarios when a user needs to enter their credentials in through a browser window that will then confirm with the user which credentials they are willing to allow the app to have.
This does not match my scenario. I need to be able to push the calendar entries in to the account without any UI. This is back end integration. It just needs to do its job silently.
I looked at this sample app:
https://github.com/OfficeDev/O365-Win-Snippets
But, this is a front end app. When it needs to authenticate, it pops up a window to force the user to enter their credentials.
When I try to call the REST API that is mentioned in the getting started page, it returns HTML. This is the Url it mentions:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&response_type=code&scope=https%3A%2F%2Foutlook.office.com%2Fmail.read
I've tried a few permutations of this Url with my client ID. I've tried passing in my Office 365 credentials through basic http authentication.
I'm stuck.

The answer is simple. Use the Exchange API - not Office 365 API.
I was confused because I assumed that Office 365 was a different entity to Exchange, but the Office 365 email server just is one giant Exchange server. Here's some sample code for good measure. This is an example of logging in to Office 365's Exchange server and sending off a calendar entry to an email address. Simple.
I made a wild guess about the exchange Url and it was correct:
https://outlook.office365.com/ews/exchange.asmx
//Connect to exchange
var ewsProxy = new ExchangeService(ExchangeVersion.Exchange2013);
ewsProxy.Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
//Create the meeting
var meeting = new Appointment(ewsProxy);
ewsProxy.Credentials = new NetworkCredential(_Username, _Password);
meeting.RequiredAttendees.Add(_Recipient);
// Set the properties on the meeting object to create the meeting.
meeting.Subject = "Meeting";
meeting.Body = "Please go to the meeting.";
meeting.Start = DateTime.Now.AddHours(1);
meeting.End = DateTime.Now.AddHours(2);
meeting.Location = "Location";
meeting.ReminderMinutesBeforeStart = 60;
// Save the meeting to the Calendar folder and send the meeting request.
meeting.Save(SendInvitationsMode.SendToAllAndSaveCopy);

My understanding is that this is possible, but the authentication looks quite complicated. For starters, any application that requires Office 365 integration must also integrate with the associated Azure AD. You can register your application for specific users so that it has the permissions required for whatever operations you need to perform. See here for a good summary of this component: https://msdn.microsoft.com/en-us/office/office365/howto/connect-your-app-to-o365-app-launcher?f=255&MSPPError=-2147217396#section_2
For authentication, you require a daemon/server application model. I've not attempted this yet, but it's documented here and looks like it should meet your needs (see the Daemon or Server Application to Web API section): https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/#daemon-or-server-application-to-web-api

In order to call the Office 365 REST API, the app requires an access token from Azure Active Directory, that's why you need (mandatory) to register app in Microsoft Azure Active Directory (Azure AD). Your Office 365 account in turn needs to be associated with Azure AD. This answer summarizes on how to register app in Azure AD in order to consume Office 365 API.
Basic authentication scheme
Regrading Basic authentication, currently it is enabled for API version 1.0, the following example demonstrates how to consume Outlook Calendar REST API in .NET application.
Prerequisites:
domain: https://outlook.office365.com/
API version: v1.0
Here is an example that gets my calendars and prints its names
private static async Task ReadCalendars()
{
var handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential()
{
UserName = ConfigurationManager.AppSettings["UserName"],
Password = ConfigurationManager.AppSettings["Password"]
};
using (var client = new HttpClient(handler))
{
var url = "https://outlook.office365.com/api/v1.0/me/calendars";
var result = await client.GetStringAsync(url);
var data = JObject.Parse(result);
foreach (var item in data["value"])
{
Console.WriteLine(item["Name"]);
}
}
}

Related

Authenticating EWS using Oauth with full_access_as_user

I'm trying to get an application using EWS to work against O365 with OAuth. I've managed to get it working in the case where I register the app in AAD, and grant it full_access_as_app (Use Exchange Web Services with full access to all mailboxes) in the portal. The code looks like this:
string tokenUri = "https://login.microsoftonline.com/TENANTID/oauth2/v2.0/token";
string resource = "https://outlook.office365.com";
string appId = "APPID_FROM_AAD";
var context = new AuthenticationContext(tokenUri);
X509Certificate2 cert = GetLocalCertificate(THUMBPRINT);
ClientAssertionCertificate c = new ClientAssertionCertificate(appId, cert);
AuthenticationResult authenticationResult = context.AcquireTokenAsync(resource, c).ConfigureAwait(false).GetAwaiter().GetResult();
m_exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);
//all the autodiscover URIs for O365 are the same
m_exchangeService.Url = new Uri(#"https://outlook.office365.com/EWS/Exchange.asmx");
m_exchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, m_emailAddress);
This seemed a little extreme though. The application is a back end service, and only needs access to one mailbox (the impersonated user id), so I wanted to change it over to full_access_as_user permissions (Access mailboxes as the signed-in user via Exchange Web Services). I added the permission to the app, and then added the application to the user, but I got a 401 anytime I tried to operate on the mailbox after impersonation. I checked the JWT, and while with the original permission I had "full_access_as_app" in the roles, I didn't have any roles assigned this time.
Is there a way I can modify this so that I can have an admin add EWS access to one or more mailboxes in a tenant, or is the only way to get this to work to give an app access to every mailbox on the service?
Yes, OAuth authentication for EWS is only available in Exchange as part of Office 365. EWS applications require the "Full access to user's mailbox" permission.
Reference - Authenticate an EWS application by using OAuth

Access sharepoint search rest api

I have a problem. I use Azure AD to authenticate my asp.net app. Authentication works fine. Then I from this app trying to access OneDrive for Business using sharepoint search rest api. But the server always receives a response with a 401 error. I understand that the problem is in the access token which I use (Now I use the token received from Azure AD). But I never found the normal description of how to obtain an access token for the sharepoint search rest api.
Thanks in advance
Answer
You need to give your ASP.NET Application permission to use your OneDrive for Business application.
Here is an overview of how to do this using the Azure Management Portal. (Note that your OneDrive for Business account is a type of Office 365 SharePoint Online account.)
Go to manage.windowsazure.com > Active Directory > Your Tenant. If your tenant has an associated OneDrive for Business account, then its list of applications will include Office 365 SharePoint Online.
If your tenant's list of application does include Office 365 SharePoint Online, then your next step is to give your ASP.NET Web Application permission to access it.
Open up your Web Application's page in the Azure Active Directory area. Then choose CONFIGURE > Add Application. Add the Office 365 SharePoint Online application. Give it all necessary permissions and save.
The following screenshot is for a Native Client Application, because that is what my demo code is using. You can do a similar thing for a Web Application, though you will need to use an X509 Certificate for authentication instead of a username/password.
Your access token will now work with your Office 365 for Business account. Hooray!
Demo
Here is some sample code that works on my machine with a Native Client App. You can do the same thing with a Web Application, though you will need to use an X509 Certificate instead of a username/password.
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net;
namespace AAD_SharePointOnlineApp
{
class Program
{
static void Main(string[] args)
{
var authContext =
new AuthenticationContext(Constants.AUTHORITY);
var userCredential =
new UserCredential(Constants.USER_NAME, Constants.USER_PASSWORD);
var result = authContext
.AcquireTokenAsync(Constants.RESOURCE, Constants.CLIENT_ID_NATIVE, userCredential)
.Result;
var token = result.AccessToken;
var url = "https://mvp0.sharepoint.com/_api/search/query?querytext=%27timesheets%27";
var request = WebRequest.Create(url);
request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
var response = request.GetResponse() as HttpWebResponse;
}
}
class Constants
{
public const string AUTHORITY =
"https://login.microsoftonline.com/mvp0.onmicrosoft.com/";
public const string RESOURCE =
"https://mvp0.sharepoint.com";
public const string CLIENT_ID_NATIVE =
"xxxxx-xxxx-xxxxx-xxxx-xxxxx-xxxx";
public const string USER_NAME =
"MY_USER#mvp0.onmicrosoft.com";
public const string USER_PASSWORD =
"MY_PASSWORD";
}
}
Comments
If you are trying to do the above with a Web Application instead of a Native Client Application, then you will need to use an X509 Certificate, otherwise you will receive the following error.
Unsupported app only token.
See also: http://blogs.msdn.com/b/richard_dizeregas_blog/archive/2015/05/03/performing-app-only-operations-on-sharepoint-online-through-azure-ad.aspx

Youtube API V3 authentication. Uploading to shared account

I'm trying to update an existing Asp.Net application using C# to avoid the Youtube V2 API deprecation. The 1st challenge is figuring out how to authenticate using the new API.
This is how the application currently authenticates:
string developerKey = (My Developer Key here);
string username = (My username here);
string password = (My password here);
ytService = new YouTubeService("SampleApplication", developerKey);
ytService.setUserCredentials(username, password);
I've researched, and reviewed the V3 API examples, and they all use OAuth2 to Authenticate like this:
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
However, that prompts the user to login to their own Google account, and for this application we want all uploaded videos to go into our account. Therefore I would like to continue using the Public API access if possible, and I can't find any examples of this for the V3 APIs.
I've also investigated OAuth2 Service Account, but for the life of me can not figure out how to authenticate using one of those.
Can you help point me in the right direction?
I faced a similar issue recently, except I was working with a shared Google Calendar instead of a Youtube account. What I ended up doing was adding an authorize button that only I could see, which was hooked up to the initial authorize request when clicked. It opened the popup from Google, I gave the permission from the shared account, and I stored the returned access token, refresh token, and expiration date in the database.
Then, whenever users need to view the shared calendar, I just have logic that says if the expiration date has passed, use the stored refresh token to request a new access token and reset the expiration date, then I use the access token to make my Google Calendar API call (getting the events list, in this case).
Unfortunately, V3 of the YouTube API does not support service accounts, and oAuth2 is the only authentication method.
One of the reasons behind this is to do away with having multiple individuals upload into a single account; the legal liability (for both YouTube and app developers) is too great. The only current alternative is the YouTube direct lite program (which has multiple clients ... see YouTube's Github page to find them), which gives you a simple management/admin pane for autogenerating a playlist based on users uploading videos into their own accounts (in other words, they upload the video through your app, to their account, but your account gets a record of it and manages what can/can't appear on the list).

Sign in to multiple Microsoft account in Windows Store app

Is it possible to:
sign-in to multiple Microsoft accounts in a Windows Store app? Mail app does it*
sign-in to Microsoft account that is not the one used for logging in to Windows? Store app does it*
I tried something like the following (taken from Live SDK sample code) but it only sign me in with the account I used to log in to Windows
var client = new LiveAuthClient();
var result = await client.LoginAsync(new[] { "wl.basic" });
if (result.Status == LiveConnectSessionStatus.Connected)
{
this.AddAccountButton.Content = "connected";
}
Appreciate any help.
* I'm not sure if the built-in apps use different API's to do this
I had a very similar issue, I've been told that's not possible, here: link
I found out, that if the user use the Windows with a Microsoft account, there is no possibilities for multiple accounts. But in case of a local account, you can implement a signing off logic to your app, and it won't log you in automatically.

Google Calendar V3 2 Legged authentication fails

I'm trying to create web page that access the (business) private calendar of the company and insert events if the time slot is available. Still I'm facing an authentication problem.
The API manual states that I should use an API key and Oauth2LeggedAuthenticator, so I did all this and the request that is fired is quite okey (it has a oauth token and such) But still the response is an exception with Invalid Credentials; Easy to say is that my credentials are wrong, still clientID, clientSecret and API Key are valid; I doubt the 2 last params of the 2legged authenticater, is this correct?
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = ClientCredentials.ClientID;
provider.ClientSecret = ClientCredentials.ClientSecret;
var authenticator =
new OAuth2LeggedAuthenticator(ClientCredentials.ClientID, ClientCredentials.ClientSecret, "myworkusername", "workdomain.com");
Google.Apis.Calendar.v3.CalendarService service = new Google.Apis.Calendar.v3.CalendarService(authenticator);
service.Key = ClientCredentials.ApiKey;
var result = service.CalendarList.List().Fetch();
Assert.IsTrue(result.Items.Count > 0);
NB: At the time of writing you can only used 2-legged authentication with Google Apps for Business/Eduction, this won't work on personal accounts as there's no way to get an OAuth 1.0 key/secret pair, you will have to use online authentication at least once (but you can use the out-of-browser option so you don't have to create a dedicated page).
Your code is correct apart from you don't need the first three lines relating to the NativeApplicationClient. This is most likely failing because you haven't properly set the OAuth keys, this causes 401s.
The other thing that causes 401s is using "matt#example.com" instead of "matt" as the username, the username is without including your domain.
To setup OAuth follow the instructions in this article from Google.
The most important parts to note are "Allow access to all APIs" must be unchecked and you have to individually grant access to all the APIs. If this hasn't been done you will get a 401 Invalid Credentials error. You then also need to turn those services on in the api console. If the api console step hasn't been done you will get a different error of 403 Daily Limit Exceeded.
This will cause you problems if you were previously relying on the "Allow access to all APIs" to use various services, you will have to grant them all individually as far as I understand it to use the v3 APIs. This seems to have been confirmed by google (4th reply by Nicolas Garnier) and is supposedly a bug, but that is an old post so it looks as if it's here to stay.
For reference once this has been done, this code will work, which in essence is the same as yours:
var auth = new OAuth2LeggedAuthenticator(domainName, consumerSecret, usernameWithoutDomain, domainName); //domainName is presently used as the OAuth ConsumerKey for Google's 2legged OAuth
var service = new CalendarService(auth);
service.Key = serviceKey;
var results = service.CalendarList.List().Fetch();
Console.WriteLine(results.Items.Count);
So in summary:
In Google Apps "Manage this Domain" > "Advanced Tools"
Using "Manage OAuth domain key" enable key, generate secret, uncheck "Allow access to all APIs".
Using "Manage third party OAuth Client access" enable the APIs you want access to using your domain as "Client Name" and the APIs you want to access e.g. "http://www.google.com/calendar/feeds/" for the calendar.
Then finally create a project in the API console, use the APIKey as the serviceKey in the above example and turn on the APIs you need to access.
I am answering this as I kept hitting this question when I was trying to find out why my code was constantly returning 401s. Hope this helps someone as the Google instructions are awful and scattered all over the place at the moment.

Categories