I am integrating the Facebook login for Xamarin PCL. I have followed the sample "Comic book" https://github.com/moljac/Xamarin.Auth.Samples.NugetReferences and integrated the login process into my application.
But, I am getting the below exception while the oauth presenter is called from Xamarin PCL project to present the Facebook UI in the app on iOS. I have provided the code i used below to call the Facebook UI for login.
Error:
Warning: Attempt to present UINavigationController: 0x7ffa01275a00 on Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x7ffa00576b80 whose view is not in the window hierarchy!
Code:
var auth = new OAuth2Authenticator(
clientId: FacebookClientId,
scope: "email",
authorizeUrl: new Uri(FacebookAuthorizeUrl),
redirectUrl: new Uri(FacebookRedirectUri));
auth.AllowCancel = true;
auth.Completed += async (s, es) =>
{
if (es.IsAuthenticated)
{
var request = new OAuth2Request(
"GET",
new Uri("https://graph.facebook.com/me?fields=email,name,picture,cover,birthday"),
null,
es.Account);
var fbResponse = await request.GetResponseAsync();
if (fbResponse != null)
{
var fbUser = JsonValue.Parse(fbResponse.GetResponseText()); // Getting the error for System.Json double exception issue as well for OAuth v1.5.0.0 only in iOS.
if (fbUser != null)
{
LoginViewModel loginViewModel = new LoginViewModel();
if (!string.IsNullOrEmpty(fbUser["name"]))
{
loginViewModel.ProfileName = fbUser["name"];
loginViewModel.UserName = fbUser["name"];
}
LoginViewModel.SelfLoginViewModel = loginViewModel;
await this.Navigation.PushModalAsync(new MasterHome());
}
else
{
await DisplayAlert("Message", "Check your internet connection!", "OK");
}
}
else
{
await DisplayAlert("Message", "Check your internet connection!", "OK");
}
}
};
auth.Error += (s, ea) =>
{
StringBuilder sb = new StringBuilder();
sb.Append("Error= ").AppendLine($"{ea.Message}");
DisplayAlert("Authentication Error", sb.ToString(),"OK");
};
Xamarin.Auth.Presenters.OAuthLoginPresenter presenter = null;
presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(auth);
How can I resolve this error?
Note: The same code works perfectly for Android. I am facing this issue only in iOS. I am using Xamarin Auth v1.5.0.0
Issue resolved by Stacking the pages in the application in Navigation Stack.
Related
I wrote a dll using .NET C# that was supposed to send emails using graph API.
When I'm using the dll from a console application - everything works as expected: if the user is logged in the mail is sent, and if not - a screen pops up to connect.
But, when I try to use the same dll in WinForms, the program stuck.
Any idea why?
This is my code:
var options = new PublicClientApplicationOptions {
ClientId = clientId,
TenantId = tenantId,
RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient",
};
if (application == null) {
application = PublicClientApplicationBuilder.CreateWithApplicationOptions(options).WithAuthority(AzureCloudInstance.AzurePublic, ClientSecretOrTenantId).Build();
}
string token = "";
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async(requestMessage) =>{
token = await GetToken();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}));
Recipient recipient = new Recipient();
recipient.EmailAddress = new EmailAddress();
recipient.EmailAddress.Address = toAddress;
List < Recipient > recipients = new List < Recipient > ();
recipients.Add(recipient);
var message = new Message {
Subject = subject,
Body = new ItemBody {
ContentType = isHtml ? BodyType.Html: BodyType.Text,
Content = bodyText,
},
ToRecipients = recipients,
};
try {
await graphServiceClient.Me.SendMail(message, false).Request().PostAsync(); // get stuck here
} catch(ServiceException) {
graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async(requestMessage) =>{
token = await GetToken();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}));
await graphServiceClient.Me.SendMail(message, false).Request().PostAsync();
}
I'd hazard a guess that you're trying to make the asynchronous method synchronous by calling SendEmailAsync(email).Wait() in your (button click?) event handler, which is causing a WinForms UI thread lock.
The solution is to mark your event handler as async void and await your method in the event handler code.
I'm developing an app in Nativescript for the first time and running into an issue where AJAX calls work on Android but not iOS. I have a login.js file which requires a user-view-model (user-view-model.js), and when I test the code on Android it takes me to the "home" page but it hits the catch function on iOS.
login.js:
var dialogsModule = require("ui/dialogs");
var UserViewModel = require("../../shared/view-models/user-view-model");
var applicationSettings = require("application-settings");
var user = new UserViewModel({
email: "aaa#aaa.com",
password: "aaa"
});
var frameModule = require("ui/frame");
var page;
exports.loaded = function(args) {
page = args.object;
page.bindingContext = user;
};
exports.login = function () {
user.login().catch(function(error) {
dialogsModule.alert({
message: "Unfortunately we could not find your account.",
okButtonText: "OK"
});
return Promise.reject();
}).then(function(response) {
console.dir(response)
console.log("past response")
applicationSettings.setString("user_id", response.user_id);
applicationSettings.setString("first_name", response.first_name);
applicationSettings.setString("last_name", response.last_name);
applicationSettings.setString("user_type", response.user_type);
var topmost = frameModule.topmost();
topmost.navigate("views/home/home");
});
};
user-view-model.js:
var config = require("../../shared/config");
var fetchModule = require("fetch");
var observableModule = require("data/observable");
var http = require("http");
function User(info) {
info = info || {};
var viewModel = new observableModule.fromObject({
email: info.email || "",
password: info.password || ""
});
viewModel.login = function() {
let loginEmail = JSON.stringify(this.get("email")).replace(/['"]+/g, '');
let loginPassword = JSON.stringify(this.get("password")).replace(/['"]+/g, '');
console.log(loginEmail, loginPassword);
let loginUrl = config.serverPHPServiceUrl + "Login.php?user_id=" + loginEmail + "&password=" + loginPassword;
console.log(loginUrl);
// I tried this way first and wasn't able to login on iOS, which made me try the second method below.
// return fetchModule.fetch(loginUrl, {
// method: "POST",
// headers: {
// "Content-Type": "application/json"
// }
// }).then(handleErrors).then(function(response) {
// return response.json();
// }).then(function(data) {
// console.dir(data);
// console.log(data["results"][0]["user_id"])
// return data["results"][0];
// });
// This method works on Android but not iOS.
return http.getJSON(loginUrl).then(function(response) {
console.dir(response);
return response.results[0];
})
};
return viewModel;
};
function handleErrors(response) {
console.log("in errors")
if (!response.ok) {
console.log(JSON.stringify(response));
throw Error(response.statusText);
}
return response;
}
module.exports = User;
Is there anything fundamentally wrong with my code, or do asynchronous calls work differently on iOS vs Android in Nativescript? I did the Grocery tutorial and didn't run into this issue, so I didn't think this was the case. Does it matter that the backend is using PHP?
I fixed my issue: I started a new project with Angular 2 and ran into the same error, but then it gave me the error message "Error: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." I solved it by adding "https" to my url call, but this post has another solution.
have the following which works on Win10 phone in a pcl.
But i cannot get the same code to return OK on samsung s7 with android 7.0
project is xamarin forms.
nuget for system.net.http is 2.2.29.
I've include the same nuget in my UWP for the win10 phone and android projects.
i've also changed the user to include be "domain\user", "domain#user", "user#domain"
var httpClientHandler = new System.Net.Http.HttpClientHandler()
{
Credentials = credentials.GetCredential(new Uri(location), "NTLM")
};
I've tried and alternative to setting the httpClientHandler.Credentials.
var credentials = new NetworkCredentials("user", "pass", "domain");
var location = "http://apps.mysite.com/api#/doit";
var httpClientHandler = new HttpClientHandler{
Credentials = credentials
}
using (var httpClient = new HttpClient(httpClientHandler, true))
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
try
{
var httpResponseMessage = await httpClient.GetAsync(location);
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
{
//handle error
}
else
{
//do something
}
}
catch (Exception)
{}
finally
{}
}
another strange thing. when i run this on android, the code hits the await httpclient.getasync(location);
and the immediately jumps to the finally.
I hava a simple form with username & password Entry Fields, plus an OK button.
all three controls are bound to a viewmodel. the OK button via an ICommand.
this code and the view live in the PCL. which has a reference to Microsoft.Net.Http.
I have Android and Universal Windows Xamarin forms builds that consume the PCL.
Android Properties. Default httpClient, SSL/TLS Default. supported arch armeabi, armeabi-v7a;x86
Android Manifest: Camera, flashlight and internet
private bool calcEnabled = false;
private ICommand okCommand;
private string message = string.Empty;
private string validatingMessage = "Validating!";
private string unauthorizedMessage = "Invalid Credentials!";
private string authenticatedMessage = "Validated";
private bool validating = false;
public ICommand OkCommand => okCommand ?? (okCommand = new Command<object>((o) => clicked(o), (o) => CalcEnabled));
protected async void clicked(object state)
{
try
{
Validating = true;
Message = validatingMessage;
var credentials = new
System.Net.NetworkCredential(Helpers.Settings.UserName, Helpers.Settings.Password, "www.domain.com");
var location = "http://apps.wwwoodproducts.com/wwlocator#/information";
var httpClientHandler = new System.Net.Http.HttpClientHandler()
{
Credentials = credentials.GetCredential(new Uri(location), "NTLM") };
using (var httpClient = new System.Net.Http.HttpClient(httpClientHandler))
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
try
{
var httpResponseMessage = await
httpClient.GetAsync(location);
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
{
Message = unauthorizedMessage;
}
else
{
Message = authenticatedMessage;
Messenger.Default.Send<bool>(true);
}
}
catch (Exception)
{
Message = unauthorizedMessage;
}
finally
{
Validating = false;
}
}
}
catch (Exception)
{
throw;
}
}
I am currently using Xamarin.Auth on a iOS project to handle some user authentication via Facebook and Twitter in my application. The Facebook authentication using OAuth2Authenticator works great and my implementation was based mainly off the docs (http://components.xamarin.com/gettingstarted/xamarin.auth). Twitter however still uses OAuth1 it seems and thus I based my implementation mainly off the answer in this StackOverflow questions (https://stackoverflow.com/a/21982205). Everything works properly and I am able to retrieve user, tweets, etc. but after all the code executes I receive a "Authentication Error" popup on the screen saying "Object reference not set to an instance of an object." there is nothing printed to the console however as is the case with most normal errors I have seen thus far. I can dismiss the popup and everything continues to preform correctly. I believe I have narrowed the problem down to something within the OAuth1Authenticator request as I still receive the error when all of the other handling code has been commented out. Please reference the code below to see what might be the cause of this.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
signupBtn.TouchUpInside += delegate {
LoginToTwitter(true, this);
};
}
void LoginToTwitter(bool allowCancel, UIViewController _vc)
{
var auth = new OAuth1Authenticator (
consumerKey: "My Consumer Key",
consumerSecret: "My Consumer Secret",
requestTokenUrl: new Uri("https://api.twitter.com/oauth/request_token"),
authorizeUrl: new Uri("https://api.twitter.com/oauth/authorize"),
accessTokenUrl: new Uri("https://api.twitter.com/oauth/access_token"),
callbackUrl: new Uri("My callback url"),
getUsernameAsync: (IDictionary<string, string> accountProperties) => {
string screen_name = "";
if (accountProperties.TryGetValue("screen_name", out screen_name)) {
Console.WriteLine("SN: {0}", screen_name);
Account a = new Account(screen_name, accountProperties);
AuthenticatorCompletedEventArgs e = new AuthenticatorCompletedEventArgs(a);
TwitterCompleted(e, _vc);
}
return null;}
);
auth.AllowCancel = allowCancel;
UIViewController authView = auth.GetUI ();
_vc.PresentViewController (authView, true, null);
}
void TwitterCompleted (AuthenticatorCompletedEventArgs e, UIViewController _vc)
{
var theAccount = e.Account;
var theProperties = theAccount.Properties;
foreach (var item in theProperties) {
Console.WriteLine (item); //debugging
}
InvokeOnMainThread (delegate {
_vc.DismissViewController (true, null);
});
AccountStore.Create ().Save (e.Account, "Twitter");
if (!e.IsAuthenticated) {
Console.WriteLine("Not authorized");
return;
}
theScreenName = e.Account.Properties["screen_name"];
theCount = "2";
IDictionary<string, string> theDict = new Dictionary<string, string>();;
theDict.Add("screen_name", theScreenName);
theDict.Add("count", theCount);
var request = new OAuth1Request("GET", new Uri("https://api.twitter.com/1.1/statuses/user_timeline.json"), theDict, e.Account, false);
request.GetResponseAsync().ContinueWith (t => {
if (t.IsFaulted)
Console.WriteLine("Error: {0}", t.Exception.InnerException.Message);
else if (t.IsCanceled)
Console.WriteLine("Canceled");
else
{
var obj = JsonValue.Parse (t.Result.GetResponseText());
Console.WriteLine("object: {0}", obj); // debugging
}
}, uiScheduler);
return;
}
private readonly TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
instead of returning null in "getUsernameAsync" return Task
I am trying to figure out how to use the AWS .NET SDK to confirm a subscription to a SNS Topic.
The subscription is via HTTP
The endpoint will be in a .net mvc website.
I can't find any .net examples anywhere?
A working example would be fantastic.
I'm trying something like this
Dim snsclient As New Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(ConfigurationSettings.AppSettings("AWSAccessKey"), ConfigurationSettings.AppSettings("AWSSecretKey"))
Dim TopicArn As String = "arn:aws:sns:us-east-1:991924819628:post-delivery"
If Request.Headers("x-amz-sns-message-type") = "SubscriptionConfirmation" Then
Request.InputStream.Seek(0, 0)
Dim reader As New System.IO.StreamReader(Request.InputStream)
Dim inputString As String = reader.ReadToEnd()
Dim jsSerializer As New System.Web.Script.Serialization.JavaScriptSerializer
Dim message As Dictionary(Of String, String) = jsSerializer.Deserialize(Of Dictionary(Of String, String))(inputString)
snsclient.ConfirmSubscription(New Amazon.SimpleNotificationService.Model.ConfirmSubscriptionRequest With {.AuthenticateOnUnsubscribe = False, .Token = message("Token"), .TopicArn = TopicArn})
End If
Here is a working example using MVC WebApi 2 and the latest AWS .NET SDK.
var jsonData = Request.Content.ReadAsStringAsync().Result;
var snsMessage = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
//verify the signaure using AWS method
if(!snsMessage.IsMessageSignatureValid())
throw new Exception("Invalid signature");
if(snsMessage.Type == Amazon.SimpleNotificationService.Util.Message.MESSAGE_TYPE_SUBSCRIPTION_CONFIRMATION)
{
var subscribeUrl = snsMessage.SubscribeURL;
var webClient = new WebClient();
webClient.DownloadString(subscribeUrl);
return "Successfully subscribed to: " + subscribeUrl;
}
Building on #Craig's answer above (which helped me greatly), the below is an ASP.NET MVC WebAPI controller for consuming and auto-subscribing to SNS topics. #WebHooksFTW
using RestSharp;
using System;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Description;
namespace sb.web.Controllers.api {
[System.Web.Mvc.HandleError]
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class SnsController : ApiController {
private static string className = MethodBase.GetCurrentMethod().DeclaringType.Name;
[HttpPost]
public HttpResponseMessage Post(string id = "") {
try {
var jsonData = Request.Content.ReadAsStringAsync().Result;
var sm = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
//LogIt.D(jsonData);
//LogIt.D(sm);
if (!string.IsNullOrEmpty(sm.SubscribeURL)) {
var uri = new Uri(sm.SubscribeURL);
var baseUrl = uri.GetLeftPart(System.UriPartial.Authority);
var resource = sm.SubscribeURL.Replace(baseUrl, "");
var response = new RestClient {
BaseUrl = new Uri(baseUrl),
}.Execute(new RestRequest {
Resource = resource,
Method = Method.GET,
RequestFormat = RestSharp.DataFormat.Xml
});
if (response.StatusCode != System.Net.HttpStatusCode.OK) {
//LogIt.W(response.StatusCode);
} else {
//LogIt.I(response.Content);
}
}
//read for topic: sm.TopicArn
//read for data: dynamic json = JObject.Parse(sm.MessageText);
//extract value: var s3OrigUrlSnippet = json.input.key.Value as string;
//do stuff
return Request.CreateResponse(HttpStatusCode.OK, new { });
} catch (Exception ex) {
//LogIt.E(ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError, new { status = "unexpected error" });
}
}
}
}
I don't know how recently this has changed, but I've found that AWS SNS now provides a very simply method for subscribing that doesn't involve extracting urls or building requests using RESTSharp.....Here's the simplified WebApi POST method:
[HttpPost]
public HttpResponseMessage Post(string id = "")
{
try
{
var jsonData = Request.Content.ReadAsStringAsync().Result;
var sm = Amazon.SimpleNotificationService.Util.Message.ParseMessage(jsonData);
if (sm.IsSubscriptionType)
{
sm.SubscribeToTopic(); // CONFIRM THE SUBSCRIPTION
}
if (sm.IsNotificationType) // PROCESS NOTIFICATIONS
{
//read for topic: sm.TopicArn
//read for data: dynamic json = JObject.Parse(sm.MessageText);
//extract value: var s3OrigUrlSnippet = json.input.key.Value as string;
}
//do stuff
return Request.CreateResponse(HttpStatusCode.OK, new { });
}
catch (Exception ex)
{
//LogIt.E(ex);
return Request.CreateResponse(HttpStatusCode.InternalServerError, new { status = "unexpected error" });
}
}
The following example helped me work with SNS. It goes through all the steps to work with Topics. The subscribe request in this case is an email address, however that can be changed to HTTP.
Pavel's SNS Example
Documentation
I ended up getting it working using the code shown. I was having trouble capturing the exception on the development server which turned out was telling me the server's time didn't match the timestamp in the SNS message.
Once the server's time was fixed up (an Amazon server BTW), the confirmation worked.