none
How do you manage/enforce licensing on a fully Sharepoint hosted app?

    Question

  • Hi,

    I have currently building a Sharepoint hosted app (my first ever app!) for Sharepoint and have started investigating how I can licence it on the App store, but am getting highly confused!.

    I'm struggling to work out how I can block functionality or people using the app without a licence on Sharepoint hosted apps, which are fully client side.

    Ideally I need to be able to stop the site owner from adding new items to a list held on the app web (which powers a client app part) unless they have a valid licence. I do not want to put a check on the client web part itself, as this would mean the check is done by every user that visits the site, where as my plan is to have a one-time only cost that allows unlimited use by any number of users/sites on a tenant (Perpetual all user).

    Any one any ideas, advice, good resources to learn more about this?

    To summaries, I need advice on ways to licence / enforce licences by blocking use or functionality a fully Sharepoint hosted (client side) app.

    Thanks!

    Martin

     

    Sunday, November 18, 2012 4:58 PM

Answers

  • Ok so here is a rough draft of the sample code (put this in App.js to see how it works).  It still needs polish but I wanted to post this right away just in case. Whenever I have a polished version I'll update the thread but this should get you going.

    To test your code, you can use the tools included in this post to import a test license:

    http://blogs.msdn.com/b/officeapps/archive/2012/11/09/licensing-your-apps-for-sharepoint.aspx

    var context;
    var licenseCollection;
    var topLicense;
    var encodedTopLicense;
    var verifiedLicense;
    var response;
    
    // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
    $(document).ready(function () {
        context = SP.ClientContext.get_current();
    
        //Retrieve license from SharePoint; change this productId with the one for your app; you can get it from AppManifest.xml
        licenseCollection = SP.Utilities.Utility.getAppLicenseInformation(context, '{b7224c84-b20c-45b0-88ab-a9f897f7aa4f}');
        context.executeQueryAsync(onRetrieveLicenseFromSPSuccess, onRetrieveLicenseFromSPFailure);
        
    });
    
    
    //Retrieval call succeded (doesn't mean there is a license, look at the contents to see if there is one)
    function onRetrieveLicenseFromSPSuccess() {
    
        if (licenseCollection.get_count() > 0) {
            topLicense = licenseCollection.get_item(0).get_rawXMLLicenseToken();
    
            //debug
            alert("License retrieved from SharePoint: \n" + topLicense);
    
            //encode license; required to call the verification service since it will be sent on the URL 
            encodedTopLicense = encodeURIComponent(topLicense);
    
            //debug, display the encoded license 
            alert("License ready to be verified:\n" + encodedTopLicense);
        }
        else {
            alert("The user doesn't have a license");
        }
    
    
        //Call verification service via the WebProxy; this ensures the validity of the license in case SharePoint was tampered with
        var request = new SP.WebRequestInfo();
        //We use the REST flavor of the verification service
        request.set_url("https://verificationservice.officeapps.live.com/ova/verificationagent.svc/rest/verify?token=" + encodedTopLicense);
        request.set_method("GET");
        response = SP.WebProxy.invoke(context, request);
        // Set the event handlers and invoke the request
        context.executeQueryAsync(onVerificationCallSuccess, onVerificationCallFailure);
    
    }
    
    // This function is executed if the above call fails
    function onRetrieveLicenseFromSPFailure(sender, args) {
        alert('Failed to retrieve license:' + args.get_message());
    }
    
    function onVerificationCallSuccess() {
        var stories;
        var storiesHTML;
        var xmlDOM;
    
        //It is highly recommended to cache (e.g. cookies) the response
        //If you are concerned about tampering only re-retrieve and re-validate the license for sensitive operations in your App. 
        var verificationResponse= response.get_body();
    
    
        //Debug, display the raw response
        alert("Verification Service Response" + verificationResponse);
    
        xmlDoc = $.parseXML(verificationResponse);
        $xml = $(xmlDoc);
        licenseType = $xml.find("EntitlementType").text();
        licenseIsValid = $xml.find("IsValid").text();
        licenseIsTest = $xml.find("IsTest").text();
    
        //DO SOMETHING NOW THAT YOU KNOW WHAT TYPE OF LICENSE THE USER HAS
        switch (licenseType)
        {
            case "Free":
                alert("Free app");
                break;
            case "Paid":
                alert("Paid app");
                break;
            case "Trial":
                alert("Trial app");
                //You can then look at the expiration date on the response
                break;
        }
        
       
    }
    
    function onVerificationCallFailure() {
        alert('Failed to call verification service via WebProxy:' + args.get_message());
    }
    
    
    
    


    Program Manager, Office Developer Platform.


    Wednesday, November 21, 2012 12:33 AM
    Moderator

