Azure Mobile Service - Windows Account Back Arrow gives a InvalidOperationException - azure

WAMS: Microsoft authentication.
http://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-windows-store-dotnet-get-started-users/
Changed from Facebook to MicrosoftAccount
PROBLEM: When I click on the back arrow (to escape the login) It should still be in the while loop and force another popup never allowing the user to have success. Instead it hit the
catch (InvalidOperationException)
private MobileServiceUser user;
private async System.Threading.Tasks.Task AuthenticateAsync()
{
while (user == null)
{
string message;
try
{
user = await App.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
message =
string.Format("You are now logged in - {0}", user.UserId);
}
catch (InvalidOperationException)
{
message = "You must log in. Login Required";
}
var dialog = new MessageDialog(message);
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
}
}

When you cancel the authentication page, the awaited call to LoginAsync will throw the InvalidOperationException. That's expected - you asked the SDK to login, the login operation didn't succeed, so you get an exception. When the exception is thrown, the assignment to the user field doesn't happen, so it retains its original value (null), which is why the loop continues. If you have a breakpoint in the catch block, and continue after hitting the breakpoint (F5), it should prompt with the authentication again.

Related

DOMException: The request is not allowed by the user agent or the platform in the current context

When attempting to access a file using the getFile() method of a FileSystemFileHandle, this error can occur: DOMException: The request is not allowed by the user agent or the platform in the current context.
This code works:
async function getTheFile() {
// open file picker
[fileHandle] = await window.showOpenFilePicker(pickerOpts);
// get file contents
const fileData = await fileHandle.getFile();
}
However if you store the fileHandle (using IndexedDB, for example) with the intent to persist access to the file even after a page refresh, it will not work. After refreshing the page, the getFile() method will throw the DOMException error.
What's going on?
When the page is refreshed (or all tabs for the app are closed) the browser no longer has permission to access the file.
There is a queryPermission() method available for the FileSystemHandle that can be used to determine whether the file can be accessed before attempting to read it.
Example from a helpful web.dev article:
async function verifyPermission(fileHandle, readWrite) {
const options = {};
if (readWrite) {
options.mode = 'readwrite';
}
// Check if permission was already granted. If so, return true.
if ((await fileHandle.queryPermission(options)) === 'granted') {
return true;
}
// Request permission. If the user grants permission, return true.
if ((await fileHandle.requestPermission(options)) === 'granted') {
return true;
}
// The user didn't grant permission, so return false.
return false;
}
This function will return true once the permission === granted or re-prompt the user for access if permission is either denied or prompt.
Note that you will have to grant permissions each time you retrieve a handle from indexedDB, serializing seems to lose the permission

How can I add user info to conv.user.storage?

I'm using Actions Builder to create my chatbot and after user logins using Google I want to save his ID to storage variable.
This storage variable doesn't exist on conv.user.
So I do this:
if (conv.user.verificationStatus === 'VERIFIED') {
conv.user.storage = {};
conv.user.storage.id = str.rows[0].id;
console.log("STORAGE");
console.log(conv.user.storage.id);
}
But on Google Assistant it returns the error message and on my Webhook it's all good (no errors shown):
Google Assistant Error
What can I do to save/persist at least my user ID for future referings?
Since user has the Google Sign In process done once, every time he enters in your action you have his info on the request (payload). It´s automatically added to user storage.
You should store it on conv.user.params and refer to it in your code.
You may have a get and set method to help you with:
getUserId(conv) {
return conv.user.params.userId;
}
setUserId(conv, userId) {
try {
conv.user.params.userId = userId;
} catch (e) {
throw new error("Error setting USERID");
}
return userId;
}

VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user XXXX1145-8bcb-48d6-b8a8-dcb75955b738

I am getting 'Invalid token' error while Email confirmation in .NET core.
Above error is thrown randomly. Clicking the confirmation link for the first time is throwing error and again if we click on the same link email is getting confirmed.
Web application is deployed in Azure. Also in logs we found the below error
VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user XXXX1145-8bcb-48d6-b8a8-dcb75955b738.
Email Confirmation method is below
public async Task<IActionResult> ConfirmEmail(string userid, string token)
{
ApplicationUser user = await _userManager.FindByIdAsync(userid);
if(user != null)
{
IdentityResult result = await _userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
return Redirect("/Thankyou?status=confirm");
}
else
{
return Redirect("/Thankyou?status=" + result.Errors.ToArray()[0].Description);
}
}
else
{
return Redirect("/Thankyou?status=Invalid User");
}
}
I had the same problem, I solved it by decoding my URL encoded tokens:
var decodedTokenString = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(token));
It looks like the user account is already confirmed. You can take a look at the value of User.EmailConfirmed.

Using 2FA for password reset

