Windows Store app for banking: code walkthrough (Windows Store apps using JavaScript and HTML)

This sample shows how to create a simple Windows Store app for banking that implements secure authentication and communication over the internet. You can use the code in this sample for any Windows Store app that requires secure interaction with a server.

The following sections describe the parts of the Windows Store app for banking that enable secure authentication.

Prerequisites

Microsoft Visual Studio Express 2012 for Windows 8

Microsoft Visual Studio Professional 2012

Microsoft Visual Studio Ultimate 2012

Developer license

Note  To run the banking app, you need a web service that provides endpoints for communicating with the server. The following topics show System.Web.Mvc controller implementations for authentication, certificate enrollment, and certificate renewal.

Download Locations

Download the full code example from the following location: Windows Store app for banking with strong authentication.

Running the app

The Windows Store app for banking is modeled on existing web apps for banking that provide certificate-based authentication. You run the app for the first time and sign in with a user name and a password, which gives you limited access to your account. Then you close the app and start it again, this time choosing to upgrade to a certificate. Finally, you close the app and start it again, and when you click Sign in to your account, you have full access to your account without entering your credentials.

Password-based authentication

Do the following steps first.

  • Start the banking app.
  • Click Sign in to your account.
  • Sign in with username and password, which can be any non-empty strings, and click Submit.

    The accounts page says that you have limited access due to password-based authentication.

Upgrading to certificate-based authentication

Your account is fully accessible when you upgrade from username/password credentials to certificate-based authentication.

  • Close the app and start it again.
  • Click Upgrade to stronger authentication.
  • The app asks you to upgrade and explains the benefit of upgrading.
  • Check Server-generated key (PFX install) and click Yes.

    The app enrolls for a certificate and authenticates the user.

Access account by using certificate-based authentication

Close the app. When you start it again, you don't need to login with your username and password.

  • Launch the app again and click Sign in to your account.

    Because the per-app certificate is available, it is used for authentication. You're presented with a dialog to confirm the certificate. Once you're authenticated, the accounts page explains that you have full access.

Goal: Store user login credentials securely

You store username and password credentials securely in your Windows Store app by using the PasswordVault.

Goal: Set up the manifest for a secure app

You specify capabilities and certificates for your Windows Store app by editing the app's package manifest.

Goal: Sign in to the server

The Windows Store app for banking shows how to handle a user signing in for an online banking session. The server checks the user's certificate and the app displays the appropriate page based on the result.

Goal: Request a certificate

You can choose to request a certificate with a client-generated key or with a server-generated key. The Windows Store app for banking shows both ways by using the createRequestAsync and importPfxDataAsync functions.

Goal: Renew a certificate

You can renew a certificate as easily as you can request a new certificate, either with a client-generated key or with a server-generated key. The Windows Store app for banking shows both ways by using the createRequestAsync and importPfxDataAsync functions.

Package settings and the app manifest

You control the deployment of a Windows Store app by editing an app manifest file named package.appxmanifest or AppxManifest.xml. You can use Visual Studio Express 2012 for Windows 8 to apply settings to the manifest file, or you can edit the file directly.

In Solution Explorer, double-click package.appxmanifest to open the manifest designer.

Package Settings: App UI Properties

In the Application UI page, the following properties are set as shown.

PropertyValue
Start Pagedefault.html
Show NameAll Logos
Short NameTBA
Foreground TextDark
Background Color#000000
Lock Screen Notifications(not set)
Initial RotationPortrait

 

Package settings: app capabilities

In the Capabilities page, Internet (Client) and Private Networks (Client & Server) are checked. To use a shared certificate, ensure that Shared User Certificates is checked. For more info, see Setting certificate store capabilities.

Note  To use smart cards with the banking app, you must specify the sharedUserCertificates capability in the app manifest.

The following example shows how to declare your app's capabilities in the app manifest. The code is in package.appxmanifest.


<Capabilities>
   <Capability Name="privateNetworkClientServer" />
   <Capability Name="internetClient" />
