TaskCanceledException on Refit call - xamarin

I am implementing a simple login screen with MvvmCross Forms and Refit.
The start of the authentication task is started by pressing the button that has the command 'LogInCommand'
<Button x:Name="loginButton" Text = "Login" HorizontalOptions = "Center"
VerticalOptions = "StartAndExpand" FontSize="24" Command="{ Binding LogInCommand }"/>
This will execute the following command in the AuthenticationViewModel:
#region commands
async Task LogInTaskAsync()
{
var errors = _validator.Validate(this);
if (!errors.IsValid)
{
_toastService.DisplayErrors(errors); //Display errors here.
}
else
{
using (new Busy(this))
{
try
{
Status = AuthenticationStatusEnum.LOADING;
// get access token
string accessToken = await _authenticationService.LogIn(_email, _password);
Debug.WriteLine(String.Format("Access Token:t {0} ", accessToken));
// save token on preferences.
Settings.AccessToken = accessToken;
Status = AuthenticationStatusEnum.LOGIN_SUCESS;
}
catch (TaskCanceledException ex) {
Debug.WriteLine("Timeout Operation ...");
}
catch (Exception ex)
{
MessagingCenter.Send(new object(), EventTypeName.EXCEPTION_OCCURRED, ex);
Status = AuthenticationStatusEnum.LOGIN_FAILED;
}
}
}
}
IMvxCommand _logInCommand;
public IMvxCommand LogInCommand =>
_logInCommand ?? (_logInCommand = new MvxCommand(async () => await LogInTaskAsync()));
An instance of the AuthenticationService that is working with refit is injected into the view model.
async public Task<string> LogIn(string email, string password)
{
Debug.WriteLine(String.Format("Login with {0}/{1}", email, password));
return await _authenticationRestService.getAuthorizationToken(new JwtAuthenticationRequestDTO()
{
Email = email,
Password = password
}).ContinueWith(t => t.Result.Data.Token, TaskContinuationOptions.OnlyOnRanToCompletion);
}
The Refit service specification is as follows:
[Headers("Accept: application/json")]
public interface IAuthenticationRestService
{
[Post("/")]
Task<APIResponse<JwtAuthenticationResponseDTO>> getAuthorizationToken([Body] JwtAuthenticationRequestDTO authorizationRequest);
}
The problem is that the task is always canceled, the TaskCanceledException exception is always thrown.
I also expose CoreApp where I configure context instances.
public class CoreApp : MvvmCross.Core.ViewModels.MvxApplication
{
public override void Initialize()
{
var httpClient = new HttpClient(new HttpLoggingHandler())
{
BaseAddress = new Uri(SharedConfig.BASE_API_URL),
Timeout = TimeSpan.FromMinutes(SharedConfig.TIMEOUT_OPERATION_MINUTES)
};
// Register REST services
Mvx.RegisterSingleton<IAuthenticationRestService>(() => RestServiceFactory.getService<IAuthenticationRestService>(httpClient));
Mvx.RegisterSingleton<IParentsRestService>(() => RestServiceFactory.getService<IParentsRestService>(httpClient));
Mvx.RegisterSingleton<IChildrenRestService>(() => RestServiceFactory.getService<IChildrenRestService>(httpClient));
CreatableTypes()
.InNamespace("Bullytect.Core.Services")
.EndingWith("ServiceImpl")
.AsInterfaces()
.RegisterAsLazySingleton();
Mapper.Initialize(cfg => {
cfg.CreateMap<ParentDTO, ParentEntity>();
cfg.CreateMap<SonDTO, SonEntity>();
});
Mvx.RegisterType<IValidator, Validator>();
RegisterAppStart(new CustomAppStart());
}
}
Someone knows how I can fix this. Thanks in advance!!

Related

Invalid state from server. Possible forgery! error in Xamarin.Auth

