Connecting javascript SinglePageApplications to Turnkey (SPA)
Intro and Explanations

In order to connect SPA's built with pure-javascript or React or Angular or Vue or anything else, we must address these needs:

  1. Cors - allows foreign apps to query our Turnkey server - read specifics here.
  2. Authentication - allows foreign apps to acquire a jwt from somewhere (Oauth host) - and we want to check it and possibly accept it.
  3. Stay logged in - with a cookie after initial authentication.

To show this, we can use the Microsoft SPA example from here:

If you follow all the instructions there, you will soon have an SPA application that logs in with Azure AD - and gives you an idToken that is in jwt format (really jws as they are signed).

Once you have this example, follow the lead and add another button, just like the readMail and seeProfile buttons from the sample:

function readTurnkey() {
        .then(response => {

            const headers = new Headers();
            const bearer = `Bearer ${response.idToken}`;
            const endpoint ='http://localhost:5052/TurnkeyRest/Get?command=RestExample'
            headers.append("Authorization", bearer);

            const options = {
                method: "GET",
                headers: headers

            console.log('request made to Turnkey API at: ' + new Date().toString());

            fetch(endpoint, options)
                .then(response => response.json())
                .then(response => updateUI(response, endpoint))
                .catch(error => { console.log(error); });

        }).catch(error => {

What we do here is acquire a new idToken and send it to a Turnkey Rest API that, in this case, is at http://localhost:5052/TurnkeyRest/Get?command=RestExample

The json we get from this call is sent to updateUI where we do this:

   else {
        // turnkey data:
        try {
            profileDiv.innerHTML = JSON.stringify(data);
        catch (err) {
            profileDiv.innerHTML = data.toString();


To handle Cors and Authentication in the model, I follow the stipulated pattern and add this:

2021-02-28 23h24 21.png

The methods are implemented like this:


if KnownExternalApp.allinstances->exists(x|(x.Origin=org) and (x.IsOk)) then  
  true -- we want to allow this app to call us 

if KnownExternalApp.allinstances->exists(x|(x.Audience=audience) and (x.IsOk)) then  
  user -- we want to allow all users from this domain if they exists in our domain 


  • Make sure your Azure app registration says SPA and not only Web.
  • Make sure your Modulus attribute is made longer than 255 chars -> say 500 chars to be sure.
  • The Audience property is equal to your Application identity; a Guid you get from Azure (or Google or your Auth provider)
  • Find the ouath provider keys from the oauth-server. You can find Azure here: (there will be multiple - 5ish)
  • Create a SysExternalJWTDefinition for each unique "kid"== key-identifier, and copy the "n" value to Modulus attribute(Make it 500 chars long so all of it fits!), copy the "e" to the exponent attribute.
  • Create a KnownExternalApp and set its origin to the URL its hosted on (add many objects if it is hosted on many servers); in my example: for http://localhost:62031 paste in the Application identity from your oauth provider in the Audience attribute, set IsOk to true.
Code Example

The demo SPA app is taken from: with a few small additions and config-changes. You can download these changes from here instead: SPA website example.

The Turnkey system that the demo SPA app gets data from can be downloaded here: Turnkey model that servers the SPA

  1. Start Visual Studio and open the SPA website as "website". Run it - navigate to yourlocaladress/app3 to see the UI
  2. In order to login, create an App registration on any Azure account and make a note of particulars and update the website code so it matches what you got:
    2021-03-01 09h27 59.png

Now your SPA app should be able to 1) start and 2) Login with an account in your azureAD or any azureAD depending on your settings in the app registration:

2021-03-01 09h33 17.png

This is how it should look once logged in:

2021-03-01 09h35 23.png

Read Turnkey button must have a functioning Turnkey server in order to work. Start MDrivenDesigner and open the downloaded Modlr file.

2021-03-01 09h38 43.png

If your server has another address than localhost:5052 - change the SPA app accordingly:

2021-03-01 09h40 43.png

The Modlr file also had data in the xml persistence that defines keys and KnownApp-objects. If your URLs are different, you may need to update in the knownApp object:

2021-03-01 09h45 35.png
2021-03-01 09h42 53.png

Register an account on the Turnkey-app with the same email address as the account you use in your SPA app and log in. You can now see the RestApi result here: http://localhost:5052/Turnkey/AngularApp#/RestExample/$null$

We can now test that we get the same result when running the SPA app rest call:

2021-03-01 09h53 07.png

You can also verify that you do not get any data if the account email does not match or if you are not logged in.

This page was edited 5 days ago on 06/17/2024. What links here