You can allow a JavaScript application hosted on another origin to call REST-enabled ViewModels in MDriven Turnkey by configuring Cross-Origin Resource Sharing (CORS) in IIS or by implementing Turnkey’s model-based origin check.
What CORS does
Cross-Origin Resource Sharing (CORS) is the browser mechanism that controls whether code loaded from one origin can request resources from another origin. An origin includes the scheme, host, and port.
For example, a page at https://app.example calling https://turnkey.example/TurnkeyRest/Get?command=RestExample is a cross-origin request. The browser sends an Origin header and requires the Turnkey site to return an appropriate Access-Control-Allow-Origin header before JavaScript can read the response.
Read the general MDN CORS description for browser behaviour. CORS controls browser access; it does not replace authentication or access control.
Choose where to configure CORS
| Approach | Use it when | Important consequence |
|---|---|---|
| IIS CORS module configuration | You want IIS to apply a fixed policy for a site or application. | IIS middleware writes the CORS headers. Turnkey’s dynamic model pattern will not have an observable effect when IIS or Cassini CORS middleware handles the request. |
| Turnkey model pattern | The allowed origin must be decided from model data, such as a list of approved external applications. | Turnkey evaluates TK_WebCors.GetAllowOrigin and caches each decision for 10 minutes.
|
Do not configure both mechanisms with competing policies. Select the layer that owns the CORS headers and verify the result in the browser network trace.
Configure a fixed policy in IIS
You can configure the IIS CORS module at the root site to affect sites on the machine, or in an application-level web.config to affect that application.
- Open the
web.configfor the IIS site that should own the policy. For a machine-wide root-site policy, use the root website, commonly Default Web Site. - Add or update the
system.webServerCORS section. - Replace the example origins with origins that you intend to allow.
- Deploy the configuration and test a request from the calling application.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="true">
<add origin="*" />
<add origin="https://www.test-cors.org" allowCredentials="true">
<allowHeaders allowAllRequestedHeaders="true" />
</add>
</cors>
</system.webServer>
</configuration>
The configuration above shows both a wildcard origin and a named origin with credentials. Review the effective IIS policy carefully before using it for a site with authenticated requests. The related JavaScript guidance states that allowing every domain does not work with login credentials under web standards; configure the particular calling domains when a session cookie is needed.
Update warning: an application-level web.config can be part of the Turnkey installation and can be replaced during an update. Keep a record of the change and verify the configuration after updating.
For IIS XML configuration details, see Getting Started with the IIS CORS Module and Documentation:IIS.
Configure dynamic origin decisions in Turnkey
Use this pattern when the list of permitted origins belongs in your model instead of a static IIS file. Turnkey calls a static method on a class named TK_WebCors for requests to REST-allowed ViewModels.
Required model pattern
- In MDriven Designer, create a class named
TK_WebCors. - Add a static method named
GetAllowOriginwith this signature:
GetAllowOrigin(org:String):Boolean
- Implement the method to return
trueonly for approved origins. - Expose the required ViewModel as REST-allowed and apply its normal access rules.
- Test from an approved origin and from an unapproved origin.
Turnkey passes the caller’s Origin value, in lower case, as org. Returning true permits that origin; returning false denies it.
Example: allow approved external applications
For a model class KnownExternalApp with Origin and IsOk attributes, the method can use OCL such as:
if KnownExternalApp.allinstances->exists(x|(x.Origin=org) and (x.IsOk)) then
true
else
false
endif
This example allows https://partner.example only when a corresponding KnownExternalApp object has that origin and IsOk is true. Store and compare origins consistently with the lower-case value received by the method.
Turnkey caches the decision in an internal dictionary for 10 minutes. A change to an approved-origin object can therefore take up to 10 minutes to take effect.
If the model pattern is invalid, Turnkey logs an exception with the message:
CheckCorsHeaders - check model pattern static TK_WebCors.GetAllowOrigin(vOrigin):string
Check the Turnkey log for this message, then verify the class name, static method name, parameter, and return type.
Headers returned by Turnkey
When Turnkey applies CORS headers through the model pattern, it returns headers equivalent to:
Access-Control-Allow-Origin: <approved origin>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Methods: POST, GET
Vary: Origin
The Vary: Origin header matters when responses pass through caches because the allowed origin can differ per request.
Call Turnkey REST from JavaScript
A cross-origin caller must originate from an allowed origin. A REST-allowed ViewModel also continues to enforce its access groups. For user-specific data, the ViewModel commonly checks IsLoggedIn and uses SysSingleton.oclSingleton.CurrentUser; CORS permission alone does not grant user access.
For the complete login flow and a cookie-based request example, see HowTos:Call Turnkey REST from Javascript. For a single-page application that sends a bearer token, see Documentation:Connecting javascript SinglePageApplications to Turnkey (SPA) and Documentation:Authenticate with a jwt.
Reuse a logged-in session cookie
The following jQuery example requests a REST ViewModel and asks the browser to include credentials. The Turnkey user must already be logged in, and the allowed CORS origin must be configured.
var serviceUrl = 'http://localhost:5052/TurnkeyRest/Get?command=ViewModel1';
$.ajax({
type: 'get',
url: serviceUrl,
xhrFields: { withCredentials: true }
}).done(function (data) {
$('#value1').text(data);
}).fail(function (jqXHR, textStatus) {
$('#value1').text(jqXHR.responseText || textStatus);
});
xhrFields: { withCredentials: true } is required to reuse the session cookie. A CORS failure is reported by this client as error; an access-group denial from the requested ViewModel is reported as AccessDenied. REST responses are JSON.
Post form data to a ViewModel
You can post form data to a ViewModel-driven MDriven form through TurnkeyRest/Post. This injects data into the standard UI and is not the preferred approach for designing a dedicated external API.
let formData = new FormData();
formData.append('Filter', 'v');
fetch('https://YOURTURNKEYSITE/TurnkeyRest/Post?command=AutoFormSysUserSeeker', {
headers: new Headers(),
method: 'POST',
mode: 'cors',
body: formData
}).then((response) => {
if (response.ok) {
return response.json();
}
}).then((responseJsonData) => {
callback && callback(responseJsonData);
}).catch((error) => {
console.log('getWatchHistory error ' + error);
});
Replace YOURTURNKEYSITE and AutoFormSysUserSeeker with your site and REST command. Confirm that the ViewModel is exposed as REST-allowed and that its access groups allow the current user.
Test and troubleshoot
- Test from the real calling origin, not only by opening the REST URL directly in a browser.
- In the browser developer tools, inspect the request
Originheader and the responseAccess-Control-Allow-Origin,Access-Control-Allow-Credentials, andVaryheaders. - To test a URL interactively, open test-cors.org, enter the root URL of the site in Remote URL, and run the test.
- If a model change appears ineffective, wait for the 10-minute Turnkey decision cache or confirm that IIS/Cassini middleware is not overriding Turnkey’s headers.
- If the browser receives
AccessDenied, troubleshoot the ViewModel’s access groups and login state rather than the CORS policy.
Security considerations
- Allow only origins you control or have explicitly approved, especially for endpoints that use a logged-in session cookie.
- Treat CORS as a browser policy, not an authorization mechanism. Keep access groups and authentication in place for every REST-enabled ViewModel.
- Do not send usernames and passwords using HTTP Basic authentication from browser JavaScript unless you have deliberately accepted the security implications. The existing basic-authentication example sends credentials from the client and is not recommended because of the open nature of the web.