Why I get this error message when trying to use the Xamarin.Auth Api?
I am running on Android Plataform and using Xamarin.Forms
OAuth2Authenticator auth = new OAuth2Authenticator
(
clientId: AppKeyDropboxtoken,
scope: "",
authorizeUrl: new Uri("https://www.dropbox.com/oauth2/authorize"),
redirectUrl: new Uri(RedirectUri),
isUsingNativeUI: false
);
auth.Completed += (sender, eventArgs) =>
{
if (eventArgs.IsAuthenticated)
{
// Use eventArgs.Account to do wonderful things
this.AccessToken = eventArgs.Account.Properties["access_token"].ToString();
Debug.WriteLine("AccessToken: " + this.AccessToken);
openDropboxFileList();
}
};
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(auth);
Create a class and add this code below:
public class AuthenticatorExtensions : OAuth2Authenticator
{
public AuthenticatorExtensions(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null, bool isUsingNativeUI = false) : base(clientId, clientSecret, scope, authorizeUrl, redirectUrl, accessTokenUrl, getUsernameAsync, isUsingNativeUI)
{
}
protected override void OnPageEncountered(Uri url, System.Collections.Generic.IDictionary<string, string> query, System.Collections.Generic.IDictionary<string, string> fragment)
{
// Remove state from dictionaries.
// We are ignoring request state forgery status
// as we're hitting an ASP.NET service which forwards
// to a third-party OAuth service itself
if (query.ContainsKey("state"))
{
query.Remove("state");
}
if (fragment.ContainsKey("state"))
{
fragment.Remove("state");
}
base.OnPageEncountered(url, query, fragment);
}
}
Then use it as below:
[Obsolete]
private void SignInGoogleAuth()
{
try
{
string clientId = null;
string redirectUri = null;
//Xamarin.Auth.CustomTabsConfiguration.CustomTabsClosingMessage = null;
clientId = Constants.GoogleAndroidClientId;
redirectUri = Constants.GoogleAndroidRedirectUrl;
account = store.FindAccountsForService(Constants.AppName).FirstOrDefault();
var authenticator = new AuthenticatorExtensions(
clientId,
null,
Constants.GoogleScope,
new Uri(Constants.GoogleAuthorizeUrl),
new Uri(redirectUri),
new Uri(Constants.GoogleAccessTokenUrl),
null,
true);
authenticator.Completed += OnAuthCompleted;
authenticator.Error += OnAuthError;
AuthenticationState.Authenticator = authenticator;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(authenticator);
}
catch (Exception ex)
{
ShowAlert("Alert", ex.Message);
}
}
[Obsolete]
async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthCompleted;
authenticator.Error -= OnAuthError;
}
if (e.IsAuthenticated)
{
// If the user is authenticated, request their basic user data from Google
// UserInfoUrl = https://www.googleapis.com/oauth2/v2/userinfo
var request = new OAuth2Request("GET", new Uri(Constants.GoogleUserInfoUrl), null, e.Account);
var response = await request.GetResponseAsync();
if (response != null)
{
// Deserialize the data and store it in the account store
// The users email address will be used to identify data in SimpleDB
string userJson = await response.GetResponseTextAsync();
StaticVariables.googleProfile = JsonConvert.DeserializeObject<GoogleProfile>(userJson);
}
if (account != null)
{
store.Delete(account, Constants.AppName);
}
await store.SaveAsync(account = e.Account, Constants.AppName);
Application.Current.Properties.Remove("Id");
Application.Current.Properties.Remove("FirstName");
Application.Current.Properties.Remove("LastName");
Application.Current.Properties.Remove("DisplayName");
Application.Current.Properties.Remove("EmailAddress");
Application.Current.Properties.Remove("ProfilePicture");
Application.Current.Properties.Add("Id", StaticVariables.googleProfile.Id);
Application.Current.Properties.Add("FirstName", StaticVariables.googleProfile.GivenName);
Application.Current.Properties.Add("LastName", StaticVariables.googleProfile.FamilyName);
Application.Current.Properties.Add("DisplayName", StaticVariables.googleProfile.Name);
Application.Current.Properties.Add("EmailAddress", StaticVariables.googleProfile.Email);
Application.Current.Properties.Add("ProfilePicture", StaticVariables.googleProfile.Picture);
await Navigation.PushAsync(new GoogleProfilePage());
}
}
[Obsolete]
void OnAuthError(object sender, AuthenticatorErrorEventArgs e)
{
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthCompleted;
authenticator.Error -= OnAuthError;
}
Debug.WriteLine("Authentication error: " + e.Message);
}
I was getting the infamous "Possible Forgery!" error and overrode OnPageEncountered() to work around it as many have done. This turns out to be unnecessary as well as insecure.
Oauth2Authenticator is stateful so you will get this problem if you don't use the same instance of OAuth2Authenticator to invoke OnPageLoading() as was used to initiate the authentication.
To resolve, just save the instance of OAuth2Authenticator used for initiating authentication and then reuse it when calling OnPageLoading() in your OauthInterceptor.

