Office 365 API MVC Authentification - c#

I am using the MVC Office 365 API libraries and I would like to archieve the following thing: Logging into User-Accounts where I know the username / password and then get there calendar entries.
What I have so far is code that makes this redirect and ask the user to enter credentials. But how can I log in for them wihtout asking? The idea is to get the calendar entries for every user (lets say 20 of them) automatically every few minutes.
public static async Task<IEvent[]> GetCalendarEvents()
{
var client = await EnsureClientCreated();
// Obtain calendar event data
var eventsResults = await (from i in client.Me.Events
where i.End >= DateTimeOffset.UtcNow
select i).Take(10).ExecuteAsync();
var events = eventsResults.CurrentPage.OrderBy(e => e.Start).ToArray();
return events;
}
public static async Task<ExchangeClient> EnsureClientCreated()
{
var _discoveryContext = await CacheHelper.GetDiscoveryContext();
var dcr = await _discoveryContext.DiscoverResourceAsync(ServiceResourceId);
return new ExchangeClient(ServiceEndpointUri, async () =>
{
return (await _discoveryContext.AuthenticationContext.AcquireTokenByRefreshTokenAsync(new SessionCache().Read("RefreshToken"),
new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(_discoveryContext.AppIdentity.ClientId, _discoveryContext.AppIdentity.ClientSecret),
ServiceResourceId))
.AccessToken;
});
}

Late answer I know. But if your still looking for this, or anyone else, this blog may be what your looking for.
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
A daemon/service app will get calendar events on behalf of a user, proving the user and the app are registered under the same tennat/organisation.

Related

How to request Contacts and Calendar Permissions on XamarinMac?

I'm trying to access the ContactStore of MacOS App, did the following implementation
public void Contacts()
{
//Starting
var store = new CNContactStore();
store.RequestAccess(CNEntityType.Contacts, async (bool granted, NSError error) =>
{
if (granted)
{
//Query things
}
});
}
The thing is that the Authorize screen never popup, and the Permission always say denied.
If I go to settings to remove that record, the app is not there.
Can someone please point me to the light?
It works for me with those two lines:
var contactStore = new CNContactStore();
var status = CNContactStore.GetAuthorizationStatus(CNEntityType.Contacts);
It could be also that you need to set NSContactsUsageDescription in info.plist.

C# BotFramework FormFlow, pass values to an external api

I am currently developing a simple c# formflow bot that captures the values and sends those values off to an external api, gets the json data back from the external api and creates Card Attachments based on the results returned. I am making the call to the external api in the OnCompletion delegate as follows, To keep it simple I am not passing any values to the api (For testing purposes)
.OnCompletion(async (context, profileForm) =>
{
var reply = context.MakeMessage();
var carsFromApi = await GetCarsAsync("/api/values");
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments = GetCards(carsFromApi);
await context.PostAsync(reply);
// Tell the user that the form is complete
})
I make the call to the api and store the results in "carsFromApi" , I step into that which is the following code snippet
private static async Task<List<Car>> GetCarsAsync(string path)
{
List<Car> car = new List<Car>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
car = await response.Content.ReadAsAsync<List<Car>>();
}
return await response.Content.ReadAsAsync<List<Car>>();
}
Problem is when I press F10 and go to the next line which is "reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;". The local variable that stored the cars "carsFromApi " is now null. This is the part where it all falls over. I cant pass this "carsFromApi" to "reply.Attachments = GetCards(carsFromApi);" I have tried to store the data in a private variable but that also seems to be null. The external api is working because it just returns a list of static text for now. Any ideas? Thanks in advance.
Based on what you are describing it sounds that your code is not existing through the path of the if (response.IsSuccessStatusCode). Check if that point is reached as I suspect an exception or something is going wrong with the request.
Alternatively, you can try doing the request in the ResumeAfter<T> method you specified when calling the Form instead of that in the OnCompletion delegate

Office 365 Calendar API - Filtering Events by Date in C# Client Libraries

In Client Libraries you can apply LINQ filtering on calendar event request:
var events = await (from i in Client.Me.Events where i.Subject == "Desired Event Name" select i)
.Take(50)
.ExecuteAsync();
Or one can use Where method, however for Start and End fields when we want events from specific time period filtering cannot be used as the DateTimes are stored as strings. Invocation of DateTime.Parse method causes an exception.
This is definitely supposed to be achievable, I even think it was possible at some point and can be done with REST. The begin/finish property is indexed according to documentation. Of course the results can be filtered once received but in that case I started getting events four years old. It really takes a lot of time to get through all the pages in the IPagedCollection in this approach. Fortunately though the events appear to be ordered by date, so you can stop the acquisition of new pages once events begin after your period of time.
Which version of Office 365 REST API you were initializing the OutLookServicesClient? I can filter the events use the v1.0 API. You can refer the code below to use the LINQ to filter the with start and end property:
OutlookServicesClient client = new OutlookServicesClient(new Uri("https://outlook.office.com/api/v1.0/"), () =>
{
return Task.Delay(10).ContinueWith(t => accessToken);
});
var events = await (from i in client.Me.Events where (i.Start > DateTimeOffset.Parse("2016-07-18") && i.End< DateTimeOffset.Parse("2016-07-25")) select i)
.Take(50)
.ExecuteAsync();
foreach (var appointment in events.CurrentPage)
{
Console.WriteLine($"{appointment.Subject}:\t{appointment.Start}~{appointment.End}");
}
Update(V2.0)
Install the V2.0 manage assembly Install-Package Microsoft.Office365.OutlookServices-V2.0
Code:
OutlookServicesClient client = new OutlookServicesClient(new Uri("https://outlook.office.com/api/v2.0/"), () =>
{
return Task.Delay(10).ContinueWith(t => accessToken);
});
var events = await (from i in client.Me.Events where (i.Start.DateTime.CompareTo("2016-07-18")>0 && i.End.DateTime.CompareTo("2016-07-25")<0) select i)
.Take(50)
.ExecuteAsync();
foreach (var appointment in events.CurrentPage)
{
Console.WriteLine($"{appointment.Subject}:\t{appointment.Start}~{appointment.End}");
}
Me. I still did not find solution to querying events with LINQ. To view a specified interval one can use
Client.Me.CalendarView(from as DateTimeOffset, to as DateTimeOffset) or Client.Me.Calendars["<valid calendar id>"].CalendarView(from, to). Filtering by function is probably exclusive to client-side code.