<!--   <Capability Name="sharedUserCertificates"/>-->
 </Capabilities>

Package settings: certificate declarations

You assign a trusted root certificate and other digital certificates by using certificate declarations. The following procedure describes how to use the declaration designer to add a new root certificate. For more information, see Installing certificates by using the manifest.

If Certificates isn't listed under Supported Declarations, use the following steps to add a root certificate.

  • Open the Declarations page.
  • In the Available Declarations dropdown, select Certificates and click Add.

    The certificate designer opens.

  • Under TrustFlags, ensure that ExclusiveTrust is checked.
  • Under Selection Criteria, ensure that Auto Select is checked.
  • Under Certificates, click Add New.
  • In the StoreName textbox, type Root.
  • In the Content textbox, type myroot.cer.

The following example shows how to assign the root and developer certificates. The code is in package.appxmanifest.


<Extensions>
   <Extension Category="windows.certificates">
     <Certificates>
       <Certificate StoreName="Root" Content="myroot.cer" />
       <Certificate StoreName="CA" Content="mystandca.cer" />
       <TrustFlags ExclusiveTrust="true" />
       <SelectionCriteria AutoSelect="true" />
     </Certificates>
   </Extension>
 </Extensions>

Package Settings: Packaging Properties

In the Packaging page, set Package Name to Microsoft.SDKSamples.TailoredBankAppShare and set Package Display Name to TailoredBankAppShare SDK Sample.

Signing in to the server with username and password

Use the PasswordVault to store and retrieve password credentials for a secure application. For best security, the banking app encourages users to upgrade to certificate-based authentication.

The following example shows how to implement signing in to a secure app with username and password credentials. The code is in scenario0.js.


//account logon
// onClick() handler for the 'submitButton'
function logon() {
    try {
        var username = document.getElementById("username").value;
        var password = document.getElementById("password").value;

        //store user credential in the vault component
        if (username && password) {
            var cred = new Windows.Security.Credentials.PasswordCredential("WoodGrove-Bank-usercred", username, password);
            var vault = new Windows.Security.Credentials.PasswordVault();
            vault.add(cred);
        }else {
            return false;
        }
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    }

    var params = "UserName=" + username;
    params += "&Password=" + password;

    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/account/simplelogon",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            var /*@override*/ item = {
                title: "Account Page",
                content: "<strong>(Please upgrade to strong authentication if you need full access to your account.)",
                backgroundColor: 'rgba(25,50,200,1)',
                navigate: "groupeditemsPage"
            };
            WinJS.Navigation.navigate('/html/account.html', { item: item });
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
};

The username and password are added to the PasswordVault and are sent to the web service's simplelogon endpoint by using the xhr function. If the request is successful, account.html displays.

Requesting a certificate and enrolling

For best security, the banking app encourages users to upgrade to certificate-based authentication. You can create a certificate request in the client app, or you can import a Personal Information Exchange (PFX) certificate from the server. In both cases, you enroll by using the CertificateEnrollmentManager object.

Submitting a certificate request to a server

Your Windows Store app can submit a certificate request to a certification authority running Microsoft certificate services, and you can use CertificateEnrollmentManager to install the issued certificate. For more information, see Submitting a certificate request and installing the certificate response.

The following example shows how to request a certificate with a client-generated key. The code is in enrollment.js.


