Sign in to multiple Microsoft account in Windows Store app - c#

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.

Related

Create Microsoft Graph GraphServiceClient with user/password unattended

I am creating a console application that connects to Microsoft Graph using the Microsoft Graph API (as shown in https://github.com/microsoftgraph/console-csharp-connect-sample).
Everything is working fine, but I wonder if there is a way where I can authenticate a user (when I already know their user/password) without them needing to manually enter their credentials on the "Sing in to your account" window rendered on the desktop.
The idea is basically to run the application unattended, so there is no need for the user to be entering their credentials when the application starts. I canĀ“t find any relevant information on the subject.
Is that even possible?
EDIT
After following the link #DanSilver posted about geting access without a user, I tried the sample suggested in that link (https://github.com/Azure-Samples/active-directory-dotnet-daemon-v2). Although that is an MVC application that forces users to authenticate (precisely what I wanted to avoid) I have managed to use part of the authentication code in that sample with my console application. After giving authorization to the application manually through a request to https://login.microsoftonline.com/myTenantId/adminconsent I can create a GraphServiceClient in my console app that connects to Graph without user interaction. So I mark the answer as valid.
Just in case someone is in the same situation, the GraphServiceclient is created as:
GraphServiceClient graphServiceClientApplication = new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string clientId = "yourClientApplicationId";
string authorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
string tenantId = "yourTenantId";
string msGraphScope = "https://graph.microsoft.com/.default";
string redirectUri = "msalXXXXXX://auth"; // Custom Redirect URI asigned in the Application Registration Portal in the native Application Platform
string clientSecret = "passwordGenerated";
ConfidentialClientApplication daemonClient = new ConfidentialClientApplication(clientId, String.Format(authorityFormat, tenantId), redirectUri, new ClientCredential(clientSecret), null, new TokenCache());
AuthenticationResult authResult = await daemonClient.AcquireTokenForClientAsync(new string[] { msGraphScope });
string token = authResult.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
));
One idea is using the "app only" authorization flow. The idea is that you can have long running apps access the Microsoft Graph without user authentication. The main difference is instead of the access token granting access to a particular user, it grants your app access to resources that you've consented to in advance. There will be no user login dialog and you can programmatically fetch access tokens to call the Graph API.
To reiterate that these tokens aren't for a particular user, consider making a GET request to 'https://graph.microsoft.com/v1.0/me'. This will return an error since the access token isn't for a particular user and "me" doesn't mean anything. Requests should be sent with full user ids "like graph.microsoft.com/users/someuser#contosos.com".
More information on this can be found at the Get access without a user documentation page.
Another idea is to let the user authenticate the first time they use your app and then store a refresh token. These tokens live longer (a few months IIRC) and then you won't need to prompt for user consent each time the app runs. Refresh tokens can be exchanged for access tokens that live 60 minutes and those can be used to call Graph API on behalf of users.
More info on refresh tokens: https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user#5-use-the-refresh-token-to-get-a-new-access-token
I did want to come back out here and share, since I ran into this problem yesterday, and the idea of granting read/write mailbox access for my application... to EVERYONE'S EMAIL BOX IN THE ENTIRE ORGANIZATION... was way over the top for my needs. (And that is exactly what happens when you start talking about granting Application level permissions instead of delegated permissions to your registered app).
It's a simple use case: I had a nightly process that needed to automate sending of emails from a shared mailbox using a traditional AD service account.
Thankfully... even though they are on the march to eliminate passwords (lol)... someone at Microsoft still recognizes my use case, and it's lack of apples-to-apples alternatives in Azure AD. There is still an extension method we can lean on to get the job done:
private AuthenticationContext authContext = null;
authContext = new AuthenticationContext("https://login.microsoftonline.com/ourmail.onmicrosoft.com",
new TokenCache());
result = authContext.AcquireTokenAsync("https://graph.microsoft.com/",
"12345678-1234-1234-1234-1234567890",
new UserPasswordCredential(
Environment.GetEnvironmentVariable("UID", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("UPD", EnvironmentVariableTarget.User)
)).Result;
You can replace those GetEnvironmentVariable calls with your Username (UID) and Password (UPD). I just stuff them in the environment variables of the service account so I didn't have to check anything into source control.
AcquireTokenAsync is an extension method made available from the Microsoft.IdentityModel.Clients.ActiveDirectory namespace. From there, it's a simple business to fire up a GraphClient.
string sToken = result.AccessToken;
Microsoft.Graph.GraphServiceClient oGraphClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", sToken);
return Task.FromResult(0);
}));
The last bit of magic was to add these permissions to Application registration I created in Azure AD (where that GUID came from). The application has be defined as a Public client (there's a radio button for that towards the bottom of the authentication tab). I added the following 5 DELEGATED permissions (NOT application permissions):
Microsoft Graph
1. Mail.ReadWrite.Shared
2. Mail.Send.Shared
3. User.Read
4. email
5. openid
Since user consents are actually blocked in our organization, another permissions admin had to review my application definition and then do an admin level grant of those rights, but once he did, everything lit up and worked like I needed: limited access by a service account to a single shared mailbox, with the actual security of that access being managed in Office 365 and not Azure AD.

Consume Office 365 REST API Without UI

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"]);
}
}
}