How to call the Publisher Methods in ASPBoilerplate?

I am exploring the ASPBoilerplate framework and what interests me more is its real time notification capability. However, I am having this error.
Here is my Notification publisher class:
public class Publisher : Hub, ITransientDependency
{
private readonly INotificationPublisher _notificationPublisher;
public Publisher(INotificationPublisher notificationPublisher)
{
_notificationPublisher = notificationPublisher;
}
//Send a general notification to all subscribed users in current tenant (tenant in the session)
public async Task Publish_Announcement(string announcementMessage)
{
//Example "LowDiskWarningMessage" content for English -> "Attention! Only {remainingDiskInMb} MBs left on the disk!"
var data = new MessageNotificationData(announcementMessage);
await _notificationPublisher.PublishAsync("abp.notifications.received", data, severity: NotificationSeverity.Info);
}
}
And I am testing if it will notify all online users whenever a new user was created.
public override async Task<UserDto> Create(CreateUserDto input)
{
CheckCreatePermission();
var user = ObjectMapper.Map<User>(input);
user.TenantId = AbpSession.TenantId;
user.IsEmailConfirmed = true;
await _userManager.InitializeOptionsAsync(AbpSession.TenantId);
CheckErrors(await _userManager.CreateAsync(user, input.Password));
if (input.RoleNames != null)
{
CheckErrors(await _userManager.SetRoles(user, input.RoleNames));
}
CurrentUnitOfWork.SaveChanges();
//I cannot call the publisher class since it has dependencies in its contructor.
new Publisher().Publish_Announcement("Hi");
return MapToEntityDto(user);
}
Or am I just doing it wrong?
Please help. Thanks!
Basically, the Notifications is based on Publisher / Subsriber pattern and while the notifications are published through the system, the users who have subscribed to that notification will receive it.
Publish Notifications
public class MyService : ITransientDependency
{
private readonly INotificationPublisher _notificationPublisher;
public MyService(INotificationPublisher notificationPublisher)
{
_notificationPublisher = notificationPublisher;
}
//Send a general notification to a specific user
public async Task Publish_SentFriendshipRequest(string senderUserName, string friendshipMessage, UserIdentifier targetUserId)
{
await _notificationPublisher.PublishAsync("SentFriendshipRequest", new SentFriendshipRequestNotificationData(senderUserName, friendshipMessage), userIds: new[] { targetUserId });
}
//Send an entity notification to a specific user
public async Task Publish_CommentPhoto(string commenterUserName, string comment, Guid photoId, UserIdentifier photoOwnerUserId)
{
await _notificationPublisher.PublishAsync("CommentPhoto", new CommentPhotoNotificationData(commenterUserName, comment), new EntityIdentifier(typeof(Photo), photoId), userIds: new[] { photoOwnerUserId });
}
//Send a general notification to all subscribed users in current tenant (tenant in the session)
public async Task Publish_LowDisk(int remainingDiskInMb)
{
//Example "LowDiskWarningMessage" content for English -> "Attention! Only {remainingDiskInMb} MBs left on the disk!"
var data = new LocalizableMessageNotificationData(new LocalizableString("LowDiskWarningMessage", "MyLocalizationSourceName"));
data["remainingDiskInMb"] = remainingDiskInMb;
await _notificationPublisher.PublishAsync("System.LowDisk", data, severity: NotificationSeverity.Warn);
}
}
Subscribe to Notifications
public class MyService : ITransientDependency
{
private readonly INotificationSubscriptionManager _notificationSubscriptionManager;
public MyService(INotificationSubscriptionManager notificationSubscriptionManager)
{
_notificationSubscriptionManager = notificationSubscriptionManager;
}
//Subscribe to a general notification
public async Task Subscribe_SentFriendshipRequest(int? tenantId, long userId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(tenantId, userId), "SentFriendshipRequest");
}
//Subscribe to an entity notification
public async Task Subscribe_CommentPhoto(int? tenantId, long userId, Guid photoId)
{
await _notificationSubscriptionManager.SubscribeAsync(new UserIdentifier(tenantId, userId), "CommentPhoto", new EntityIdentifier(typeof(Photo), photoId));
}
}
Client-Side
This is done by signalr and you need to capture the event once it's triggered.
abp.event.on('abp.notifications.received', function (userNotification) {
if (userNotification.notification.data.type === 'Abp.Notifications.LocalizableMessageNotificationData') {
var localizedText = abp.localization.localize(
userNotification.notification.data.message.name,
userNotification.notification.data.message.sourceName
);
$.each(userNotification.notification.data.properties, function (key, value) {
localizedText = localizedText.replace('{' + key + '}', value);
});
alert('New localized notification: ' + localizedText);
} else if (userNotification.notification.data.type === 'Abp.Notifications.MessageNotificationData') {
alert('New simple notification: ' + userNotification.notification.data.message);
}
});
You can find more information here