//pkcs10 certification enrollment
function createRequestBlobAndEnroll(user)
{
    var encoded;
    try {
        //WinRT APIs for creating a certficate request
        var request = new Windows.Security.Cryptography.Certificates.CertificateRequestProperties;
        request.subject = user;
        request.friendlyName = user + "'s WoodGrove Bank Certificate";
        request.keyProtectionLevel = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
        Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.createRequestAsync(request).then(
            function (requestResult) {
                encoded = requestResult;
	               var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
                var params = "username=" + user;
                params += "&request=" + encodeURIComponent(encoded);
		    
                WinJS.xhr({
                    type: "POST",
                    url: "Your URL",    //Please provide the server url here. For example:
                                        //url: "https://WoodGrove-Bank/bankserver2/enrollment/submit",
                    headers: { "Content-type": "application/x-www-form-urlencoded" },
                    data: params
                }).then(
                    function (/*@override*/request) {
                        var obtainedData = window.JSON.parse(request.responseText);
                        try {
                            // WinRT API for certficate enrollment
                            Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                                .installCertificateAsync(obtainedData.certificate, installOption ).then(
                            function() {
                                // set certificate enrollment mark to aviod redundant certificate enrollments
                                Windows.Storage.ApplicationData.current.localSettings.values["EnrollCertificate"] = true;

                                var /*@override*/ item = {
                                        title: "Account Page",
                                        content: 
"<strong>(Please sign out and re-launch the application so as to use your enrolled certificate.)<strong>",
                                        backgroundColor: 'rgba(191, 84, 46, 1)',
                                        navigate: "sign-out"
                                };
                                WinJS.Navigation.navigate('/html/account.html', { item: item });
                                return false;
                            },
                            function(installError) {
                                gotoError("(The error was: <strong>" + intallError.message + "</strong>)");
                                return false;
                            });
                        } catch (err) {
                            var /*@override*/ message = '';
                            for (var /*@override*/ f in err) {
                            message += f;
                            message += ':';
                            message += err[f];
                            message += ' ';
                        }
                            goError(message);
                            return false;
                        }
                    },
                    function (/*@override*/request) {
                        goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                        + "The server URL you are using may not be valid. <br>"
                        + "Please contact your bank server service, "
                        + "or refer to the bank server topic for instructions to setup your own server.");
                        return false;
                    });
            },
            function (requestError) {
                gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                return false;
            });
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    };
}

The createRequestBlobAndEnroll function uses the CertificateEnrollmentManager object to generate a certificate request and submit it to the server. If the request is successful, the installCertificateAsync function installs the provided certificate.

Renewing with a client-generated key

Renewing a certificate is essentially the same procedure as installing it the first time, because Windows 8 filters out the old certificate.

The following example shows how to request a certificate with a client-generated key. The code is in groupedItemsPage.js.



//pkcs10 certificate renewal
function createRequestBlobAndEnroll(user) {
    var encoded;
    try {
        //WinRT APIs for creating a certficate request
        var request = new Windows.Security.Cryptography.Certificates.CertificateRequestProperties;
        request.subject = user;
        request.friendlyName = user + "'s WoodGrove Bank Certificate";
        request.keyProtectionLevel = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
        Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager.createRequestAsync(request).then(
            function (requestResult) {
                encoded = requestResult;
                // No username or password required in this case - we already have a
                // client-authenticated SSL connection
                var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
                var params = "request=" + encodeURIComponent(encoded);
                WinJS.xhr({
                    type: "POST",
                    url: "Your URL",    //Please provide the server url here. For example:
                                        //url: "https://WoodGrove-Bank/bankserver2/renewal/renewP10",
                    headers: { "Content-type": "application/x-www-form-urlencoded" },
                    data: params
                }).then(
                    function (/*@override*/request) {
                        var obtainedData = window.JSON.parse(request.responseText);
                        // We do not need to remove the previous certificate first -
                        // Win8 filtering will take care of it.
                        try {
                            Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                                .installCertificateAsync(obtainedData.certificate, installOption).then(
                            function () {
                                var /*@override*/ item = {
                                    title: "Account Page",
                                    content: "",
                                    backgroundColor: 'rgba(25,50,200,1)',
                                    navigate: "sign-out"
                                };
                                WinJS.Navigation.navigate('/html/account.html', { item: item });
                            },
                            function (installError) {
                                gotoError("(The error was: <strong>" + intallError.message + "</strong>)");
                                return false;
                            });
                        } catch (err) {
                            var /*@override*/ message = '';
                            for (var /*@override*/ f in err) {
                                message += f;
                                message += ':';
                                message += err[f];
                                message += ' ';
                            };
                            goError(message);
                            return false;
                        };
                    },
                    function (/*@override*/request) {
                        goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                        + "The server URL you are using may not be valid. <br>"
                        + "Please contact your bank server service, "
                        + "or refer to the bank server walk through document for instructions to setup your own server.");
                        return false;
                    });
            },
            function (requestError) {
                gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                return false;
            });
    }
    catch (err) {
        var message = '';
        for (var f in err) {
            message += f;
            message += ':';
            message += err[f];
            message += ' ';
        }
        goError(message);
        return false;
    };
}

