I'm creating an app that access the Microsoft Cloud API to get health data. It uses OAuth to log in when you hit the Sign In Button
private void signinButton_Click(object sender, RoutedEventArgs e)
{
UriBuilder uri = new UriBuilder("https://login.live.com/oauth20_authorize.srf");
var query = new StringBuilder();
query.AppendFormat("redirect_uri={0}", Uri.EscapeDataString(RedirectUri));
query.AppendFormat("&client_id={0}", Uri.EscapeDataString(ClientId));
query.AppendFormat("&scope={0}", Uri.EscapeDataString(Scopes));
query.Append("&response_type=code");
uri.Query = query.ToString();
this.webView.Visibility = Visibility.Visible;
this.webView.Navigate(uri.Uri);
}
This brings up a webView with the page to log in using Microsoft credentials. Once completed, it leads to this:
private async void WebView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
//
// When the web view navigates to our redirect URI, extract the authorization code from
// the URI and use it to fetch our access token. If no authorization code is present,
// we're completing a sign-out flow.
//
if (args.Uri.LocalPath.StartsWith("/oauth20_desktop.srf", StringComparison.OrdinalIgnoreCase))
{
WwwFormUrlDecoder decoder = new WwwFormUrlDecoder(args.Uri.Query);
var code = decoder.FirstOrDefault((entry) => entry.Name.Equals("code", StringComparison.OrdinalIgnoreCase));
var error = decoder.FirstOrDefault((entry) => entry.Name.Equals("error", StringComparison.OrdinalIgnoreCase));
var errorDesc = decoder.FirstOrDefault((entry) => entry.Name.Equals("error_description", StringComparison.OrdinalIgnoreCase));
// Check the code to see if this is sign-in or sign-out
if (code != null)
{
// Hide the browser again, no matter what happened...
sender.Visibility = Visibility.Collapsed;
if (error != null)
{
this.responseText.Text = string.Format("{0}\r\n{1}", error.Value, errorDesc.Value);
return;
}
var tokenError = await this.GetToken(code.Value, false);
if (string.IsNullOrEmpty(tokenError))
{
this.responseText.Text = "Successful sign-in!";
this.signoutButton.IsEnabled = true;
this.signinButton.IsEnabled = false;
this.getProfileButton.IsEnabled = true;
this.getDevicesButton.IsEnabled = true;
this.getActivitiesButton.IsEnabled = true;
this.getDailySummaryButton.IsEnabled = true;
this.getHourlySummaryButton.IsEnabled = true;
}
else
{
this.responseText.Text = tokenError;
}
}
else
{
this.responseText.Text = "Successful sign-out!";
this.signoutButton.IsEnabled = false;
this.signinButton.IsEnabled = true;
this.getProfileButton.IsEnabled = false;
this.getDevicesButton.IsEnabled = false;
this.getActivitiesButton.IsEnabled = false;
this.getDailySummaryButton.IsEnabled = true;
this.getHourlySummaryButton.IsEnabled = false;
}
}
}
private async Task<string> GetToken(string code, bool isRefresh)
{
UriBuilder uri = new UriBuilder("https://login.live.com/oauth20_token.srf");
var query = new StringBuilder();
query.AppendFormat("redirect_uri={0}", Uri.EscapeDataString(RedirectUri));
query.AppendFormat("&client_id={0}", Uri.EscapeDataString(ClientId));
query.AppendFormat("&client_secret={0}", Uri.EscapeDataString(ClientSecret));
if (isRefresh)
{
query.AppendFormat("&refresh_token={0}", Uri.EscapeDataString(code));
query.Append("&grant_type=refresh_token");
}
else
{
query.AppendFormat("&code={0}", Uri.EscapeDataString(code));
query.Append("&grant_type=authorization_code");
}
uri.Query = query.ToString();
var request = WebRequest.Create(uri.Uri);
try
{
using (var response = await request.GetResponseAsync())
{
using (var stream = response.GetResponseStream())
{
using (var streamReader = new StreamReader(stream))
{
var responseString = streamReader.ReadToEnd();
var jsonResponse = JObject.Parse(responseString);
this.creds.AccessToken = (string)jsonResponse["access_token"];
this.creds.ExpiresIn = (long)jsonResponse["expires_in"];
this.creds.RefreshToken = (string)jsonResponse["refresh_token"];
string error = (string)jsonResponse["error"];
return error;
}
}
}
}
catch (Exception ex)
{
return ex.Message;
}
}
I don't want users to have to accept the permissions every time the app is launched. Is there a way to save credentials locally so that it automatically authenticates on launch? Thanks!
You can use
Windows.Storage.ApplicationData.Current.LocalSettings
This process good described by this answer Best Way to keep Settings for a WinRT App?
The code in link identity to UWP
Store the needed oauth parts in the credential locker API. Never store these kind of information in the normal settings API.
On start read the oauth information and use the refreshtoken to get a new access token.
More Information here.
https://msdn.microsoft.com/en-us/library/windows/apps/mt270189.aspx
Related
I am trying to learn how to use InstaSharper but I've got a problem. Everytime when I run my code to see the new code working, I have to login, but if I do this too many times, I will be blocked by Instagram. I was blocked on two accounts allready. So I need a solution not to always have to login like a token or something.
My login:
public static async void Login()
{
api = InstaApiBuilder.CreateBuilder()
.SetUser(user)
.UseLogger(new DebugLogger(LogLevel.Exceptions))
.SetRequestDelay(RequestDelay.FromSeconds(5, 10))
.Build();
var loginRequest = await api.LoginAsync();
if (loginRequest.Succeeded)
{
Console.WriteLine("Login In Success!");
Follow("artofbokeh");
}
else
{
Console.WriteLine("Login Failed");
}
}
Yes you can save your state in a a file and load it again.
Please give this piece of code a try, it is taken form the official github repo which can be found under this link: Github repo
// create user session data and provide login details
var userSession = new UserSessionData
{
UserName = "username",
Password = "password"
};
// create new InstaApi instance using Builder
_instaApi = InstaApiBuilder.CreateBuilder()
.SetUser(userSession)
.UseLogger(new DebugLogger(LogLevel.Exceptions)) // use logger for requests and debug messages
.SetRequestDelay(TimeSpan.FromSeconds(2))
.Build();
const string stateFile = "state.bin";
try
{
if (File.Exists(stateFile))
{
Console.WriteLine("Loading state from file");
Stream fs = File.OpenRead(stateFile);
fs.Seek(0, SeekOrigin.Begin);
_instaApi.LoadStateDataFromStream(fs);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (!_instaApi.IsUserAuthenticated)
{
// login
Console.WriteLine($"Logging in as {userSession.UserName}");
var logInResult = await _instaApi.LoginAsync();
if (!logInResult.Succeeded)
{
Console.WriteLine($"Unable to login: {logInResult.Info.Message}");
return false;
}
}
var state = _instaApi.GetStateDataAsStream();
using (var fileStream = File.Create(stateFile))
{
state.Seek(0, SeekOrigin.Begin);
state.CopyTo(fileStream);
}
Edit the function to get the state data does the following:
public Stream GetStateDataAsStream()
{
var state = new StateData
{
DeviceInfo = _deviceInfo,
IsAuthenticated = IsUserAuthenticated,
UserSession = _user,
Cookies = _httpRequestProcessor.HttpHandler.CookieContainer
};
return SerializationHelper.SerializeToStream(state);
}
So yes you save all needed information to not login everytime. If this doesnt work for you you are doing something wrong. Please post your code where you load / save the state file.
I'm trying to create a program that will download all my OneNote files from OneDrive. But when I try to authenticate using msaAuthenticationProvider a white window appears and then nothing happens. I think the window is supposed to be the Microsoft login, but nothing appears in it.
Here's my code:
string[] scopes = new string[] {
"onedrive.readonly",
"wl.signin"
};
var msaAuthenticationProvider = new MsaAuthenticationProvider(
clientId,
returnURL,
scopes);
await msaAuthenticationProvider.AuthenticateUserAsync();
var client = new OneDriveClient(URL, msaAuthenticationProvider);
It gets to the AuthenticateUserAsync method, then the window apperas, and after that nothing happens.
I'm also not sure what the returnURL is supposed to be because all examples where either for an app version or just said return URL without giving any examples.
sorry for the delay. Have you tried this method:
msaAuthenticationProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
Edit : If the last known connection token is usable, this method can be used to authenticate the user without prompt it. So, this restore the last authentication cache if it can or prompt the user to give his login and password. This can replace the already used AuthenticateUserAsync method. I had the same issue and this method solved it.
Edit 2 : The OneDrive SDK documentation is very poor, I found this myself fiercely as I found that you can get the connection token (to save it for example) and inject it when you need like that in an async task :
if (_OneDriveCacheBlob == null)
{
bool needtosaveblob = true;
_OneDriveCacheBlob = null;
CredentialCache cc = new CredentialCache();
_OneDriveCacheBlob = GetUser(CurrentUserName).OneDriveAuthProviderBlob;
if (_OneDriveCacheBlob != null)
{
cc.InitializeCacheFromBlob(_OneDriveCacheBlob);
needtosaveblob = false;
}
MsaAuthenticationProvider msaAuthProvider = new MsaAuthenticationProvider(OneDriveClass.clientId, OneDriveClass.returnUrl, scopes, cc);
int timeout = 15;
_ = Task.Run(() => WaitForODConnection(msaAuthProvider));
while (!WaitForODConnectionExecuted)
{
if (timeout <= 0)
break;
await Task.Delay(TimeSpan.FromSeconds(1));
timeout -= 1;
}
WaitForODConnectionExecuted = false;
if (timeout <= 0)
{
// Request for reconnection to OneDrive because of invalid Blob
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
//This method requests a new login by a simple msaAuthProvider.AuthenticateUserAsync() call from a new instance of MsaAuthenticationProvider and a new instance of CredentialCache.
//ChangeOneDriveAccount();
});
}
else
{
_OneDriveClient = new OneDriveClient(OneDriveClass.basUrl, msaAuthProvider);
}
string accessToken = msaAuthProvider.CurrentAccountSession.AccessToken;
JObject json = await GetUserInfos(msaAuthProvider.CurrentAccountSession.AccessToken);
if (json != null)
{
// If you need
oneDriveUserName = json["name"].ToString();
oneDriveEmail = json["emails"]["account"].ToString();
}
else
{
//Unable to get OneDrive user informations;
}
if (needtosaveblob)
{
_OneDriveCacheBlob = cc.GetCacheBlob();
//You can save _OneDriveCacheBlob to reuse it later;
}
}
To get the user infos :
/// <summary>
/// Return User informations as a JObject. To get username and email, if return isn't null :
/// username = json["name"].ToString();
/// email = json["emails"]["account"].ToString();
/// </summary>
/// <param name="accessToken">accesstoken of Onedrive account</param>
/// <returns>JObject value</returns>
public static async Task<JObject> GetUserInfos(string accessToken)
{
JObject json = null;
Uri uri = new Uri($"https://apis.live.net/v5.0/me?access_token={accessToken}");
System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(uri);
//user info returnd as JSON
string jsonUserInfo = await result.Content.ReadAsStringAsync();
if (jsonUserInfo != null)
{
json = JObject.Parse(jsonUserInfo);
//username = json["name"].ToString();
//email = json["emails"]["account"].ToString();
}
return json;
}
And because the OneDrive method never expires if the token is no longer usable :
bool WaitForODConnectionExecuted = false;
private async Task WaitForODConnection(MsaAuthenticationProvider msaAuthProvider)
{
await msaAuthProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
WaitForODConnectionExecuted = true;
}
It was not funny and I think my code is not clean so do not use it as it is without working a little on it.
I am working with windows phone 8.1 application and I want to login with Google Plus , for that I have create Client id and Client secret from Google
string uri = string.Format("{0}?response_type=code&client_id={1}&redirect_uri={2}&scope={3}&approval_prompt=force",
authEndpoint,
clientId,
"urn:ietf:wg:oauth:2.0:oob",
scope);
webBrowser.Navigate(new Uri(uri, UriKind.Absolute));
and I Got string something like 4/BGIl1Na3TQJlAD8SQ7blvHyONJ_Jyav8COHa7tIrAdo
I want User Email and Name from account for signup or login
Please help me for that.
Thank you
Implement the interafce IWebAuthenticationContinuable to your class and then under ContinueWebAuthentication() Method use this code :
var authData = GetGoogleSuccessCode(args.WebAuthenticationResult.ResponseData);
var userData = await GetTokenAndUserInfo(authData);
private string GetGoogleSuccessCode(string data)
{
if (string.IsNullOrEmpty(data)) return null;
var parts = data.Split('&')[0].Split('=');
for (int i = 0; i < parts.Length; ++i)
{
if (parts[i] == "Success code")
{
return parts[i + 1];
}
}
return null;
}
public async Task<string> GetTokenAndUserInfo(string code)
{
var client = new HttpClient();
var auth = await client.PostAsync("https://accounts.google.com/o/oauth2/token", new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("code", code),
new KeyValuePair<string, string>("client_id",Constants.GoogleAppId),
new KeyValuePair<string, string>("client_secret",Constants.GoogleAppSecret),
new KeyValuePair<string, string>("grant_type","authorization_code"),
new KeyValuePair<string, string>("redirect_uri","urn:ietf:wg:oauth:2.0:oob"),
}));
var data = await auth.Content.ReadAsStringAsync();
var j = JToken.Parse(data);
var token = j.SelectToken("access_token");
var searchUrl = "https://www.googleapis.com/oauth2/v2/userinfo";
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.ToString());
HttpResponseMessage res = await client.GetAsync(searchUrl);
string content = await res.Content.ReadAsStringAsync();
return content;
}
And add following code in App.xaml.cs file:
public static ContinuationManager ContinuationManager { get; private set; }
public App()
{
ContinuationManager = new ContinuationManager();
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
#if WINDOWS_PHONE_APP
ContinuationManager.MarkAsStale();
#endif
// TODO: Save application state and stop any background activity
deferral.Complete();
}
protected override void OnActivated(IActivatedEventArgs args)
{
#if WINDOWS_PHONE_APP
if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation)
{
var continuationEventArgs = args as IContinuationActivatedEventArgs;
if (continuationEventArgs != null)
{
ContinuationManager.Continue(continuationEventArgs);
ContinuationManager.MarkAsStale();
}
}
#endif
}
Don't forget to include Continuation Manager class file in your project.
You will get the user info.
I'm not an expert.
It looks like you need to request the email scope:
https://developers.google.com/+/web/api/rest/oauth#login-scopes
as well as (probably) the profile scope (same page).
If this isn't helpful, please edit your question and show more of your code (obscuring your secret API key, etc.).
I have also searched for sign in using facebook and google in WIN RT Apps and finally found solution from this link :
Web authentication broker sample
Maybe it will help you also.
I would like to make an apprequest from my application using the c# facebook sdk.
After some investigation I found some examples that are using the app access token which has the following format : access_token = YOUR_APP_ID|YOU_APP_SECRET
Now I have tried the following using the appAccessToken:
string appAccessToken = String.Format("{0}|{1}",Constants.FacebookAppId,Constants.FacebookAppSecret);
FacebookClient fb = new FacebookClient(appAccessToken);
fb.PostCompleted += (o, args) =>
{
if (args.Error != null)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(args.Error.Message));
return;
}
};
dynamic parameters = new ExpandoObject();
parameters.message = "Test: Action is required";
parameters.data = "Custom Data Here";
fb.PostTaskAsync(String.Format("{0}/apprequests", Constants.FacebookAppId), parameters);
But it doesn't work... Is it possible to make an apprequest using the c# Facebook skd?
I hope anybody can help me.
Have a nice day!
With best regards, Matthias
To get access token of App.
Just use this link and you will get it
https://developers.facebook.com/tools/access_token/
Please check this out : http://facebooksdk.net/docs/phone/tutorial.
To get user access token
Add this to App.xaml.cs
internal static string AccessToken = String.Empty;
internal static string FacebookId = String.Empty;
public static bool IsAuthenticated = false;
public static FacebookSessionClient FacebookSessionClient = new FacebookSessionClient(Constants.FacebookAppId);
Create a method to get Access token
private async Task Authenticate()
{
try
{
_session = await App.FacebookSessionClient.LoginAsync("user_about_me,read_stream");
App.AccessToken = _session.AccessToken;
App.FacebookId = _session.FacebookId;
}
catch (InvalidOperationException e)
{
var messageBox = new OneButtonCustomMessageBox
{
TbMessageTitle = { Text = "Facebook Login Error" },
TbMessageContent = {Text = e.Message}
};
messageBox.Show();
}
}
After you have Access Token you will use a method like this to call GraphAPI from FB to get user information
private async void LoadUserInfo()
{
var fb = new FacebookClient(App.AccessToken);
fb.GetCompleted += (o, e) =>
{
if (e.Error != null)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(e.Error.Message));
return;
}
var result = (IDictionary<string, object>)e.GetResultData();
Dispatcher.BeginInvoke(async () =>
{
var profilePictureUrl = string.Format("https://graph.facebook.com/{0}/picture?type={1}&access_token={2}", App.FacebookId, "normal", App.AccessToken);
this.ImgAvatar.Source = new BitmapImage(new Uri(profilePictureUrl));
this.TxtName.Text = String.Format("{0}", (string)result["name"]);
});
};
await fb.GetTaskAsync("me");
}
Working Platform: ASP.NET 4.0 C# ( Framework Agnostic )
Google GData is my dependency
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Documents;
I have two pages Auth and List.
Auth redirects to Google Server like this
public ActionResult Auth()
{
var target = Request.Url.ToString().ToLowerInvariant().Replace("auth", "list");
var scope = "https://docs.google.com/feeds/";
bool secure = false, session = true;
var authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
return new RedirectResult(authSubUrl);
}
Now it reaches the List Page if Authentication is successful.
public ActionResult List()
{
if (Request.QueryString["token"] != null)
{
String singleUseToken = Request.QueryString["token"];
string consumerKey = "www.blahblah.net";
string consumerSecret = "my_key";
string sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null).ToString();
var authFactory = new GOAuthRequestFactory("writely", "qwd-asd-01");
authFactory.Token = sessionToken;
authFactory.ConsumerKey = consumerKey;
authFactory.ConsumerSecret = consumerSecret;
//authFactory.TokenSecret = "";
try
{
var service = new DocumentsService(authFactory.ApplicationName) { RequestFactory = authFactory };
var query = new DocumentsListQuery();
query.Title = "project";
var feed = service.Query(query);
var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(result);
}
catch (GDataRequestException gdre)
{
throw;
}
}
}
This fails at the line var feed = service.Query(query); with the error
Execution of request failed: https://docs.google.com/feeds/default/private/full?title=project
The HttpStatusCode recieved on the catch block is HttpStatusCode.Unauthorized
What is wrong with this code? Do I need to get TokenSecret? If so how?
You need to request a token from Google and use it to intialize your DocumentsService instance.
Here's an example using Google's ContactsService. It should be the same for the DocumentsService.
Service service = new ContactsService("My Contacts Application");
service.setUserCredentials("your_email_address_here#gmail.com", "yourpassword");
var token = service.QueryClientLoginToken();
service.SetAuthenticationToken(token);
But as you mentioned, you are using AuthSub. I jumped the gun a bit too fast.
I see that you are requesting a session token. According to the documentation of the API you must use the session token to authenticate requests to the service by placing the token in the Authorization header. After you've set the session token, you can use the Google Data APIs client library.
Here's a complete example (by Google) on how to use AuthSub with the .NET client library:
http://code.google.com/intl/nl-NL/apis/gdata/articles/authsub_dotnet.html
Let me include a shortened example:
GAuthSubRequestFactory authFactory =
new GAuthSubRequestFactory("cl", "TesterApp");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
EventQuery query = new EventQuery();
query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");
EventFeed calFeed = service.Query(query);
foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
{
//...
}
And if I see correctly your example code pretty follows the same steps, except that you set the ConsumerKey and ConsumerSecret for the AuthFactory which is not done in the example by Google.
Used the 3-legged OAuth in the Google Data Protocol Client Libraries
Sample Code
string CONSUMER_KEY = "www.bherila.net";
string CONSUMER_SECRET = "RpKF7ykWt8C6At74TR4_wyIb";
string APPLICATION_NAME = "bwh-wssearch-01";
string SCOPE = "https://docs.google.com/feeds/";
public ActionResult Auth()
{
string callbackURL = String.Format("{0}{1}", Request.Url.ToString(), "List");
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Scope = SCOPE,
Callback = callbackURL,
SignatureMethod = "HMAC-SHA1"
};
OAuthUtil.GetUnauthorizedRequestToken(parameters);
string authorizationUrl = OAuthUtil.CreateUserAuthorizationUrl(parameters);
Session["parameters"] = parameters;
ViewBag.AuthUrl = authorizationUrl;
return View();
}
public ActionResult List()
{
if (Session["parameters"] != null)
{
OAuthParameters parameters = Session["parameters"] as OAuthParameters;
OAuthUtil.UpdateOAuthParametersFromCallback(Request.Url.Query, parameters);
try
{
OAuthUtil.GetAccessToken(parameters);
GOAuthRequestFactory authFactory = new GOAuthRequestFactory("writely", APPLICATION_NAME, parameters);
var service = new DocumentsService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
var query = new DocumentsListQuery();
//query.Title = "recipe";
var feed = service.Query(query);
var docs = new List<string>();
foreach (DocumentEntry entry in feed.Entries)
{
docs.Add(entry.Title.Text);
}
//var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(docs);
}
catch (GDataRequestException gdre)
{
HttpWebResponse response = (HttpWebResponse)gdre.Response;
//bad auth token, clear session and refresh the page
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Session.Clear();
Response.Write(gdre.Message);
}
else
{
Response.Write("Error processing request: " + gdre.ToString());
}
throw;
}
}
else
{
return RedirectToAction("Index");
}
}
This 2-legged sample never worked for me for google docs.