My application uses Asp.Net Identity and sends a Two Factor code to my Auth app on login. This is pretty standard (as there lots of examples on the net) and uses the SendCode() method. My understanding is that the 'magic' is done by this line:
// Generate the token and send it
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
View("Error");
}
My requirement is to ensure the user goes through the same process of 2FA when they want to change their password after they have logged in.
My issue is that when the code to send the 2FA code is executed:
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
View("Error");
}
I receive the error 'UserID not found':
Server Error in '/MSPortal' Application.
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: UserId not found.
Source Error:
Line 555:
Line 556: // Generate the token and send it
Line 557: if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
Line 558: {
Line 559: return View("Error");
I know SendTwoFactorCodeAsync() calls GetVerifiedUserIdAsync() but my understanding is that the user is verified now that I have already logged in using 2FA.
Does anyone know why I would be getting this error?
Thanks.
I've worked around this by overriding SendTwoFactorCodeAsync() in IdentityConfig.cs. In this override, I first call GetVerifiedUserIdAsync() as per usual but then if that is 0 I get the User's ID from the Current HttpContext.
I am not stating this is the best way but it's what I have done thus far and its got me moving ahead in my aim of having 2FA for login, change password and forgot password.
The code (likely to go through some refactoring if I get feedback) is:
public override async Task<bool> SendTwoFactorCodeAsync(string provider)
{
int userId = 0;
try
{
userId = await GetVerifiedUserIdAsync();
if (userId == 0)
{
userId = Convert.ToInt32(HttpContext.Current.User.Identity.GetUserId());
}
if (userId == 0)
return false;
}
catch
{
return false;
}
var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider);
// See IdentityConfig.cs to plug in Email/SMS services to actually send the code
await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token);
return true;
//return base.SendTwoFactorCodeAsync(provider);
}

Azure App Services (Mobile Apps) AAD authentication token refresh

I am trying to use Azure Active Directory to perform login functions on my uwp app. This happens successfully however I cannot get it to refresh the token when it expires and always receive the error "Refresh failed with a 403 Forbidden error. The refresh token was revoked or expired." and so I have to bring up the login window again. I am using the version 2.1.0 and the following code to authenticate:
private async Task<bool> AuthenticateAsync(bool forceRelogon = false)
{
//string message;
bool success = false;
// Use the PasswordVault to securely store and access credentials.
PasswordVault vault = new PasswordVault();
PasswordCredential credential = null;
//Set the Auth provider
MobileServiceAuthenticationProvider provider = MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory;
MobileServiceUser user = null;
try
{
// Try to get an existing credential from the vault.
var credentials = vault.FindAllByResource(provider.ToString());
credential = credentials.FirstOrDefault();
}
catch (Exception ex)
{
// When there is no matching resource an error occurs, which we ignore.
Debug.WriteLine(ex);
}
if (credential != null && !forceRelogon)
{
// Create a user from the stored credentials.
user = new MobileServiceUser(credential.UserName);
credential.RetrievePassword();
user.MobileServiceAuthenticationToken = credential.Password;
// Set the user from the stored credentials.
App.MobileService.CurrentUser = user;
//message = string.Format($"Cached credentials for user - {user.UserId}");
// Consider adding a check to determine if the token is
// expired, as shown in this post: http://aka.ms/jww5vp.
if (RedemptionApp.ExtensionMethods.TokenExtension.IsTokenExpired(App.MobileService))
{
try
{
await App.MobileService.RefreshUserAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
success = true;
}
else
{
try
{
// Login with the identity provider.
user = await App.MobileService
.LoginAsync(provider);
// Create and store the user credentials.
if (credential != null)
vault.Remove(credential);
credential = new PasswordCredential(provider.ToString(),
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
success = true;
//message = string.Format($"You are now logged in - {user.UserId}");
}
catch (MobileServiceInvalidOperationException)
{
//message = "You must log in. Login Required";
}
}
//var dialog = new MessageDialog(message);
//dialog.Commands.Add(new UICommand("OK"));
//await dialog.ShowAsync();
return success;
}
Can anyone see something wrong with what I am doing, or need to do anything within the AAD service provider?
You might be able to get more accurate information by taking a look at the server-side application logs. Token refresh failure details will be logged there automatically. More details on application logs can be found here: https://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/. I recommend setting the trace level to Informational or Verbose.
Also, if you haven't done this already, Azure AD requires a bit of extra configuration to enable refresh tokens. Specifically, you need to configure a "client secret" and enable the OpenID Connect hybrid flow. More details can be found in this blog post: https://cgillum.tech/2016/03/07/app-service-token-store/ (scroll down to the Refreshing Tokens section and see where it describes the process for AAD).
Besides what has been said about mobile app configuration, I can spot this.
You have:
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
It should be:
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
new Dictionary<string, string>() {{ "response_type", "code id_token" }});
Maybe this will help:
https://azure.microsoft.com/en-us/blog/mobile-apps-easy-authentication-refresh-token-support/

Resources