Getting azure mobile app – 412 precondition failed after first successful time

If like me, you’re getting 412 precondition failed on azure mobile app service on any updates (patch) your posting to the service, even though the update worked the first time….then most likely its because your updating the same item without fetching the new data.

i.e. say you have item User and you update the users first name, then without reloading the user object you try to update the last name….it wont work because the Version column will be out of date.

You need to reload the item from your local store before adding new changes to it.

Step by step guide Adding Custom Authentication to Azure mobile app service walkthrough

This post assumes you have already created a new Azure Mobile app service. Im also assuming you already have an existing database on Azure with AspUsers tables like so:

usertables

Steps:

  1. Open Azure portal and select app services then select your mobile app

selectappservice

2. Choose Authentication/Authorization and turn App Service Authentication on.  In this post we’re only looking at custom authentication so in the Action to take dropdown select Allow requst no action. If you will have additional external auth then you may select that. Hit save changes.

turnonauth

3. OK now we need to edit our server code, if you’ve not already done so download the quick start project. For our example we’ll be looking at C# not Nodejs

quickstart

4. Open the project in Visual Studio. As we’re going to be using Asp.net Identity we need to add these Nuget packages as well as EntityFramework Identity. Make sure you have all of the below if not add them in:

packagaes

5. Under the App_Start folder modify Startup.MobileApp.cs and add the following lines to intiate Owin identity:

 public static void ConfigureMobileApp(IAppBuilder app)
        {
            //added to enable authentication
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

...

After the HttpConfiguration config = new HttpConfiguration(); add the following route. This will be used by your app later when we select “Custom” auth the app will call the below route on your service.

config.Routes.MapHttpRoute("custom", ".auth/login/custom", new { controller = "Auth" });

As Im using existing database for this Im also going to comment our the database seed intialization and uncomment the intialize with null line:

 // Use Entity Framework Code First to create database tables based on your DbContext
           // Database.SetInitializer(new siteauditsappInitializer());//not used as dont want code first to overwrite database

            // To prevent Entity Framework from modifying your database schema, use a null database initializer
            Database.SetInitializer<siteauditsappContext>(null);

6. Then add IdentityConfig.cs to the App_Start folder. Im not going to go into the code for this as you should ready have it, if not you should be able to get it from them Asp.net MVC project template when select IndivualAccounts for authentication.

7. Under Controllers folder create a new WebApi Controller called AuthController.cs with below code:

 public class AuthController : ApiController
    {

        public class LoginResult
        {
            public string authenticationToken { get; set; }
            public LoginResultUser user { get; set; }
        }

        public class LoginResultUser
        {
            public string userId { get; set; }
        }

        private ApplicationUserManager _userManager;
        private ApplicationSignInManager _signInManager;

        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }

        public ApplicationSignInManager SignInManager
        {
            get
            {
                return _signInManager ?? HttpContext.Current.GetOwinContext().Get<ApplicationSignInManager>();
            }
            private set
            {
                _signInManager = value;
            }
        }



        public IHttpActionResult Post([FromBody] JObject assertion)
        {
            
            bool passValid = IsPasswordValid(assertion).Result;
            if (passValid) // user-defined function, checks against a database
            {

                var signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY"); //This will be provided by Azure Mobile service when we publish our project
                
                var audience = "https://your app here.azurewebsites.net/"; // audience must match the url of the site
                var issuer = "https://your app here.azurewebsites.net/"; // audience must match the url of the site

                string username = assertion.GetValue("email").Value<string>();

                JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, username) },
                    signingKey,
                    audience,
                    issuer,
                    TimeSpan.FromHours(24));
                return Ok(new LoginResult()
                {
                    authenticationToken = token.RawData,
                    user = new LoginResultUser() { userId = username }
                });
            }
            else // user assertion was not valid
            {
                return Content(HttpStatusCode.Unauthorized, "Wrong credentials");
            }
        }

        private async Task<bool> IsPasswordValid(JObject assertion)
        {
            // this is where we would do checks agains a database
            try
            {
                string username = assertion.GetValue("email").ToString();
                string password = assertion.GetValue("password").ToString();
               
                
                var resultTask = SignInManager.PasswordSignInAsync(username, password, true, shouldLockout: true);
                var result = resultTask.Result;
                switch (result)
                {
                    case SignInStatus.Success:
                        return true;
                    case SignInStatus.LockedOut:
                        return false;
                    case SignInStatus.RequiresVerification:
                        return false;
                    case SignInStatus.Failure:
                    default:
                        return false;
                }

            }
            catch (Exception ex)
            {
                return false;
            }
        }
}