Method Prerequisites

The Situation
I'm working on a OAuth2 Api Wrapper. Some api routes are for logged people and some for anonymous and logged.
Here is an example of one method in my wrapper :
public async Task<UploadListResponse> List(bool pagination = false, int page = 1, int limit = 10)
{
var request = UploadRequests.List(pagination, page, limit);
var cancellationTokenSource = new CancellationTokenSource();
var restResponse = await Context.Client.ExecuteTaskAsync(request, cancellationTokenSource.Token);
return restResponse.Handle<UploadListResponse>();
}
I build a request with all parameter set up then execute the request and then handle the answer in case I have an api error and then output an object containing all the data that request gave me.
The problem
With OAuth2, when you log to the API you'll receive an access token and a refresh token. If your access token is expired you have to contact the api with your refresh token to get a fresh new access token.
As I said earlier some of my method needs you to be logged but if your access token is expired I want to try to refresh token before throwing an exception like with this method :
public async Task<bool> NeedRelog()
{
try
{
var validAuth = await ValidAuth();
}
catch
{
try
{
var refresh = await Refresh(Context.Client.Config.RefreshToken);
}
catch
{
return true;
}
}
return false;
}
ValidAuth check with the API if you are logged and if I have an exception then I'll try to refreshToken.
I want to tag method that need logged to call NeedRelog() and those who aren't tag to not call it.
I may just do it in every method but it wouldn't be clean.
What I've done so far
I've found a great tool : PostSharp that seems to fit my needs.
I've started to do a checkLog aspect like this :
[Serializable]
public class CheckLog : OnMethodBoundaryAspect, IOnStateMachineBoundaryAspect
{
public CheckLog()
{
ApplyToStateMachine = false;
}
public override void OnEntry(MethodExecutionArgs args)
{
var instance = (ApiService)args.Instance;
var res = instance.Parent.OAuth.NeedRelog().Result;
if (!res)
{
args.Exception = new Exception("Need to relog");
args.FlowBehavior = FlowBehavior.Return;
}
}
}
Where I'm stuck
The Main problem is with the call to my NeedRelog() Method. Due to the fact this is an async method I'm struggling to make my aspect await for it.
If my OnEntry method is async then It won't block the call if you are not logged.
If my OnEntry method is not async and I wait for needLog it freeze and nothing happen.
I really want to know to use this kind of "conditional method call" with postsharp, it looks awesome but the fact is after looking for hours in the documentation I didn't find a way to do what I want.
I'm starting to ask myself if it is even possible to achieve what I'm aiming to do.
Did you try using a way to make the call synchronous maybe with something like this stackoverflow.com/a/25097498/3131696 ? – M22an 5 hours ago
As I can't mark a comment as answering a question I quote your comment to make this question answered as it is said here : link
Thanks you for this M22an.

Getting a list of all users via Valence

I am trying to get a list of all users in our instance of Desire2Learn using a looping structure through the bookmarks however for some reason it continuously loops and doesn't return. When I debug it it is showing massive amounts of users (far more than we have in the system as shown by the User Management Tool. A portion of my code is here:
public async Task<List<UserData>> GetAllUsers(int pages = 0)
{
//List<UserData> users = new List<UserData>();
HashSet<UserData> users = new HashSet<UserData>();
int pageCount = 0;
bool getMorePages = true;
var response = await Get<PagedResultSet<UserData>>("/d2l/api/lp/1.4/users/");
var qParams = new Dictionary<string, string>();
do
{
qParams["bookmark"] = response.PagingInfo.Bookmark;
//users = users.Concat(response.Items).ToList<UserData>();
users.UnionWith(response.Items);
response = await Get<PagedResultSet<UserData>>("/d2l/api/lp/1.4/users/", qParams);
if (pages != 0)
{
pageCount++;
if (pageCount >= pages)
{
getMorePages = false;
}
}
}
while (response.PagingInfo.HasMoreItems && getMorePages);
return users.ToList();
}
I originally was using the List container that is commented out but just switched to the HashSet to see if I could notice if duplicates where being added.
It's fairly simple, but for whatever reason it's not working. The Get<PagedResultSet<UserData>>() method simply wraps the HTTP request logic. We set the bookmark each time and send it on.
The User Management Tool indicates there are 39,695 users in the system. After running for just a couple of minutes and breaking on the UnionWith in the loop I'm showing that my set has 211,800 users.
What am I missing?
It appears that you’ve encountered a defect in this API. The next course of action is for you to have your institution’s Approved Support Contact open an Incident through the Desire2Learn Helpdesk. Please make mention in the Incident report that Sarah-Beth Bianchi is aware of the issue, and I will work with our Support team to direct this issue appropriately.

Categories