C# application - Read Google Contacts

Since Google stopped support for their older Auth, and now we have to use oAuth 2, our simple desktop application can no longer read contacts from my google account.
Fine - i understand this, however this new oAuth 2 is extraordinarily complicated... and im not talking about from a developer perspective. From what i am reading online. We now have to make our customers jump over a multitude of hoops in order for our simple application to read contacts stored in their Google mail/Contacts.
My iPhone seems to be able to sync contacts just fine with just the typical email and password that i entered about a year ago. How do they get it to work? and yet with my simple desktop application, the client has to be rummaging around in Google Developer sites and with API settings etc. I'm a developer and im confused!! - could you imagine what my customer is going to go through... it cant be this complicated.
Is there anyone who can give me the simple 1,2,3 to get a C# desktop application to go off and get the contacts (read-only) from a particular Gmail account... with the least amount of fiddling around (for the owner of the Gmail account).
Ill do all the hard work in the application - i just don't want to client to have to spend an hour authorizing and creating API's and clicking around in a developer site (he/she is NOT a developer).
The main problem you have here is that contacts is an old Gdata API. It is possible to use Oauth2 with the Gdata library but its not pretty. Personally I like to hack things a little. I use the Current .net client library with the old Gdata client library.
Nuget New client library for authentication:
not 100% sure this is the only one you need let me know if it doesn't work we can find it. You basically need Google.apis.auth.oauth2 and google apis.util.store.
Install-Package Google.Apis.Auth
Nuget old client library for contacts:
Install-Package Google.GData.Contacts
Code
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store;
using Google.Contacts;
using Google.GData.Client;
using System;
using System.Threading;
public static void auth()
{
string clientId = "xxxxxx.apps.googleusercontent.com";
string clientSecret = "xxxxx";
string[] scopes = new string[] { "https://www.googleapis.com/auth/contacts.readonly" }; // view your basic profile info.
try
{
// Use the current Google .net client library to get the Oauth2 stuff.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, "test"
, CancellationToken.None
, new FileDataStore("test")).Result;
// Translate the Oauth permissions to something the old client libray can read
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.AccessToken = credential.Token.AccessToken;
parameters.RefreshToken = credential.Token.RefreshToken;
RunContactsSample(parameters);
Console.ReadLine();
}
catch (Exception ex)
{
Console.ReadLine();
}
Console.ReadLine();
}
/// <summary>
/// Send authorized queries to a Request-based library
/// </summary>
/// <param name="service"></param>
private static void RunContactsSample(OAuth2Parameters parameters)
{
try
{
RequestSettings settings = new RequestSettings("Google contacts tutorial", parameters);
ContactsRequest cr = new ContactsRequest(settings);
Feed<Contact> f = cr.GetContacts();
foreach (Contact c in f.Entries)
{
Console.WriteLine(c.Name.FullName);
}
}
catch (Exception a)
{
Console.WriteLine("A Google Apps error occurred.");
Console.WriteLine();
}
}
Tutorial can be found here
Google developers console
All applications accessing google apis must be registered on Google developers console. It is the application accessing Google that is registered users running the code do not need to do this step. Its you as a developer who has to register it.
From this you get the client id and client secret used in the code above.
I have done this but its all a bit of a blur from like you say, a lot of fiddling.
I think you can sign up and setup a project in google developer console and generate a service account. Then the client will need to sign in to HERE as the google app admin and fill out the clientID field with the name of your service account generated by the developer console and the API scope you need access to.
In the end I just logged in as the client to their admin panel and set it up for them. There is no easy way about it without the client also engaging a google apps re-seller to assist. I managed to figure it out as a developer with a lot of googling.