8. Add IdentityModel.cs to your Models folder:

using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;

namespace siteauditsappService.Models
{
    public class ApplicationUser : IdentityUser
    {

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        public string FirstName { get; set; }
        public string LastName { get; set; }

    }



    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        private const string connectionStringName = "Name=MS_TableConnectionString";

        public ApplicationDbContext()
            : base(connectionStringName, throwIfV1Schema: false)
        {
            Configuration.ProxyCreationEnabled = false;
            Configuration.LazyLoadingEnabled = false;
        }

        // protected override void OnModelCreating(DbModelBuilder modelBuilder)
        // {
        //     base.OnModelCreating(modelBuilder);
        //      modelBuilder.HasDefaultSchema("Application");//if users table has different scheme from dbo
        //   }


        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }


    }
}

In above code note the connection string should be for where you aspnet users database is. Store your connection string in web.config for dev but on Azure this will be in the Application Settings area. When you publish your project to Azure whatever connection string you have in  y ou web.config it will be replaced with the ones in Azure:

webconfig

9. Finally you should be ready to test this. Publish the project and then open you favourite Restful client. I google chrome extension Postman. In the client set body to json and do a post to the service with header Content-Type application/json and body like below:

postman

If credentials are correct you should get back token like so:

{
  "authenticationToken": "blablabbla",
  "user": {
    "userId": "myemail@hotmail.com"
  }
}

Finally add the

[Authorize]

attribute to all the table controllers that you want to secure.

 

Useful tip: What I found really useful was the ability to monitor issues when testing authentication using the Azure Log stream.

Next we need to modify our app to authenticate with this service. I show how to do this using Xamarin app.

 

Step by step – Creating an authenticated Azure mobile app with Xamarin walkthrough

In a previous post I showed how to setup to Azure mobile app backend C# service to use custom authenticated with Asp.Net Identity from an existing user database. If you’ve not done that yet please follow that post first.

Now that we have the back end ready for custom authentication we need to configure the app. In our example we’ll be modifying the quick starts Xamarin sample you get from Azure:

xamarin

You need to create a new login screen and set it is as default. Once done add username and password fields and a sign in button. In the button click event you need:

 
private MobileServiceClient client;
client = new MobileServiceClient(applicationURL);
var credentials = new JObject
			{
				["email"] = Helper.RetrieveString("Username"),
				["password"] = Helper.RetrieveString("Password")
			};
var user = await TodoItemManager.DefaultManager.CurrentClient.LoginAsync("custom", credentials);

The user field will now contain the token from the webservice. The Azure mobile service should will automatically add the token using X-Zumo-Auth headers to each call for data. You wont need to do anything.

Simples!

Add trace message diagnostics to Azure Log stream for Web and Mobile App Services

 

public class AzureApplicationLogTraceListener : TraceListener
    {
        private readonly string _logPath;
        private readonly object _lock = new object();

        public AzureApplicationLogTraceListener()
        {
            string instanceId = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID");
            if (instanceId != null)
            {
                string logFolder = Environment.ExpandEnvironmentVariables(@"%HOME%\LogFiles\application");
                Directory.CreateDirectory(logFolder);
                instanceId = instanceId.Substring(0, 6);
                _logPath = Path.Combine(logFolder, $"logs_{instanceId}.txt");

            }
        }

        public override void Write(string message)
        {
            if (_logPath != null)
            {
                lock (this)
                {
                    File.AppendAllText(_logPath, message);
                }
            }
        }

        public override void WriteLine(string message)
        {
            Write(message + Environment.NewLine);
        }
    }

I used this for diagnosing issues with Azure mobile app service authentication and it saved a lot of time!

Using an existing database with Azure mobile app service

Most samples for the azure mobile app server (on the new portal) are for start-from-scratch Todo item list….however what if you already have a database? Perhaps its used by a seperate Azure web app and now you want to add a native app to it.  This article will show the steps needed to make your database work with Azure Mobile Apps.