All replies

  • Hello Arktic,

    The general process for licensing your apps is described in this post. 

    http://blogs.msdn.com/b/officeapps/archive/2012/11/09/licensing-your-apps-for-sharepoint.aspx

    Specifically for SharePoint hosted apps we are working on a sample (should be ready in a day or 2) but the process is very similar, you retrieve*, verify and then use a license; I bet you can derive most of it from the sample in the post above.

    Be aware though that for purely client side apps there are inherent limitations with the technology because all code is ultimately interpreted by the browser; obfuscating your code usually helps.  

    I'll keep this thread updated once we have the full sample.

    *The retrieval portion can be done using our JS OM or REST. For the latter the syntax is similar to this:

    http://<appweburl>/_api/SP.Utilities.Utility.GetAppLicenseInformation(guid'<productId>')


    Monday, November 19, 2012 6:36 PM
    Moderator
  • Thanks Humberto! I'll have a play about and see if I can get it working, i was thinking about obfuscating the code to make it a bit more secure, but was worried that this might impact it getting accepted to the app store as that part of code would be hard to review?

    Also looking forward to seeing the sample, so really appreciate your offer to post back in thread once its completed :)


    Monday, November 19, 2012 7:02 PM
  • Obfuscating your code shouldn't affect app validation for the store.  There are multiple valid reasons (performance, security/etc.) that require minification and/or obfuscation of code.

    I'll post the sample once we have it ready.

    Monday, November 19, 2012 7:55 PM
    Moderator
  • Ok so here is a rough draft of the sample code (put this in App.js to see how it works).  It still needs polish but I wanted to post this right away just in case. Whenever I have a polished version I'll update the thread but this should get you going.

    To test your code, you can use the tools included in this post to import a test license:

    http://blogs.msdn.com/b/officeapps/archive/2012/11/09/licensing-your-apps-for-sharepoint.aspx

    var context;
    var licenseCollection;
    var topLicense;
    var encodedTopLicense;
    var verifiedLicense;
    var response;
    
    // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model
    $(document).ready(function () {
        context = SP.ClientContext.get_current();
    
        //Retrieve license from SharePoint; change this productId with the one for your app; you can get it from AppManifest.xml
        licenseCollection = SP.Utilities.Utility.getAppLicenseInformation(context, '{b7224c84-b20c-45b0-88ab-a9f897f7aa4f}');
        context.executeQueryAsync(onRetrieveLicenseFromSPSuccess, onRetrieveLicenseFromSPFailure);
        
    });
    
    
    //Retrieval call succeded (doesn't mean there is a license, look at the contents to see if there is one)
    function onRetrieveLicenseFromSPSuccess() {
    
        if (licenseCollection.get_count() > 0) {
            topLicense = licenseCollection.get_item(0).get_rawXMLLicenseToken();
    
            //debug
            alert("License retrieved from SharePoint: \n" + topLicense);
    
            //encode license; required to call the verification service since it will be sent on the URL 
            encodedTopLicense = encodeURIComponent(topLicense);
    
            //debug, display the encoded license 
            alert("License ready to be verified:\n" + encodedTopLicense);
        }
        else {
            alert("The user doesn't have a license");
        }
    
    
        //Call verification service via the WebProxy; this ensures the validity of the license in case SharePoint was tampered with
        var request = new SP.WebRequestInfo();
        //We use the REST flavor of the verification service
        request.set_url("https://verificationservice.officeapps.live.com/ova/verificationagent.svc/rest/verify?token=" + encodedTopLicense);
        request.set_method("GET");
        response = SP.WebProxy.invoke(context, request);
        // Set the event handlers and invoke the request
        context.executeQueryAsync(onVerificationCallSuccess, onVerificationCallFailure);
    
    }
    
    // This function is executed if the above call fails
    function onRetrieveLicenseFromSPFailure(sender, args) {
        alert('Failed to retrieve license:' + args.get_message());
    }
    
    function onVerificationCallSuccess() {
        var stories;
        var storiesHTML;
        var xmlDOM;
    
        //It is highly recommended to cache (e.g. cookies) the response
        //If you are concerned about tampering only re-retrieve and re-validate the license for sensitive operations in your App. 
        var verificationResponse= response.get_body();
    
    
        //Debug, display the raw response
        alert("Verification Service Response" + verificationResponse);
    
        xmlDoc = $.parseXML(verificationResponse);
        $xml = $(xmlDoc);
        licenseType = $xml.find("EntitlementType").text();
        licenseIsValid = $xml.find("IsValid").text();
        licenseIsTest = $xml.find("IsTest").text();
    
        //DO SOMETHING NOW THAT YOU KNOW WHAT TYPE OF LICENSE THE USER HAS
        switch (licenseType)
        {
            case "Free":
                alert("Free app");
                break;
            case "Paid":
                alert("Paid app");
                break;
            case "Trial":
                alert("Trial app");
                //You can then look at the expiration date on the response
                break;
        }
        
       
    }
    
    function onVerificationCallFailure() {
        alert('Failed to call verification service via WebProxy:' + args.get_message());
    }
    
    
    
    


    Program Manager, Office Developer Platform.


    Wednesday, November 21, 2012 12:33 AM
    Moderator
  • You sir, are a legend! thanks, this example is really helpful :)
    Wednesday, November 21, 2012 12:30 PM
  • I think it's a great concept to check licensing in client side javascript code - nobody will ever be able to steal your code and remove your license check in 60 seconds.
    Wednesday, November 28, 2012 9:27 PM
  • It's a risk, but then again I think are people really going to go through the effort to remove my license check for an app that costs a few hundred dollars at most?  Maybe, but we're also talking about business people using SharePoint and not kids trying to steal the latest game.

    Corey Roth - SharePoint Server MVP blog: www.dotnetmafia.com twitter: @coreyroth

    Friday, December 07, 2012 2:31 PM
    Answerer
  • Thanks for the code above!

    It worked well for me first time but after going back and forward from app web to host web to test out the fetching of the license I am getting intermittent license failures. Sometimes it fetches my trial license and other times it fails on "the user doesnt have a license".

    I get same results when testing in browsers on both VM and base machine.

    I installed the LicenseSPAppSample and created a test license on my O365 developer site. My app is a SharePoint-hosted app.

    Anyone else experience this issue? And if so what did you do to fix?

    Edit: 7/9/2013

    Not sure what went wrong for me but after a number of hours I finally change the app's Product Id, recreated the test license with new ID, and used the cleaned up JS here http://keutmann.blogspot.com/2013/02/sharepoint-2013-hosted-app-license-check.html and everything is now running well!     

    • Edited by SimonO Wednesday, July 10, 2013 3:50 AM Decided to update with successful steps rather than deleting
    Thursday, July 04, 2013 6:58 AM
  • Awesome code, thanks.

    You have 1 logical bug I found and fixed in my code.

    In this method: "onRetrieveLicenseFromSPSuccess"

    when you do not get any licenses, you go to the else alert("The user doesn't have a license");

    So, encodedTopLicense will be empty, you should return and stop execution there, and not run the web request to verify license.

    At the very least, add an if(encodedTopLicense != null) before you call for verification.

    Can't wait to see if it works in my app!


    Regards, Shai Petel.

    Tuesday, December 03, 2013 6:40 PM
  • In my sharepoint hosted app "licenseCollection.get_count()" is always 0, what is wrong with my code?
    Thursday, March 13, 2014 1:08 PM
  • Unfortunately it's trivial to "hack" this since it's client side Javascript....

    SharePoint-hosted apps can't be safely licensed.

    Thanks,

    Jonas


    JN

    Wednesday, June 18, 2014 3:38 AM
  • Hi Humberto,

    Do you know if there is a way to read the productID from the manifest.xml in javascript ?

    I have seen posts that it is not supported for PROVIDER hosted apps, but for SharePoint hosted apps, it might be possible, I hope ?


    Thursday, January 28, 2016 3:37 PM
  • Ok I have that the productID is available in this REST call

    _api/web/AllProperties

    Wednesday, February 10, 2016 2:35 PM