The createRequestBlobAndEnroll function uses the CertificateEnrollmentManager object to generate a certificate request and submit it to the server. If the request is successful, the installCertificateAsync function installs the provided certificate. It's not necessary remove the old certificate, because Windows 8 filters it out.

Checking a certificate

You can query the status of a certificate and display different pages depending on the result.

The following example shows how to check a certificate and display different pages depending on the result. The code is in groupedItemsPage.js.



//check certification
function checkCert(senario, scenarioitem) {
    var params = "";
    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/renewal/CheckCert",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            if (obtainedData.hasCert === true) {
                if (obtainedData.renew === true) {
                    if (obtainedData.pfx === true) {
                        doPFXRenewal(obtainedData.user);
                    }
                    else {
                        doPKCS10Renewal(obtainedData.user);
                    }
                }
                else {
                    var /*@override*/ item = {
                        title: "Account Page",
                        content: "",
                        backgroundColor: 'rgba(25,50,200,1)',
                        navigate: "sign-out"
                    };
                    WinJS.Navigation.navigate('/html/account.html', { item: item });
                }
            }
            else {
                switch (senario) {
                    case 0:
                        if (checkCredential()) {
                            var item2 = {
                                title: "Account Page",
                                content: 
"<strong>(Please upgrade to strong authentication if you need full access to your account. <br>"
+ "If you already upgraded to use strong authentication, please sign out and launch the app again and sign-in.)</strong>",
                                backgroundColor: 'rgba(191, 84, 46, 1)',
                                navigate: "groupeditemsPage"
                            };
                            WinJS.Navigation.navigate('/html/account.html', { item: item2 });
                        } else {
                            WinJS.Navigation.navigate('/html/scenario0.html', { item: scenarioitem });
                        }
                        break;
                    case 1:
                        if (checkCredential() && !checkEnrollment()) {
                            WinJS.Navigation.navigate('/html/enrollment.html', { item: scenarioitem });
                        }
                        else if (!checkEnrollment()) {
                            WinJS.Navigation.navigate('/html/scenario1.html', { item: scenarioitem });
                        }
                        else {
                            var item3 = {
                                title: "Account Page",
                                content: 
"<strong>(You already upgraded to use strong authentication. Please sign out and launch the app again and sign-in.)</strong>",
                                backgroundColor: 'rgba(191, 84, 46, 1)',
                                navigate: "sign-out"
                            };
                            WinJS.Navigation.navigate('/html/account.html', { item: item3 });
                        }
                        break;
                    default:
                        goError("unknown scenario!!!");
                        break;
                }
            }
            return false;
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

The checkCert function queries the web service's CheckCert endpoint and parses the response. If the response is a certificate, the app checks whether the certificate needs to be renewed, and if so, the app calls the appropriate renewal function. Otherwise, the strongly authenticated account page displays.

Upgrading to certificate-based authentication

For best security, the banking app encourages users to upgrade to certificate-based authentication. You can create a certificate request in the client app, or you can import a Personal Information Exchange (PFX) certificate from the server. In both cases, you enroll by using the CertificateEnrollmentManager object.

Importing a PFX certificate from a server

The following example shows how to import a PFX certificate from a server. The code is in enrollment.js.