1) First step turn off EF database initialisation:
In your startup class comment out:

//Database.SetInitializer(new myappInitializer());

and add:

Database.SetInitializer<myappContext>(null);

This will stop entity framework from trying to overwrite data/structure in the database.

2) Modify the database:

Azure needs Id column on each table. If you primary key column has  a different name you will need to rename it. Also the datatype will need to be nvarchar.

3) Finally add in the models into your app service but leave out the Id column. This will be managed by Azure Mobile App service itself.

4) Add an InsertUpdateDelete trigger to each table that will be used by your TableControllers:

CREATE TRIGGER [dbo].[TR_dbo_TABLENAMEHERE_InsertUpdateDelete] ON [dbo].[TABLENAMEHERE] AFTER INSERT, UPDATE, DELETE AS BEGIN UPDATE [dbo].[TABLENAMEHERE] SET [dbo].[TABLENAMEHERE].[UpdatedAt] = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME()) FROM INSERTED WHERE inserted.[Id] = [dbo].[TABLENAMEHERE].[Id] END

All done!

Tutorial: Setup SharePoint 2016 on Azure

Follow this tutorial to setup a development environment for 2016 on Azure.  The setup is essentially the same as it was for 2013 with the exception of Min-Roles (we will discuss this later).  Assuming its the first time for you using Azure, the steps are:

  1. Setup virtual network and DNS
  2. Create a new Cloud Service
  3. Create three VMs
    1. Domain Controller
    2. SQL
    3. SharePoint
  4. Create new AD forest on DC VM and domain join the VMs
  5. Create farm, setup and services accounts and add to SQL server logins and add as Administrators on SharePoint VM
  6. Install SharePoint

 

Setup virtual network and DNS

Login to your Azure portal at manage.windowsazure.com then click Networks and New:

new network

Create a new virtual network with the following settings. As for Location I’ve selected West Europe, choose whatever works for you. Note we have selected to create a new DNS server, if you’ve already got one you can use that.

newvnet

Once created go to Configure and create 3 subnets for AD, SQL and SharePoint and press Save.

subnets

 

Create Cloud Service

For information on cloud services see link.

On the left hand navigation select Cloud Services > New > Quick  create.   Enter a unique url. Ive gone with isharepoint16.cloudapp.net

 

Create three VMs

Go to virtual machines section in azure portal and select New > From gallery.  Create the virtual machines with the following settings:

Domain controller:
OS: Windows Server 2012
VM name: Whatever you like, I chose DC1
Hardware: Standard tier, 2 cores 3.5 GB (A2)
Cloud service: Choose the one created earlier
Virtual network: Choose the one created earlier
Subnet: Choose the AD Subnet

server

SQL:
Machine: SQL Server 2014 SP1 Enterprise, Windows Server 2012 R2
VM name: Whatever you like, I chose sql1
Hardware: Standard tier, 4 cores 7 GB (A3)
Cloud service: Choose the one created earlier
Virtual network: Choose the one created earlier
Subnet: Choose the SQL Subnet

sqlserver

SharePoint 2016:
Machine: SharePoint Server 2016 IT Preview
VM name: Whatever you like, I chose sp1
Hardware: Standard tier, 4 cores  16 GB
Cloud service: Choose the one created earlier
Virtual network: Choose the one created earlier
Subnet: Choose the SharePoint Subnet

spserver

 

Create new AD forest on Domain Controller VM and domain join the VMs

Remote connect to the Domain controller VM, add AD DS features. After it installs promote create new forest and promote machine to domain controller.

Once done create AD accounts as SPFarm, SPSetup, SPServices

 

Add to SQL server logins

Connect to SQL server VM and open SQL management studio. Once open expand logins and add the 3 accounts above with Sysadmin role.

 

Install SharePoint

Connect to the SharePoint VM and add the 3 accounts as administrators on the machine.

Log off and log back in as the setup account.

Run the sharepoint configuration wizard just the same as you would for SP2013, with exception of Min-Roles. We will create single server farm with all the roles however in production environment you would need to select Custom and choose the roles that work for you.

Once complete you have SharePoint 2016 ready to go.  Access it using your cloud service url.