Protips for Windows Azure Mobile Services

  • Upload
    abena

  • View
    80

  • Download
    1

Embed Size (px)

DESCRIPTION

Protips for Windows Azure Mobile Services. Chris Risner Technical Evangelist 3-543. Introduction. Windows Azure Technical Evangelist. @ chrisrisner. Mobile Developer. http:// chrisrisner.com. Former .NET developer. Live in Washington. Grew up in Michigan. - PowerPoint PPT Presentation

Citation preview

Protips for Windows Azure Mobile Services Chris RisnerTechnical Evangelist3-543Introduction

@chrisrisnerhttp://chrisrisner.comLive in WashingtonWindows Azure Technical EvangelistMobile DeveloperFormer .NET developerCo-Organizer of Seattle GDGGrew up in MichiganAgenda

Mobile Services RecapTricksTipsTipsTricksQuestionsMobile Services RecapWindows Azure Mobile ServicesDataNotificationsAuthServer LogicScheduler

Logging & DiagScaleMulti-Platform AppsYou dont need a different Mobile Service for each platform!Build 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/137Connect them all!

Build 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/138Cross-Platform SupportCross-Platform Support

Multi-Device PushSingle Platform Push NotificationsWindows Storepush.wns.sendToastText04(item.channel, {text1: text}, );

Windows Phonepush.mpns.sendFlipTile(item.channel, {title: text}, );

iOSpush.apns.send(item.token, { alert: text, payload: { inAppMessage: Details }}, );

Androidpush.gcm.send(item.registrationId, item.text, );