// pfx certificate enrollment
function doPFXEnrollment(user) {

    var exportable = Windows.Security.Cryptography.Certificates.ExportOption.exportable;
    var consent = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
    var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
    var params = "username=" + user;

    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/enrollment/getPFX",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            try {
                // import pfx certificate file into the app container
                Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                    .importPfxDataAsync(obtainedData.pfx, obtainedData.password, exportable, consent, 
                        installOption, obtainedData.friendlyName).then(
                    function () {
                // set certificate enrollment mark to aviod redundant certificate enrollments
                Windows.Storage.ApplicationData.current.localSettings.values["EnrollCertificate"] = true;
                var /*@override*/ item = {
                    title: "Account Page",
                    content: 
"<strong>(Please sign out and re-launch the application so as to use your enrolled certificate.)</strong>",
                    backgroundColor: 'rgba(191, 84, 46, 1)',
                    navigate: "sign-out"
                };
                WinJS.Navigation.navigate('/html/account.html', { item: item });
                return;
                    },
                    function(importError) {
                        gotoError("(The error was: <strong>" + requestError.message + "</strong>)");
                        return false;
                    }); 
            } catch (err) {
                var message = '';
                for (var f in err) {
                    message += f;
                    message += ':';
                    message += err[f];
                    message += ' ';
                }
                goError(message);
                return false;
            }
        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

The doPFXEnrollment function attempts to connect to the web service's getPFX endpoint. If successful, the CertificateEnrollmentManager object imports the provided certificate and installs it on the client machine.

Renewing a PFX certificate

Renewing a Personal Information Exchange (PFX) certificate is essentially the same procedure as installing it the first time, because Windows 8 filters out the old certificate.

The following example shows how to renew a PFX certificate. The code is in groupedItemsPage.js.


//pfx certificate renewal
function doPFXRenewal(user) {
    var exportable = Windows.Security.Cryptography.Certificates.ExportOption.exportable;
    var consent = Windows.Security.Cryptography.Certificates.KeyProtectionLevel.noConsent;
    var installOption = 0; //Windows.Security.Cryptography.Certificates.InstallOptions.none;
    var params = "username=" + user;
    WinJS.xhr({
        type: "POST",
        url: "Your URL",    //Please provide the server url here. For example:
                            //url: "https://WoodGrove-Bank/bankserver2/enrollment/getPFX",
        headers: { "Content-type": "application/x-www-form-urlencoded" },
        data: params
    }).then(
        function (request) {
            var obtainedData = window.JSON.parse(request.responseText);
            // We do not need to remove the previous certificate first -
            // Win8 filtering will take care of it.
            try {
                // No need to remove old cert, Windows will filter.
                Windows.Security.Cryptography.Certificates.CertificateEnrollmentManager
                    .importPfxDataAsync(obtainedData.pfx, obtainedData.password, exportable, consent, 
                        installOption, obtainedData.friendlyName).then(
                    function () {
                        var /*@override*/ item = {
                            title: "Account Page",
                            content: "",
                            backgroundColor: 'rgba(25,50,200,1)',
                            navigate: "sign-out"
                        };
                        WinJS.Navigation.navigate('/html/account.html', { item: item });
                    },
                    function (importError) {
                        gotoError("(The error was: <strong>" + importError.message + "</strong>)");
                        return false;
                    });
            } catch (err) {
                var message = '';
                for (var f in err) {
                    message += f;
                    message += ':';
                    message += err[f];
                    message += ' ';
                };
                goError(message);
                return false;
            }

        },
        function (request) {
            goError("(The error was: <strong>" + request.message + "</strong>) <br>" 
                    + "The server URL you are using may not be valid. <br>"
                    + "Please contact your bank server service, "
                    + "or refer to the bank server walk through document for instructions to setup your own server.");
            return false;
        }
    );
}

The doPFXRenewal function posts a request to the server for a PFX certificate. If successful, the importPfxDataAsync function imports the provided certificate and installs it on the client machine. It's not necessary remove the old certificate, because Windows 8 filters it out.

Related topics

CertificateEnrollmentManager
PasswordVault
Working with certificates
Creating a Windows Store app for banking
Create a Windows Store app for banking: resources

 

 

Build date: 6/11/2013

Show:
© 2014 Microsoft