1. Training and Consulting: contact@winsmarts.com
Mobile devices and SharePoint
WWW.WINSMARTS.COM
@SAHILMALIK
FACEBOOK.COM/SAHILMALIK
LINKED.IN/SAHIL
Sahil Malik
3. Training and Consulting: contact@winsmarts.com
Before I waste your time ..
1. This talk is important. We will all be writing mobile apps in the future.
2. The first 10-15 minutes are generic – even if you are not a developer you will benefit from it.
3. Rest of the talk is 100% dev centric.
4. Concepts involved,
1. SharePoint Apps/Office365
2. ASP.NET MVC 5
3. Owin/Oauth
4. JavaScript and AngularJS
5. Swift/ObjectiveC
5. It is okay to not be an expert at all of these.
4. Training and Consulting: contact@winsmarts.com
Mobile Devices and SharePoint
Mobile Devices and Office 365
Mobile Devices and Microsoft
developers
Mobile Devices and .. Why just Microsoft?
7. Training and Consulting: contact@winsmarts.com
Challenges to solve
• Platform
• HTML5
• Hybrid
• Xamarin Native
• Platform Native
• Connectivity
• Enterprise data is on premises
• Mobile devices and Active Directory don’t mix easily (yet!)
• Security
• Enterprise apps
• Open App store apps
8. Training and Consulting: contact@winsmarts.com
Security
• OAuth is the way to go
• Claims is the only choice
• Token management is something you need to understand
• Never use embedded web views for authentication
• Common mistakes
• IT Administrators are trying to solve Mobile devices with the same
approach they used for windows active directory
• VPN
• Per App VPN is finally usable in limited scenarios with many downsides. Yet for
enterprise scenarios this is the choice
• End to End encryption
• Device Management for enterprise scenarios
9. Training and Consulting: contact@winsmarts.com
Connectivity
• Easy: Device to cloud
• iOS app talking to Office 365
• Direct http connectivity (well with a middle layer, I’ll explain in a moment why!)
• Tough: Device to on premises
• iOS app talking to on-prem SharePoint
• Azure Service Bus, NServiceBus, JBOSS FUSE etc.
• BiDirectional TCP channels
• Azure remote endpoints
• Per App VPN connections
• Important Best Practice: Design your app logic independent of the
communications layer
10. Training and Consulting: contact@winsmarts.com
Platform
• HTML 5
• Maximum reach
• Difficult to get UI 100% right, but UI can scale. Even iOS has multiple
device sizes now.
• No need to go through App Stores
• Native Device capabilities are poorer
• Offline experience is very hard to get right.
11. Training and Consulting: contact@winsmarts.com
Platform
• Hybrid
• The best choice in my opinion if you are targeting multiple platforms
• You do need to go through app stores
• You cannot update anything you wish at your whim (app stores may
block you)
• Needs lots of experience to get this right and be 100% native and
responsive
• iOS8 no longer throttles hosted safari
• What is possible with this?
• phonegap.com/app/ios/
• It still needs you to learn Swift/ObjectiveC/Java etc. Windows UI is
the toughest to get right.
12. Training and Consulting: contact@winsmarts.com
Platform
• Xamarin
• Promotes reuse of logic written in C#
• Not every .NET class is reusable.
• And, how much reusable logic do you have IN the app? Usually its REST services.
• Common scenarios such as offline scenarios are already solved.
• The project structure, builds, release management becomes complicated.
• ObjectiveC sucks, but Swift is not so bad!
• It’s a technical marvel. But how much value does it really have!?
• But, as a consultant, I need to learn Xamarin.
13. Training and Consulting: contact@winsmarts.com
Platform
• Native
• Best of everything, except you need to write your app for each platform.
• Swift and ObjectiveC can co-exist.
• Swift is like C# with an Apple personality (because of it’s retarded
ObjectiveC brother). Still its pretty good.
14. Training and Consulting: contact@winsmarts.com
C:>whoami
12xMVP
15xAuthor
Pluralsight Author
Funny and Honest
HTTP://BLAH.WINSMARTS.COM
@SAHILMALIK
16. Training and Consulting: contact@winsmarts.com
Your App SDK Office 365
• App hard tied to Office 365
• Secrets embedded in apps will expire, you cannot control when you update the app, because you don’t update it
• Your mobile app, is also an Office 365 App. You are limited by the security model they offer
• You may want to reuse your Mobile app investments for back-ends other than Office 365.
• Your authentication is tied to Azure AD. Your customer may not be.
• SDKs for iOS may not get the same love as .NET classes will
17. Training and Consulting: contact@winsmarts.com
Your App
Standards
based
communication
Office 365
ASP.NET WebAPI
• Standards! Yeah!
• You can update the ASP.NET backend, without users having to update the app
• You can offer security that makes sense for you, and map it to diverse back ends.
• You can add/remove support back ends at your whim.
• Authentication can be whatever you wish, even though backend can be Azure AD.
My preferred approach
18. Training and Consulting: contact@winsmarts.com
SHOW ME SOME CODE
2 PARTS
1. THE BACKEND
2. THE FRONTEND
19. Training and Consulting: contact@winsmarts.com
Mobile backend
• An ASP.NET MVC WebAPI site that supports OAuth and CORS.
21. Training and Consulting: contact@winsmarts.com
Step 2: Add Nuget Packages
Install-Package Microsoft.AspNet.WebApi.Owin –Version 5.1.2
Install-Package Microsoft.Owin.Host.SystemWeb –Version 2.1.0
Basic Setup
22. Training and Consulting: contact@winsmarts.com
Step 3: Add OWIN Startup.cs
using Microsoft.Owin;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
[assembly: OwinStartup(typeof(AngularJSAuthentication.API.Startup))]
namespace AngularJSAuthentication.API
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
// also delete global.asax – you don’t need it, this will suffice
Basic Setup
23. Training and Consulting: contact@winsmarts.com
Step 4: App_StartWebApiConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
using Newtonsoft.Json.Serialization;
namespace MobileBackEnd
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter =
config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
}
}
}
Basic Setup
24. Training and Consulting: contact@winsmarts.com
Step 5: Setup Authentication
Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1
Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.0.1
Add a class called AuthContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Identity.EntityFramework;
namespace MobileBackEnd
{
public class AuthContext : IdentityDbContext<IdentityUser>
{
public AuthContext() : base("AuthContext") { }
}
}
And add the supporting connection string
<connectionStrings>
<add name="AuthContext" connectionString="Data Source=SP; Initial Catalog=Users;User Id=sa;Password=p@ssword1" providerName="System.Data.SqlClient"/>
</connectionStrings>
Authentication
25. Training and Consulting: contact@winsmarts.com
Step 6: Authentication .. ModelsUserModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace MobileBackEnd.Models
{
public class UserModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
}
Authentication
26. Training and Consulting: contact@winsmarts.com
Step 7: Add AuthRepository
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using MobileBackEnd.Models;
namespace MobileBackEnd
{
public class AuthRepository : IDisposable
{
private AuthContext ctx;
private UserManager<IdentityUser> userMgr;
public AuthRepository()
{
ctx = new AuthContext();
userMgr = new UserManager<IdentityUser>(new UserStore<IdentityUser>(ctx));
}
public async Task<IdentityResult> RegisterUser(UserModel userModel)
{
IdentityUser user = new IdentityUser { UserName = userModel.UserName};
var result = await userMgr.CreateAsync(user, userModel.Password);
return result;
}
public async Task<IdentityUser> FindUser(string userName, string password)
{
IdentityUser user = await userMgr.FindAsync(userName, password);
return user;
}
public void Dispose()
{
ctx.Dispose();
userMgr.Dispose();
}
}
}
Authentication
27. Training and Consulting: contact@winsmarts.com
Step 8: Add an AccountController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.AspNet.Identity;
using MobileBackEnd.Models;
namespace MobileBackEnd.Controllers
{
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private AuthRepository repo = null;
public AccountController() { repo = new AuthRepository();}
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel) {
if (!ModelState.IsValid) return BadRequest(ModelState);
IdentityResult result = await repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null) return errorResult;
return Ok();
}
protected override void Dispose(bool disposing) {
if (disposing) repo.Dispose();
base.Dispose(disposing);
}
private IHttpActionResult GetErrorResult(IdentityResult result) {
if (result == null) return InternalServerError();
if (!result.Succeeded) { if (result.Errors != null) {
foreach (string error in result.Errors) {
ModelState.AddModelError("", error);
}
}
if (ModelState.IsValid) return BadRequest();
return BadRequest(ModelState);
}
return null;
}
}
}
Authentication
28. Training and Consulting: contact@winsmarts.com
Step 9: Create a user
{“username”:”Sahil”, “password”:”p@ssword1”}
Authentication
29. Training and Consulting: contact@winsmarts.com
Step 10: Add SPWebController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace MobileBackEnd.Controllers
{
[RoutePrefix("api/SPWeb")]
public class SPWebController : ApiController
{
[Authorize]
[Route("title")]
public IHttpActionResult Get()
{
return Ok("Dummy title for now");
}
}
}
Business Logic
If you visit /api/SPWeb/title
.. You will get a 401 unauthorized
30. Training and Consulting: contact@winsmarts.com
Step 11: Add support for OAuth
Authentication
Install-Package Microsoft.Owin.Security.Oauth –Version 2.1.0
Add a UserNamePasswordAuthorizationProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security.OAuth;
31. Training and Consulting: contact@winsmarts.com
Step 11: UserNamePasswordAuthorizationProvider.cs
namespace MobileBackEnd
{
public class UsernamePasswordAuthorizationProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); }
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
using (AuthRepository repo = new AuthRepository())
{
IdentityUser user = await repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "Incorrect username password.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
context.Validated(identity);
}
}
}
Authentication
32. Training and Consulting: contact@winsmarts.com
Step 12: Hook in the UPAuthProvider
• In the Startup.cs file, in the Configuration Method,
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new UsernamePasswordAuthorizationProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
Authentication
33. Training and Consulting: contact@winsmarts.com
Step 13: Add support for CORS
Install-Package Microsoft.Owin.Cors –Version 2.1.0
And add the below in Startup.cs
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
CORS
36. Training and Consulting: contact@winsmarts.com
Step 14: Add business logic
• Turn it into a SharePoint app
37. Training and Consulting: contact@winsmarts.com
Step 15: Modify SPWebController
public IHttpActionResult Get()
{
Uri siteUri = new Uri("https://winsmartsdev.sharepoint.com");
string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);
string accessToken = TokenHelper.GetAppOnlyAccessToken(
TokenHelper.SharePointPrincipal,
siteUri.Authority, realm).AccessToken;
using (var clientContext = TokenHelper.GetClientContextWithAccessToken(“https://winsmartsdev.sharepoint.com", accessToken))
{
if (clientContext != null)
{
clientContext.Load(clientContext.Web);
clientContext.ExecuteQuery();
return Ok(clientContext.Web.Title);
}
else return Ok("Title not found");
}
}
}
yes you will also have to use AppRegNew.aspx etc. to install the app ..
40. Training and Consulting: contact@winsmarts.com
The App
• Native or Hybrid
• Both are possible, but I’ll show Hybrid
• AngularJS SPA with 2 pages
• SignIn
• Home
41. Training and Consulting: contact@winsmarts.com
Step #1: The basics of the app
• Views
• Controllers
• And routing between them
42. Training and Consulting: contact@winsmarts.com
Step #1b: The screens
Index.html signin.html
Home.html
43. Training and Consulting: contact@winsmarts.com
Step #1c: The screens - controllers
loginController.js
Homecontroller.js