Using thiss-ds
==============

Overview
--------

There are two main APIs - a highlevel DiscoveryService API (which in hindsight really should have been called a discovery client API but ...) and a lowlevel PersistenceService. The job of the PersistenceService is to keep track of previous IdP choices. The data is stored in namespace browser local storage. The namespace is called the "context" below (more about how contexts work later).

The DiscoveryService class is essentially a reference to an instance of the PersistenceService and a metadata query service (MDQ for short) which relies on fetch to retrieve JSON-objects that represent known identity providers. This class also contains some utility methods providing a way to implement SAML Identity Provider Discovery v1.0.

Create an instance of the DiscoveryService object thus (where `my_context` is a string or unknown which makes the instance default to the default (or global) context):

.. code-block:: js

  var ds = new DiscoveryService(function(entity_id) { 
           // return json representation of entity_id
        }, 
        'https://use.thiss.io/ps/', 
        my_context,
        {
          selector: "#some-elem-id",
        }):

  var ds = new DiscoveryService('https://md.thiss.io/entities/', 
       'https://use.thiss.io/ps/', 
       my_context,
        {
          selector: "#some-elem-id",
          trustProfile: "name-of-profile",
          entityID: "https://sp-publishing-trust-profile.net/shibboleth"
        }):

The options `selector`, `trustProfile`, and `entityID` are optional.

.. _saa-usage-label:

The `selector` option is used to provide an anchor in the top level UI to which the service can attach a checkbox that will allow it to prompt the user for permission to access global persistence. As noted in the introduction, this will only affect users of chrome and chromium-based browsers that have enabled the privacy flag; this will presumably be a very small subset of users, so if the checkbox does not easily fit with the UX of the top level site, you can consider omitting it.

The `trustProfile`, and `entityID` are used to indicate a trust profile (which the MDQ must know about) that will limit the IdPs returned by the MDQ to some subset of all the IdPs known to it. There are different criteria that trust profiles can use to limit the IdPs served, such as by registration authority, or metadata source.

Calling the metadata lookup service with the entityID of an IdP returns a Promise that resolves (if the lookup was successful) to a JSON object (or undefined) that represents the IdP. The "schema" of the JSON is based in large parts on the classical discojson format and is explained below.

.. code-block:: js

  ds.mdq('https://idp.unitedid.org/idp/shibboleth').then(entity => {
    // do something with entity
  });

In order to implement a simple SAML discovery response:

.. code-block:: js

  ds.saml_discovery_response(entity_id, persist)


This call first calls the persistence service to record the users choice (possibly refreshing metadata using the mdq first) and then returns a SAML identity provider discovery protocol response. This could for instance called from an "onclick" method in a UI. In a typical implementation the mdq method is used to lookup metadata which is then used to drive the UI. When the user selects a particular IdP the above call persists the users choice and returns the discovery response via the SAML identity provider discovery protocol (which is essentially just a redirect) by setting the `window.location.href` to the assembled return URL.

The second parameter (persist) is a boolean (which by default is set to true) which if true causes the metadata returned from the lookup to be persisted in browser local store via the persistence service.

For situations where it is necessary to have more control over how the discovery response is created the following call is available:

.. code-block:: js
 
  ds.do_saml_discovery_response(entity_id, persist).then(entity => {
   // ... do something with entity JSON
  });

In this case the caller has to process the Promise returned by the call and assemble and process a discovery response. This could be useful for implementing extensions to SAML discovery or even as a basis for federation-enabled OpenIDC discovery.

Another use-case for do_saml_discovery_response is to "pin" an IdP choice based on some other process (other than a UI). For instance it may be known that users with access to an intranet site by definition should have a certain IdP pre-selected. In this case a call to do_saml_discovery_response with a static entity_id acts as a way to "pin" that IdP. In combination with UX that displays previous user "choices" this means that intranet users would never have to visit a (possibly complex) IdP search UX.

Because this is an important use-case an alias for ds.do_saml_discovery_response called ds.pin is available:

.. code-block:: js

  ds.pin(enterprise_idp_entity_id);

Note that the mdq implementation provided to the instance of DiscoveryService must be able to resolve this entity_id.

Finally the remove method removes the chose entity_id from the persistence-service if present.

.. code-block:: js

  ds.remove(entity_id)

Versions
--------

The version of the thiss-ds package will depend on the version of the persistence service that is going to be used.
The version of the persistence service can be found in `/manifest.json`. For example, if the persistence service is at
`https://use.thiss.io/ps/`, its version can be found at `https://use.thiss.io/manifest.json`. Also if post-robot is used
to directly communicate with the persistence service its version will depend on the persistence service version.

This is a table of compatible versions:

.. csv-table:: Compatible versionss
   :header: "Persistence Service", "thiss-ds", "post-robot"
   :widths: 150, 150, 150
   :align: center

    "2.1.98", "2.1.52", "10.0.14"
    "2.1.141", "2.1.52", "10.0.14"
    "3.0.3", "3.0.3", "10.0.14"

Metadata JSON schema
--------------------

The following fields are currently used:

.. code-block:: json

  {
    "entity_icon": "a data: URI for direct inclusion in html",
    "entity_icon_url": "a URI for reference inclusion in html",
    "title": "the name of the identity provider - primary display for users",
    "title_langs": "translations of the title",
    "descr": "a short description suitable for display inline",
    "descr_langs": "translations of the description",
    "md_sources": "metadata sources in which the entity was present",
    "name_tag": "an upper-case SLUG - typically based on the non-TLD/ccTLD part of the domain",
    "type": "idp or sp",
    "auth": "saml|opendic|other",
    "entity_id": "the entityID of the IdP",
    "hidden": "if hide-from-discovery is set",
    "scope": "a comma-separated list of domains/scopes associated with the IdP",
    "id": "sha1 ID as specified by the MDQ spec"
  }

Context
-------

The PersistenceService is initialized with a context. The context is a namespace string passed with each call to the API. The context is used to differentiate the persistence local storage to avoid overlap. This may seem counter intuitive as the point of the thiss.io persistence service is to share IdP choices among several services. However the goal is really to share IdP choice among services that share a common view of metadata. In order to make it possible for service to have overlapping or even conflicting metadata "views" the context can be used to differentiate between "metadata domains". A contexts may be protected in a given persistence service ORIGIN so some operations (such as removing a choice) may fail. Failures are always handled as rejected Promises and should be handled by the caller in the appropriate way.
