Storefront apps

See us on Github.

We love feedback. Please get in touch at


Storefront apps are specially privileged apps that perform actions on TAS itself, like installing apps, adding identity sources, etc.

Storefront apps need to be tagged as "storeFront" by TAS support. Please contact TAS if you want to build a storefront app.

In theory multiple storefront apps can coexist within the same tenant (like an Android phone can run multiple launchers at once).

Patching a tenant

All changes to a store's configuration - i.e. installs, edits or uninstalls of the apps and identity sources making up the store - are made via patch documents (documents that conform to the HTTP PATCH semantics described in

As per the RFC, all changes in the patch set are made with a single atomic transaction.

Additional patch semantics

TAS supports an additional id-based path style inspired by SCIM (, except that EMCA script is used, e.g.:

This path points to the installed app "tas-legacyats".

"path": "installs[app=='tas-legacyats']"

Patch examples

This example updates the baseUri on the "main" server of the installed app "tas-legacyats".

PATCH /{tenant} [ { "op": "replace", "path": "installs[app=='tas-legacyats'].servers[name=='main'].baseUri", "value": "" ]

This example installs the app "zongo" from the developer "zwarg", after first checking that the store is still at incarnation 23.

PATCH /{tenant} [ { "op": "test", "path": "status.incarnation", "value": 23 }, { "op": "add", "path": "status.installs.-", "value": {"app": "zwarg-zongo"} } ]

This example installs an app with a server override (i.e., a non-multi tenanted app such as a legacy ATS).

PATCH /{tenant} [ { "op": "test", "path": "/incarnation", "value": 23 }, { "op": "add", "path": "/installs/-", "value": {"app": "tas-legacyats", "servers": [{"name": "ats0", "basicAuthCredentials": {"user": "fred", "password": "secret1"}}]} } ]

This example changes the password for the previously installed app:

PATCH /{tenant} [ { "op": "test", "path": "/incarnation", "value": 23 }, { "op": "add", "path": "/installs/6/servers/0/basicAuthCredentials/password", "value": "secret2" } ]

This example sets up a new tenant:

  1. creates the store for a new tenant "acme"
  2. installs the storefront app
  3. installs a single identity source for the bootstrap IdP
  4. hardcodes all people in the bootstrap IdP as "hrStoreAppAdmin" and "hrStoreIdentityAdmin", so that they can manage the store.

PATCH /{tenant} [ { "op": "add", "path": "/", "value": { "details": { "organizationName": "ACME Corporation", "shortCode": "acme", "description": "A test site while we see if this stuff works", "country": "US", "industry": "Manufacturing" }, "installs": [ { "app": "tas-storefront" } ], "identitySourceSets": [ { "principalDeveloper": "tas", "principalType": "user", "identitySources": [ { "kind": "bootstrapIdentitySource", "description": "The store owner automatically gets access to manage the store", "attributeOverrides": [ { "filter": "*", "ifMatched": [ { "name": "tas-user-roles", "values": [ "hrStoreIdentityAdmin", "hrStoreAppsAdmin", "hrStoreViewer" ] } ] } ] } ] } ] } } ]

Applying a patch

To apply a patch document to a tenant, your storefront app makes a series of API calls, as shown in the example below:

  1. The user (an HR admin person) is at the app details page within your storefront web site.
  2. They click install on the app, submitting a web form
  3. Your app's server code composes a patch document based on the user's input.
  4. Your app's server code POSTS to /{tenant}/patchSets, with:
    1. the patch document
    2. the continue page, i.e. where the storefront would like the user to end up on completion
    ..and checks for errors.
  5. The response from TAS contains a signed JWT, which embeds the original patch document and the continue page.
  6. Your server code inserts the JWT into the onClick handler of a new "confirm" button on the app details page.
  7. The user is now back at the app details page. They click confirm.
  8. The browser opens a progress dialog.
  9. The browser submits the JWT, including the original patch document, via an ajax call to PATCH /{tenant}.
  10. The call returns immediately with a launch key and the first few items of the patch log.
  11. The browser writes the patch log to the progress dialog.
  12. The browser immediately makes another ajax call to GET /{tenant}/patchLaunches/{launchKey}
  13. This is a long polling method, which returns as soon as any new patch log items are available.
  14. The browser loops, making repeated ajax calls to GET /{tenant}/patchLaunches/{launchKey} and adding new log messages to the progress dialog, until the operation is complete.
  15. The final response from GET /{tenant}/patchLaunches/{launchKey} includes the URI of the continue page.
  16. The browser displays a "patch complete" message to the user, and a "Continue" button.
  17. On pressing Continue, the user is redirected to the continue page.
  18. We're done.

The complexity of the interactions, and the use of JWTs, is necessary:

How TAS applies patch sets

To apply a patch set, TAS does the following:

  1. requests each already installed app to shut down
  2. applies the set
  3. resets all volatile credentials for the tenant:
  4. Increments the tenant's incarnation (thus invalidating all existing OAuth tokens)
  5. detects whether the delta set includes uninstalling the app that created tha launcher, and if so calls GET /m/storeFrontMainPage to get a new continue page. Note this prevents an about-to-be-uninstalled storefront from specifying any continue page, not just a page on itself.
  6. sends startup requests to each installed app

In parallel to the above, TAS causes any outstanding long polling calls to GET /{tenant}/patchSetLaunches to return each time there is a new available event. A maximum of 100 events are returned per call.

Bootstrap patch set

As well as patching existing tenants, storefront apps can also create new tenants, for example:

POST /tenantBootstraps/{tenant}

Once this launch is complete, the storefront app can take over the UI. TAS will call GET /storeFrontMain.