Exception of type 'System.Collections.Generic.KeyNotFoundException' was thrown ? in Xamarin.Forms

i wanna use simple database in Xamarin Forms. So i used this code;
public partial class DBExample : ContentPage
{
string _dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),"myDB.db3");
public DBExample ()
{
InitializeComponent ();
}
private async void InsertButton(object sender, EventArgs e)
{
SQLiteConnection db=null;
try
{
db = new SQLiteConnection(_dbPath);
}
catch (Exception ex)
{
await DisplayAlert(null, ex.Message, "OK");
}
db.CreateTable<Users>();
var maxPk = db.Table<Users>().OrderByDescending(c => c.Id).FirstOrDefault();
Users users = new Users()
{
Id = (maxPk == null ? 1 : maxPk.Id + 1),
Name = userName.Text
};
db.Insert(users);
await DisplayAlert(null,userName.Text + "saved","OK");
await Navigation.PopAsync();
}
}
and i have problem. You can see it in Headtitle.
"Exception of type 'System.Collections.Generic.KeyNotFoundException' was thrown"
im waiting your support. Thanks for feedback.

Dialog stack doesn't reset

I have a scorable dialog that will check if the incoming message is equal to "resetconversation". When true, the dialog resets the dialog stack in the PostAsync method.
When the postasync method is hit, the dialog stack resets as expected but when I again send a message the stack reappears and the conversation proceeds as if it was never reset.
[note]: I have noticed that this issue happens when a PromptDialog.Choice() is expecting an input.
Below is the scorable registration and the scorable dialog class.
Thanks!
Registering my scorable dialog:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
string conn = ConfigurationManager.ConnectionStrings["BotDataContextConnectionString"].ConnectionString;
var store = new SqlBotDataStore(conn);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
//Scorable to check if dialog stack reset has been requested
builder.RegisterType<HandleResetRequestScorable>()
.AsSelf()
.As<IScorable<IActivity, double>>();
});
Scorable Dialog:
[Serializable]
public class HandleResetRequestScorable : ScorableBase<IActivity, bool, double>
{
private readonly IBotToUser _botToUser;
private readonly IDialogStack _dialogStack;
private bool _isMessageType { get; set; }
public HandleResetRequestScorable(IBotToUser botToUser, IDialogStack dialogTask)
{
SetField.NotNull(out _botToUser, nameof(_botToUser), botToUser);
//SetField.NotNull(out _dialogStack, nameof(_dialogStack), dialogTask);
}
protected override async Task<bool> PrepareAsync(IActivity activity, CancellationToken token)
{
string message = string.Empty;
if (activity.Type == ActivityTypes.Event)
{
_isMessageType = false;
message = activity.AsEventActivity().Name.ToLower();
}
else
{
_isMessageType = true;
message = activity.AsMessageActivity().Text.ToLower();
}
if (message == "resetconversation")
return true;
return false;
}
protected override bool HasScore(IActivity item, bool state)
{
return state;
}
protected override double GetScore(IActivity item, bool state)
{
return state ? 1.0 : 0;
}
protected override async Task PostAsync(IActivity activity, bool state, CancellationToken token)
{
try
{
var message = activity as IMessageActivity;
string[] data = message.Value.ToString().Split('|');
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity as Activity))
{
//get the conversation related data
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(default(CancellationToken));
//get the dialog stack
var convoStack = scope.Resolve<IDialogStack>();
convoStack.Reset();
if (_isMessageType)
{
await _botToUser.PostAsync("Conversation reset complete.");
await _botToUser.PostAsync("Hi");
}
await botData.FlushAsync(default(CancellationToken));
}
}
catch
{
throw;
}
}
protected override Task DoneAsync(IActivity item, bool state, CancellationToken token)
{
//throw new NotImplementedException();
return Task.CompletedTask;
}
}
}