Multi-Platform Push Notificationsfunction sendNotifications() { var deviceInfoTable = tables.getTable('DeviceInfo'); deviceInfoTable.where({ userId : user.userId }).read({ success: function(deviceInfos){ deviceInfos.forEach(function(deviceInfo){ if (deviceInfo.uuid != request.parameters.uuid) { if (deviceInfo.pushToken != null && deviceInfo.pushToken != 'SimulatorToken') { if (deviceInfo.platform == 'iOS') { push.apns.send(deviceInfo.pushToken, { alert: "New something created" } , { //success / error block}); } else if (deviceInfo.platform == 'Android') { push.gcm.send(deviceInfo.pushToken, "New something created", { success / error block}); } } } }); } });}Dont forget to check the response on error (or getFeedback for APNS)

Also, check out Delivering Push Notifications to Millions of Devices Friday @12pmBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1315Virtual TablesCreate a tableUse its endpointDont call request.ExecuteBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1317Custom APINon-table based scriptsAccessible fromGETPOSTPUTPATCHDELETEPermissions basedCustom APICustom API DemoTalking to Azure StorageIts doableIts not perfectScriptsand the Azure moduleBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1322Reading Tablesvar azure = require('azure');function read(query, user, request) { var accountName = 'accountname'; var accountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.table.core.windows.net'; var tableService = azure.createTableService(accountName, accountKey, host); tableService.queryTables(function (error, tables) { if (error) { request.respond(500, error); } else { request.respond(200, tables); } });}Reading Table Rowsvar azure = require('azure');function read(query, user, request) { var accountName = 'accountname'; var accountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.table.core.windows.net'; var tableService = azure.createTableService(accountName, accountKey, host); var tq = azure.TableQuery .select() .from(request.parameters.table);

tableService.queryEntities(tq, function (error, rows) { if (error) { request.respond(500, error); } else { request.respond(200, rows) } });}

Creating Containersvar azure = require('azure');function insert(item, user, request) { var accountName = 'accountname'; var accountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.blob.core.windows.net'; var blobService = azure.createBlobService(accountName, accountKey, host); if (request.parameters.isPublic == 1) { blobService.createContainerIfNotExists(item.containerName ,{publicAccessLevel : 'blob'} , function (error) { if (!error) { request.respond(200, item); } else { /* error */ request.respond(500);} }); } else { blobService.createContainerIfNotExists(item.containerName, function (error) { if (!error) { request.respond(200, item); } else { /*error */ request.respond(500); } }); }}Reading and Creating Blobsvar azure = require('azure'), qs = require('querystring');function insert(item, user, request) { var accountName = 'accountname'; var accountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.blob.core.windows.net'; var blobService = azure.createBlobService(accountName, accountKey, host); var sharedAccessPolicy = { AccessPolicy: { Permissions: 'rw', //Read and Write permissions Expiry: minutesFromNow(5) } }; var sasUrl = blobService.generateSharedAccessSignature(request.parameters.containerName, request.parameters.blobName, sharedAccessPolicy);

var sasQueryString = { 'sasUrl' : sasUrl.baseUrl + sasUrl.path + '?' + qs.stringify(sasUrl.queryString) }; request.respond(200, sasQueryString);}

function minutesFromNow(minutes) { var date = new Date() date.setMinutes(date.getMinutes() + minutes); return date;}

Storage DemoTalking RESTThe REST APIActionHTTP VerbURL SuffixCreatePOST/TodoItemRetrieveGET/TodoItem?$filter=id%3D42UpdatePATCH/TodoItem/idDeleteDELETE/TodoItem/idData Operations and their REST EquivalentsBase REST API Endpoint URLhttp://Mobileservice.azure-mobile.net/tables/*JSON to SQL Type MappingsJSON ValueT-SQL TypeNumeric values (integer, decimal, floating point)FloatBoolean BitDateTimeDateTimeOffset(3)String Nvarchar(max)Postman &Runscope DemoBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1331Sending Emails

Sending an Email//var crypto = require('crypto');//item.tempId = new Buffer(crypto.randomBytes(16)).toString('hex');

function sendEmail(item) { var sendgrid = new SendGrid('[email protected]', 'mypassword'); var email = { to : item.email, from : '[email protected]', subject : 'Welcome to MyApp', text: 'Thanks for installing My App! Click this link to verify:\n\n' + 'http://myapp.azurewebsites.net/activate.html?id=' + item.id + '&tid=' + item.tempId, createDate : new Date() };

sendgrid.send({ to: item.email, from: email.from, subject: email.subject, text: email.text }, function(success, message) { // If the email failed to send, log it as an error so we can investigate if (!success) { console.error(message); } else { saveSentEmail(email); } });}

Setting up SendGrid DemoBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1335The CLIIts aweSOMEBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1337CLI DemoBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1338Service Filters and DelegatingHandlersClient sideIntercepts requestsIntercepts responsesBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1340Sending Version Info with Each Request- (void)handleRequest:(NSURLRequest *)request next:(MSFilterNextBlock)next response:(MSFilterResponseBlock)response{ MSFilterResponseBlock wrappedResponse = ^(NSHTTPURLResponse *innerResponse, NSData *data, NSError *error) { response(innerResponse, data, error); }; // add additional versioning information to the querystring for versioning purposes NSString *append = [NSString stringWithFormat:@"build=%@&version=%@", self.build, self.version]; NSURL *url = nil; NSRange range = [request.URL.absoluteString rangeOfString:@"?"]; if (range.length > 0) { url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&%@&p=iOS", request.URL.absoluteString, append]]; } else { url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?%@&p=iOS", request.URL.absoluteString, append]]; } NSMutableURLRequest *newRequest = [request mutableCopy]; newRequest.URL = url; next(newRequest, wrappedResponse);}DelegatingHandlers are Service Filterspublic static MobileServiceClient MobileService = new MobileServiceClient( "https://.azure-mobile.net/", "", new VersionHandler()); using System;using System.Net.Http;using System.Threading.Tasks;namespace WindowsStore{ public class VersionHandler : DelegatingHandler { protected override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.ToString() + "?version=v2"); return base.SendAsync(request, cancellationToken); } }} Script VersioningChecking the Version in Scriptsfunction insert(item, user, request) { if (request.parameters.build < 2.0) { item.description = 'Not entered'; } request.execute({ success : function() { if (request.parameters.build < 2.0) { delete item.description; } request.respond(); } }); }

For more on versioning, check outGoing Live and Beyond with Windows Azure Mobile ServicesFriday @ 10:30 am

Build 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1345Talking Twitterv1 is deadv1.1 is hardBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1347Part 1: The Helpersfunction generateOAuthSignature(method, url, data){ var index = url.indexOf('?'); if (index > 0) url = url.substring(0, url.indexOf('?')); var signingToken = encodeURIComponent('Your Consumer Secret') + "&" + encodeURIComponent('Your Access Token Secret'); var keys = []; for (var d in data){ if (d != 'oauth_signature') { console.log('data: ' , d); keys.push(d); } } keys.sort(); var output = "GET&" + encodeURIComponent(url) + "&"; var params = ""; keys.forEach(function(k){ params += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(data[k]); }); params = encodeURIComponent(params.substring(1)); return hashString(signingToken, output+params, "base64");} function hashString(key, str, encoding){ var hmac = crypto.createHmac("sha1", key); hmac.update(str); return hmac.digest(encoding);} function generateNonce() { var code = ""; for (var i = 0; i < 20; i++) { code += Math.floor(Math.random() * 9).toString(); } return code;} Part 2: The Work (part 1)var crypto = require('crypto');var querystring = require('querystring'); function read(query, user, request) { var result = { id: query.id, identities: user.getIdentities(), userName: '' }; var identities = user.getIdentities(); var userId = user.userId; var twitterId = userId.substring(userId.indexOf(':') + 1); //API 1.0 //url = 'https://api.twitter.com/1/users/show/' + twitterId + '.json'; //API 1.1 var url = 'https://api.twitter.com/1.1/users/show.json?user_id=' + twitterId; var key = 'This is your consumer key'; var nonce = generateNonce(); var sigmethod = 'HMAC-SHA1'; var version = '1.0'; var twitterAccessToken = identities.twitter.accessToken; var oauth_token = 'The Access Token'; var seconds = new Date() / 1000; seconds = Math.round(seconds); var requestType = 'GET'; var oauthData = { oauth_consumer_key: key, oauth_nonce: nonce, oauth_signature:null, oauth_signature_method: sigmethod, oauth_timestamp: seconds, oauth_token: oauth_token, oauth_version: version }; var sigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } sigData['user_id'] = twitterId; Part 2.2: The Workvar sig = generateOAuthSignature('GET', url, sigData); oauthData.oauth_signature = sig; var oauthHeader = ""; for (k in oauthData){ oauthHeader += ", " + encodeURIComponent(k) + "=\"" + encodeURIComponent(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1); var authHeader = 'OAuth' + oauthHeader; //Generate callback for response from Twitter API var requestCallback = function (err, resp, body) { if (err || resp.statusCode !== 200) { console.error('Error sending data to the provider: ', err); request.respond(statusCodes.INTERNAL_SERVER_ERROR, body); } else { try { var userData = JSON.parse(body); if (userData.name != null) result.UserName = userData.name; else result.UserName = "can't get username"; request.respond(200, [result]); } catch (ex) { console.error('Error parsing response from the provider API: ', ex); request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex); } } } //Create the request and execute it var req = require('request'); var reqOptions = { uri: url, headers: { Accept: "application/json" } }; if (authHeader != null) reqOptions.headers['Authorization'] = authHeader; req(reqOptions, requestCallback);}That was terribleDo thisBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1351The Easy Wayexports.post = function(request, response) { var twitter = require(ctwitter.js); twitter.init(consumer key',consumer secret'); twitter.tweet(request.body.tweettext, request.user, request);}

Get the script here: http://bit.ly/14b73Gg

Script Source ControlEnable on dashboardCreates Git repoChanges push from client

Build 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1354

Shared Scriptsrequire(jsfile.js');

*Need a config change on update (for now)Note that sometime after Build this will break and youll do a normal require(jsfile.js);Build 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1357Auth Part 1: CustomPass creds inValidateHash your saltCreate a JWTBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1359Part 1: The Helpersfunction hash(text, salt, callback) { crypto.pbkdf2(text, salt, iterations, bytes, function(err, derivedKey){ if (err) { callback(err); } else { var h = new Buffer(derivedKey).toString('base64'); callback(null, h); } });} function slowEquals(a, b) { var diff = a.length ^ b.length; for (var i = 0; i < a.length && i < b.length; i++) { diff |= (a[i] ^ b[i]); } return diff === 0;} function zumoJwt(expiryDate, aud, userId, masterKey) { var crypto = require('crypto'); function base64(input) { return new Buffer(input, 'utf8').toString('base64'); } function urlFriendly(b64) { return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(new RegExp("=", "g"), ''); } function signature(input) { var key = crypto.createHash('sha256').update(masterKey + "JWTSig").digest('binary'); var str = crypto.createHmac('sha256', key).update(input).digest('base64'); return urlFriendly(str); } var s1 = '{"alg":"HS256","typ":"JWT","kid":0}'; var j2 = { "exp":expiryDate.valueOf() / 1000, "iss":"urn:microsoft:windows-azure:zumo, "ver":1, "aud":aud, "uid":userId }; var s2 = JSON.stringify(j2); var b1 = urlFriendly(base64(s1)); var b2 = urlFriendly(base64(s2)); var b3 = signature(b1 + "." + b2); return [b1,b2,b3].join(".");}Part 2: The Workvar crypto = require('crypto'), iterations = 1000, bytes = 32;var aud = "Custom", masterKey = "MyMobileServiceMasterKey;function insert(item, user, request) { var accounts = tables.getTable('accounts'); if (!item.username.match(/^[a-zA-Z0-9]{5,}$/)) { request.respond(400, "Invalid username (at least 4 chars, alphanumeric only)"); return; } else if (item.password.length < 7) { request.respond(400, "Invalid password (least 7 chars required)"); return; } accounts.where({ username : item.username}).read({ success: function(results) { if (results.length > 0) { request.respond(400, "Username already exists"); return; } else { // add a unique salt to the item item.salt = new Buffer(crypto.randomBytes(bytes)).toString('base64'); // hash the password hash(item.password, item.salt, function(err, h) { item.password = h; request.execute({ success: function () { // We don't want the salt or the password going back to the client delete item.password; delete item.salt; var userId = aud + ":" + item.id; item.userId = userId; var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30); item.token = zumoJwt(expiry, aud, userId, masterKey); request.respond(); } }); }); } } });}Part 3: Signing Invar crypto = require('crypto'), iterations = 1000, bytes = 32;var aud = "Custom", masterKey = "MyMobileServiceMasterKey"; function insert(item, user, request) { var accounts = tables.getTable('accounts'); accounts.where({ username : item.username }).read({ success: function(results) { if (results.length === 0) { request.respond(401, "Incorrect username or password"); } else { var account = results[0]; hash(item.password, account.salt, function(err, h) { var incoming = h; if (slowEquals(incoming, account.password)) { var expiry = new Date().setUTCDate(new Date().getUTCDate() + 30); var userId = aud + ":" + account.id; request.respond(200, { userId: userId, token: zumoJwt(expiry, aud, userId, masterKey) }); } else { request.respond(401, "Incorrect username or password"); } }); } } });}or just use Auth0http://aka.ms/authZeroZumo

Check out Whos that User? Friday @ 2pmBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1363Auth Part 2: Identity CachingStoring Credentials in .NETpublicstaticclassCredentialLocker{privateconststringRESOURCE="MobileServices";publicstaticvoidAddCredential(stringusername,stringpassword) {varvault=newPasswordVault();varcredential=newPasswordCredential(RESOURCE,username,password);vault.Add(credential);}publicstaticPasswordCredentialGetCredential() {PasswordCredentialcredential=null;varvault=newPasswordVault();try{credential=vault.FindAllByResource(RESOURCE).FirstOrDefault();if(credential!=null){credential.Password=vault.Retrieve(RESOURCE,credential.UserName).Password;}} catch(Exception){//creds not found}

returncredential;}

publicstaticvoidRemoveCredential(stringusername) {varvault=newPasswordVault();try{//Removesthecredentialfromthepasswordvault.vault.Remove(vault.Retrieve(RESOURCE,username));}catch(Exception){//creds not stored}}}Getting and Setting CredentialsSetting:

MoblieServiceUser user;user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook);CredentialLocker.AddCredential(user.userId, user.MobileServiceAuthenticationToken);

Getting:

var credential = CredentialLocker.GetCredential();if (credential != null){ MobileService.CurrentUser = new MobileServiceUser(credential.UserName); MobileService.CurrentUser.MobileServiceAuthenticationToken = credential.Password; }Auth Part 3: Expired TokensExpiration FlowInitial requestCheck response for 401Relogin userUpdate request with new tokenResend requestUpdate UI68DelegationHandlers (again)public class VersionHandler : DelegatingHandler{ protected async override Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); while (response.StatusCode == HttpStatusCode.Unauthorized) { try { await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook); request.Headers['X-ZUMO-AUTH'] = App.MobileService.CurrentUser.MobileServiceAuthenticationToken; } catch (Exception ex) {}

response = await base.SendAsync(request, cancellationToken); } } }ServiceFilter (iOS)- (void) filterResponse: (NSHTTPURLResponse *) response forData: (NSData *) data withError: (NSError *) error forRequest:(NSURLRequest *) request onNext:(MSFilterNextBlock) onNext onResponse: (MSFilterResponseBlock) onResponse{ if (response.statusCode == 401) { [self.client loginWithProvider:@"facebook" onController:[[[[UIApplication sharedApplication] delegate] window] rootViewController] animated:YES completion:^(MSUser *user, NSError *error) { if (error && error.code == -9001) { [self busy:NO]; onResponse(response, data, error); return; } NSMutableURLRequest *newRequest = [request mutableCopy]; [newRequest setValue:self.client.currentUser.mobileServiceAuthenticationToken forHTTPHeaderField:@"X-ZUMO-AUTH"]; onNext(newRequest, ^(NSHTTPURLResponse *innerResponse, NSData *innerData, NSError *innerError){ [self filterResponse:innerResponse forData:innerData withError:innerError forRequest:request onNext:onNext onResponse:onResponse]; }); }]; } else { [self busy:NO]; onResponse(response, data, error); }}Auth DemoBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1371One-to-ManyClientServerBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1373Clientpublic class BigTodo { public int Id { get; set;} ... [IgnoreDataMember] public List LittleTodos { get; set; }

}

//writing dataprivate async Task InsertBigTodo(MobileServiceClient mobileServiceClient, BigTodo bigTodo){ var bigTodoTable = mobileServiceClient.GetTable(); await bigTodoTable.InsertAsync(bigTodo); var bigTodoId = bigTodo.Id; var littleTodosTable = mobileServiceClient.GetTable(); foreach (var littlTodo in bigTodo.LittleTodos) { littleTodo.BigTodoId = bigTodoId; await littleTodosTable.InsertAsync(littleTodo); }}Server 1function insert(item, user, request) { var littleTodosTable = tables.getTable('LittleTodo'); var littleTodos = item.LittleTodos; var ids = new Array(littleTodos.length); var count = 0; littleTodos.forEach(function(littleTodo, index) { littleTodosTable.insert(littleTodos, { success: function() { // keep a count of callbacks count++; // build a list of new ids - make sure // they go back in the right order ids[index] = littleTodos.id; if (littleTodos.length === count) { // we've finished all updates, // send response with new IDs request.respond(201, { littleTodoIds: ids }); } } }); });}Benefits:Less likelihood of any specific save causing an issue. (especially if your mobile service and DB are in the same DC)Can use mssql to call a stored proc with a transactionCons: More logic in scriptsBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1375Server 2function insert(item, user, request) { var littleTodos; if (item.LittleTodos) { littleTodos = item.LittleTodos; delete item.LittleTodos; } request.execute({ success: function () { item.LittleTodos = []; if (littleTodos) { var i = 0; var insertNext = function () { if (i < littleTodos.length) { var littleTodo = littleTodos[i]; littleTodo.BigTodoId = item.id; littleTodo.LittleTodoOrder = i; tables.getTable('LittleTodo').insert(littleTodo, { success: function () { item.LittleTodos.push(littleTodo); i++; insertNext(); } }); } else { request.respond(); } }; insertNext(); } } });}Benefits:Less likelihood of any specific save causing an issue. (especially if your mobile service and DB are in the same DC)Can use mssql to call a stored proc with a transactionCons: More logic in scriptsBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1376Remember API call #s when considering client side one-to-manyBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1377Paging DataClientServerBuild 2013 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1379ClientC#:IMobileServiceTableQuery query = todoTable .Where(todoItem => todoItem.Complete == false) .Skip(3) .Take(3);items = await query.ToCollectionAsync();ListItems.ItemsSource = items;

iOS:NSPredicate * predicate = [NSPredicate predicateWithFormat:@"complete == NO"];MSQuery * query = [self.table queryWithPredicate:predicate];query.includeTotalCount = YES; // Request the total item count

query.fetchOffset = 3;query.fetchLimit = 3;

[query readWithCompletion:^(NSArray *results, NSInteger totalCount, NSError *error) {

Android:mToDoTable.where().field("complete").eq(false).skip(3).top(3) .execute(new TableQueryCallback() {Server ScriptsOption 1:query.where({complete: false}) .take(3) .skip(3);

Option 2:var q = query.getComponents();q.take = 3;q.skip = 1;query.setComponents(q);

Option 3:query.where(function() {return this.complete == false}) .take(3) .skip(3);

On-PremOn-Prem Solutions in Windows AzureSecure Site-to-Site Network ConnectivityWindows Azure Virtual NetworkCLOUDENTERPRISEData SynchronizationSQL Data SyncApplication-Layer Connectivity & Messaging Service BusSecure Machine-to-Machine Network ConnectivityWindows Azure ConnectSecure Point-to-Site Network ConnectivityWindows Azure Virtual Network83Communicate to your on-premise resources by setting up a Web Application Proxy (with Windows Server)Web Application ProxyService Bus RelaysCorporate NetworkWindows AzureDatabase

Service Bus

ApplicationSQL

Mobile ServiceCloud Worker

Corporate NetworkWindows AzureMobile ServiceOn-premise App

Service Bus

On-premisesYour datacenterIndividual computers behind corporate firewall

Point-to-Site VPNRoute-based VPN

Windows AzureVirtual NetworkVPN Gateway

DNS ServerVPN GatewayRemote devicesSite-to-SiteVPNPoint-to-Site VPNs

Build 2012 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1386On-premisesYour datacenter

Hardware VPN or Windows RRAS

Windows AzureVirtual NetworkVPN Gateway

DNS ServerVPN GatewaySite-to-SiteVPNSite-to-Site ConnectivityExtend your premises to the cloud securelyOn-ramp for migrating services to the cloudUse your on-prem resources in Azure (monitoring, AD, )

When we announced general availability of infrastructure services, we have re-iterated our commitment to make the power of AND work for our customers. We have more than one view of the world, it is cloud AND on-premises and we continue to deliver on that theme. Case in point is Point-to-Site VPN.

It allows you to setup VPN connections between individual computers and a virtual network in Azure. We built this capability based on customer requests and learnings from a preview feature called Windows Azure Connect. Point-to-Site VPN greatly simplifies setting up secure connections between Azure and client machines, whether from your office environment or from remote locations.

Using Point-to-Site VPN enables some new and exciting ways to connect to Windows Azure that are not possible from other cloud providers. Here are a few examples:

You can securely connect to your Windows Azure environment from any location. You can connect your laptop to a Windows Azure test and development environment and continue to code away while sipping coffee at an airport caf!Small businesses or departments within an enterprise who dont have existing VPN devices and/or network expertise to manage VPN devices can rely on the Point-to-Site VPN feature to securely connect to workloads running in Windows Azure virtual machines.You can quickly set up secure connections to Windows Azure even if your computers are behind a corporate proxy or firewall.Independent Software Vendors (ISVs) wanting to provide secure access to their cloud apps can leverage the Point-to-Site VPN feature to offer a seamless application experience.

Build 2012 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.6/26/1387Hybrid Networking with Windows Azure http://aka.ms/zumoprem1

Windows Azure Websites and On-Prem http://aka.ms/zumoprem2

Links for morePricingPreview Pricingpreview pricing & licensing$service level agreementsPreviewNo availability SLAShared (multitenant environment)Small instance is the only size available; multiple may be purchased First 10 Mobile Services are free, with no plans to start chargingReserved (private VM)Small instance is the only size available; multiple may be purchasedCharge for Preview will be at the same reduced rate as Web SitesUp to 100 Mobile Services90GA Pricingpreview pricing & licensing$service level agreementsGeneral Availability99.9%FreeStandardPremiumPriceFree(up to 10 services/month)$25 USD/mo(per unit*)$199 USD/mo(per unit*)API Calls500K (per subscription)1.5M(per unit)15M(per unit)Active Devices100(per subscription)5K (per unit)100K(per unit)ScaleN/AUp to 6 Standard unitsUp to 10 Enterprise unitsScheduled JobsLimitedIncludedIncludedSQL Database (required)20MB Included, Standard rates apply for more capacity20MB Included, Standard rates apply for more capacity20MB Included, Standard rates apply for more capacity*prorated daily

**Active devices refers to the number of devices that have both obtained your Mobile Services powered app from a public app store and have made at least one call to your mobile service or received a push notification from the mobile service in the past 30 days, including the current day.91ResourcesGet a Windows Azure Free Trial Accounthttp://www.windowsazure.com

Videos, Tutorials and morehttp://www.windowsazure.com/mobile

Mobile Services Resourceshttp://aka.ms/CommonWAMS

Contact mehttp://chrisrisner.com@chrisrisner

Mobile Services at BuildMobile Services Soup to NutsJosh Twist Thursday 2pmProtips for Mobile ServicesChris Risner Thursday 5pmCross-Platform w/ Mobile ServicesChris Risner Thursday 4pmConnected Win Phone AppsYavor Georgiev Friday 9amGoing Live and BeyondKirill and Paul Friday 10:30amDelivering Push Notifications to MillionsElio Demaggio Friday 12pmWhos that user?Dinesh Kulkarni Friday 2pmDeveloping Windows 8.1 apps using Windows Azure Mobile Services Nick Harris Friday 2pm 2013 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.