What is the url for Facebook authentication with WebAuthenticationCoreManager

I have this code for Facebook authentication
var scopes = "email user_birthday user_events user_friends user_about_me";
WebAccountProvider facebookAccountProvider =
await WebAuthenticationCoreManager
.FindAccountProviderAsync("https://www.facebook.com/dialog/oauth");
WebTokenRequest webTokenRequest = new WebTokenRequest(facebookAccountProvider, scopes);
WebAuthenticationCoreManager does not like Facebook OAuth endpoint https://www.facebook.com/dialog/oauth and sets my facebookAccountProvider to null.
Does anyone know how I can get FindAccountProviderAsync working with Facebook?
WebAuthenticationCoreManager.FindAccountProviderAsync() finds account providers that have been registered with Windows. This means (say) the Facebook app would need to support it (which it doesn't). At the moment it's only useful for Microsoft accounts and organizational accounts (Office 365/Azure AD).
If you want to use Facebook's OAuth 2.0 implementation directly you can use WebAuthenticationBroker instead (there are examples on that page).

Facebook Login for Windows Phone 8.1

I am trying to figure out a simple code for logging in facebook from a Windows Phone 8.1 app (C#).
As the Facebook Client from NuGet doesn't target Windows Phone 8.1, it seems I'd have to write some extra code. As I read in this facebook post, I'd need to launch an Uri to invoke the Login Dialog. That much, I managed to do :
await Launcher.LaunchUriAsync(FacebookUri.DisplayLoginDialog);
where DisplayLoginDialog is a static string object with the required necessary data on the request (appId, productId, permissions, etc). I was redirected to the facebook app, to accept that my app requires such permissions.
And I accepted.
So what now? How do I get a response or something with the access_token? I have researched a lot for this but I wasn't able to find relevant posts.
The same facebook link from above, says at the section of Handling the Login Dialog that :
If someone successfully logs in, the URI association of your app will be automatically triggered, meaning they will be sent to your app, along with an access token:
msft-{ProductID}://authorize/?
access_token={user-access-token}&
expires_in={expiration-time-of-token}
But I am confused on how to actually use this in C#.
How do I get a response with the access token after the login suceeded or and error code and error message if it failed, like it is written in the facebook post?
In the Package.appxmanifest file, go to 'Declarations' Tab and add a Protocol Declaration.
In the Name Field, enter your Microsoft product id as 'msft-PRODUCT_ID' [product id without dashes].
In the App.xaml.cs file, add the following code
protected override void OnActivated(IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.Protocol)
{
ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs;
Uri responseUri = eventArgs.Uri;
//Now you can use responseUri to retrieve the access token and expiration time of token
}
base.OnActivated(args);
}
You might want to take a look at this: Sign into Windows Phone 8 apps with Facebook Login.
This tutorial on the Microsoft blogs shows you how you can tie directly into the Facebook app from your app like many do on Android and iOS.

Categories