Xamarin http webservice issue

I m trying to use http request webservice issue is that when we post wrong username and password the login service generate exception and it can't return any value in async calls.
A code snippet would help assist with the problem ...
However using a try catch should help you catch your exception and prevent application from crashing and handling the exceptions accordingly.
As seen in my sample code below I cater for the incorrect details entered / connectivity problems. I peform the http async request then parse the xml to my model handling the exceptions accordingly
var response = await WebRequestHelper.MakeAsyncRequest(url, content);
if (response.IsSuccessStatusCode == true)
{
Debug.WriteLine("Login Successfull" + "result.IsSuccessStatusCode" + response.IsSuccessStatusCode);
var result = response.Content.ReadAsStringAsync().Result;
result = result.Replace("<xml>", "<LoginResult>").Replace("</xml>", "</LoginResult>");
loginResult = XMLHelper.FromXml<LoginResult>(result);
if (loginResult != null)
{
login.Type = ResultType.OK;
login.Result = loginResult;
}
else
{
login.Type = ResultType.WrongDetails;
}
}
else
{
Debug.WriteLine("Login Failed" + "result.IsSuccessStatusCode" + response.IsSuccessStatusCode);
login.Type = ResultType.WrongDetails;
}
}
catch (Exception ex)
{
login.Type = ResultType.ConnectivityProblem;
}
Web Request
public static async Task<HttpResponseMessage> MakeAsyncRequest(string url, Dictionary<string, string> content)
{
var httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(0, 5, 0);
httpClient.BaseAddress = new Uri(url);
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type: application/x-www-form-urlencoded", "application/json");
if (content == null)
{
content = new Dictionary<string, string>();
}
var encodedContent = new FormUrlEncodedContent(content);
var result = await httpClient.PostAsync(httpClient.BaseAddress, encodedContent);
return result;
I would recommend wrapping the response in a generic ServiceResponse where you can store the exceptions. await methods can be included in try/catch blocks so the standard process can be followed.
E.G.
public async Task<ServiceResponse<T>> PostAsync<T>(String address, object dto){
var content = Serializer.SerializeObject (dto);
var response = await client.PostAsync (
address,
new StringContent (content));
if (response.IsSuccessStatusCode) {
try {
var responseString = await response.Content.ReadAsStringAsync ();
return new ServiceResponse<T> (Serializer.DeserializeObject<T> (responseString),
response.StatusCode);
} catch (Exception ex) {
return new ServiceResponse<T> (response.StatusCode, ex);
}
} else {
return new ServiceResponse<T> (response.StatusCode);
}
}
With the ServiceResponse defined as :
public class ServiceResponse<T>
{
public HttpStatusCode StatusCode { get; set;}
public T Value { get; set;}
public String Content { get; set;}
public Exception Error {get;set;}
public ServiceResponse(T value, HttpStatusCode httpStatusCode){
this.Value = value;
this.StatusCode = httpStatusCode;
}
public ServiceResponse(HttpStatusCode httpStatusCode, Exception error = null){
this.StatusCode = httpStatusCode;
this.Error = error;
}
}
This will give you a clean way of managing all your HTTP responses and any errors that may occur.

Resources