Remere Login

A declarative HTML API for User Authentication

Most recent revision 2021-04-22. Available here.

Navigation

Introduction

The Remere Login specification was created to automate the process of users signing into websites, in order to make it easier for users, and make the process more secure. (i.e. Enable password alternatives.) It has the following primary characteristics.

Primary characteristics:
Example of a ServiceTrigger HTML Element (a Anchor HTML Element) and login selection popup list.
Example HTML login anchor Example HTML login anchor
Note that the "remere" menu item in the browser chrome will change its status depending on the login status of the selected tab. (i.e. If the browser is logged into the website or not.) If the browser is logged in, then the menu item will display a different icon or label. And it will display different UserLogin list items in the popup dialog box, etc.

Example HTML.
Includes a ServiceGuide HTML Element (contains a FiskDigest), and a ServiceTrigger HTML Element (contains a FiskTrigger).
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
The mainFiskCatalog.json contents =
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

Introduction to the Login Process

The Remere User Authentication process is commonly started when a user clicks on a Service Trigger. The trigger defines what FiskProfile to use, and what Operation to perform. (In the FiskTrigger.protocolOp property.) The process will connect to the Login Manager (if it has not already done so), and request from it a list of User Logins for the current website. It will display the list of login items to the user, and allow the user to select a login from the list, or create a new one. Upon selection, it will ask the Login Manager to generate a Proof of Identity from the selected User Login and the HTML page's challengeTime. (A Proof of Identity includes a "username" and a list of "Credential Proofs", etc.) Then the process will create a Service Request (an RML Request), encode the Proof of Identity in it, and send the request to the Service Endpoint. The website will perform the requested operation and will return a Service Response (an RML Response). The Remere process will receive the response from the website, finish logging in the user, and will then update the HTML page being displayed.

Intro to Soc

Intro to "Service Order Configuration"

The Remere Login specification works like this. The browser creates a "Proof of Identity" (that contains various User Credentials) and sends it to the website. (It is sent inside of a Service Request.) The website evaluates the proof, and sends a response. The response includes a confirmation, if the proof was accepted or not. If the proof was accepted, the response might also also include an instruction that the browser should (or can) refresh the current page. Or it might contain a url of a new page the browser should be redirected to.

The hardest part is the creation of the Proof of Identity. That is what Soc is for.

Simply put, Soc is a bunch of properties, that tell the browser HOW to create the Service Request. (SOC is JSON. Usually it is embedded in an HTML page somehow.) It is a means to avoid the web page having to contain JavaScript. It is a list of requirements, instead of a list of instructions to perform. (i.e. It is not code. Not an algorithm.) It is used to avoid the web page having to contain JavaScript code.

SOC allows custom Service Requests to be created. It is a more-advanced alternative to a web page including a pre-made URL. Like the following.

<a href="remere://example.com?arg1=value1&arg2=value2&arg3=value3">Send info to website</a>

A pre-made URL will work for simple service requests. However, it is not possible to make more advanced Service Requests with a pre-made URL. For example, a service request with dynamic argument values. Where the argument values require processing or a choice. For Example: Argument values that are determined at the time of the service call. And/or argument values that are copied from the browser. (That have to do with the browser state, etc.) To perform an advanced Service Request, a custom URL may have to be created, that contains custom arguments.

When a more advanced Service Request needs to be performed, the traditional way to do it is with JavaScript. (Example: AJAX call, etc. JavaScript is used to create a custom URL or custom arguments, and process the results.) However, in the case where using JavaScript is not an option, something else was needed. That is what SOC is. (Note that SOC generally uses the HTTPS POST mechanism. Not HTTPS GET.)

Think of an order form to order a product. (Or an application form to apply for a job, service, etc.) An SOC Profile specifies what data the browser needs to supply in the order form.

SOC is customizable by protocol. Each protocol defines its own type of FiskProfile. And a browser must have a protocol handler installed that will perform the tasks required by that protocol.

There are 4 main SOC object types. They are FiskProfile, FiskCatalog, FiskDigest, FiskTrigger.

OLD Intro to Soc

The Remere Login specification uses the concept of a website "Service". The entire Remere Login process is simply one type of service, offered by the website. This particular service defines a general process for how User Authentication will work. With a "service" an RP can declare that it supports a particular service, (like the Remere Login "service"), and the browser can then call the service when it wants to perform an action. (To login or logout, etc.)

Because the process of defining and invoking a "website service" may be useful for other types of services beyond User Authentication, it was generalized and split out from Remere Login into a separate specification, called the Capis specification.

A "service" defines a specific process. Each service must have a well-known API and protocol, so that the browser can behave as the spec requires. A service works *without JavaScript*. (The browser is not required to enable Javascript for the website.) A service is performed by the browser, depending on what JSON settings are included in the HTML. The ConfigSettings (a FiskProfile JSON object) allow a service to be customized. They define what the caller (the browser) should do when making a service call. A website (or an individual page on a website) can define different JSON settings to use with the service, to get the behavior that it desires. Each service defines its own set of JSON settings, that a caller (the browser) can choose to use or not. A website can choose what services it wants to offer, and what JSON settings it wants to include in the HTML page.

A service works a lot like a form submission. The browser assembles a "Service Request" and sends the request to the RP. The request can contain a bunch of different JSON values. The RP then sends back a response, and the browser must react to the response in the manner that the service requires.

The "service settings" are JSON values embedded in HTML markup by the RP website. In order to use any service, an HTML page MUST contain a ServiceGuide HTML Element. And the ServiceGuide MUST contain a FiskDigest JSON object in the name="serviceguidedata" attribute. A FiskDigest object will either contain a list of FiskProfile objects directly, or it will contain a reference to a separate FiskCatalog object. (And the Catalog will contain the list of FiskProfile objects.)

(A FiskCatalog MUST contain a list of FiskProfile objects. In the "serviceProfileList" property. The FiskDigest structure inherits from the FiskCatalog structure, so it may also contain the list.) Note that the "FiskProfile" is written in JSON, not Javascript.

Remere Login Primary Goals

Primary Goals:

Remere Login Primary Characteristics

The primary characteristics of the Remere Login specification are:

Remere Login general process overview

Remere Login uses the Capis specification to embed data about Remote Services in HTML. When the RP web page is requested by the browser, the RP creates an HTML page that contains a special ServiceGuide HTML Element. (Defined by the Capis specification.) This HTML element contains a FiskDigest JSON object. The FiskDigest includes a unique challengeKey.

The login process is started when the user activates one of the ServiceTriggers. For example, this can be a click on a Anchor HTML Element. First, the browser will perform the Service Trigger Activation Protocol, examine the FiskTrigger object inside the trigger, and depending on the contents, it will then start the Remere User Authentication process. When Remere starts, the browser will need to connect to the user's Login Manager (if it has already not done so). The browser will communicate with the Login Manager, to get a list of Login Items for the current website. Then it will display the list to the user. The user can then select which Login Item to use. Upon selection, the browser will again communicate with the Login Manager to get the proof-of-identity (generated from the User Login that was selected) and then it will send the proof to the RP website (at its Service Endpoint) in a RML Request.

Note that this process does not use an HTML form. The user does not enter any data into a website form. The browser does not send the RP a form submission. Instead, the browser creates and sends a Service Request to the RP's Service Endpoint. This eliminates user data entry, and thus will allow websites to support using longer, more complicated, login keys during the login process, without requiring users to remember or type anything complicated. (Note that a website is a type of relying party. An "RP".)

The Remere-Capis login process is similar to Webauthn, but there are many differences.

Some major differences between Remere (which uses Capis) and Webauthn:

This login process is similar to Webauthn, but there are many differences. Among them are (1) It eliminates forms on websites, (2) It does not use JavaScript. (Instead, it embeds a FiskDigest JSON object.) It does not require a web page to have JavaScript enabled. (3) It defines a transport format for the proof-of-identity, the RML Request. (4) The Service Request can contain multiple Credential Proof. (i.e. It supports Multi-factor authentication.) (5) The Service Request can contain an operation ID. A trigger can be used to invoke any of the different operations at the RP. (i.e. Any of the RML Operation List, including "login", "logout", "proveUserPresence", "setActiveCredentials" i.e. "change password".) (6) The embedded FiskDigest can be used to activate other types of protocols, besides User Authentication.

Primary Goals

The primary goals of the Remere Login specification are:

There needs to be a way for HTML to mark which HTML elements are used to perform User Authentication. This is useful for password managers, screen readers, accessibility, (for the blind, etc.). Password managers should not have to guess which HTML elements (which forms) are used for User Authentication.

To meet these primary goals, and all the other design goals, a pair of specifications were created. The Capis specification and Remere Login. (i.e. The login process was split into two different specifications.)

Capis Specification Overview

The Capis specification is a way to specify Remote Services and Service Triggers in HTML files, without using JavaScript. (It embeds a FiskDigest JSON object.) It is a general format specification, it does not include any User Authentication API. It can be used to embed any type of Service Trigger.

The Capis specification does a few things. (1) It provides a way for websites (RPs) to embed FiskProfile data and Service Triggers in HTML markup, without using JavaScript. (It embeds a FiskDigest JSON object.) (2) It defines the process of how a user can invoke a Protocol Handler by activating a Service Trigger. This is called the Service Trigger Activation Protocol. (3) It provides a way for websites to categorize different types of services. (i.e. Each service specifies a FiskProfile.protocolUsed.) (4) It provides an easy way for browsers to support multiple types of services. (A browser can implement a Protocol Handler.)

Capis, by itself, does not do anything. In particular, it does not perform User Authentication. Capis must be used together with a Protocol Handler (implemented in the browser) to perform any actions. (Such as Remere Login, or WebAuthn or some other protocol, to perform User Authentication.)

In the Login process, the delivery of the login values to the website must be done using a protocol and a login value format that the website understands. So the format must either be a standard, or the website must publish some type of FiskProfile or a FiskCatalog that contains what protocols it understands. The Remere Login specification defines one such protocol and format (The RML Request). Websites are free to prefer or require other protocols (such as WebAuthn), but this is dependent on browser capabilities. Browsers should support at least the base level protocol. (Note that WebAuthn is another such user authentication protocol. It can be used with or instead of Remere Login.)

The Capis specification includes the ServiceGuide HTML Element, ServiceTrigger HTML Element, some JSON formats, and a browser protocol, called the Service Trigger Activation Protocol. (The JSON formats include the FiskTrigger, FiskDigest, and FiskProfile formats.)

The Capis specification is a way for websites (i.e. RPs) to define their own Remote Services and embed FiskProfile values and Service Triggers in HTML markup. (i.e. FiskDigest and FiskTrigger object.)

In Capis, the website explicitly declares, in the ServiceGuide HTML Element, how it wants its services to be invoked. The Service Trigger Activation Protocol causes the browser to read the FiskTrigger object, create a ServiceOperationArgs object out of them, and then call CapisControl.invokeServiceOp. The CapisControl.invokeServiceOp function will then invoke the Protocol Handler for the requested service. The handler then performs the requested operation. In general, the handler reads the FiskProfile for the service (The set of these are in the a FiskDigest), then prompts the user appropriately (if necessary), then it creates and sends a Service Request to the Service Endpoint and awaits a Service Response. (Capis is known as a declarative ServiceTrigger HTML Element API.)

Capis, by itself, does not do anything. Capis must be used together with a Protocol Handler to perform actions. (Such as Remere Login, or WebAuthn to perform User Authentication.)

Capis has both new browser requirements, and new website requirements.

The Capis specification defines the following items.

These abilities allow a website to embed different Service Triggers in its HTML. These can include a "login trigger". This allows a login action to be triggered with a button or link, etc. Since the trigger does not have to be a form.

Remere Login Overview

Remere Login is a way to use Capis to perform User Authentication.

Remere Login is a specification separate from, yet dependent on, the Capis specification. It is a Protocol Handler that does one type of User Authentication. In order to be used in a browser, the browser must implement the Protocol Handler and the RP must implement the Remote Service. (The RP's HTML web pages must include a ServiceGuide HTML Element. The ServiceGuide must contain a FiskDigest object, and the FiskDigest must contain a FiskProfile that describe a Remere Login Remote Service.) The Remere Login Protocol Handler is called by the Service Trigger Activation Protocol if the Service Trigger contains a Remere Login FiskTrigger.protocolId property value. (Service Protocol Id). (When the FiskTrigger.protocolId property specifies a "remere" Service Protocol Id, then the CapisControl.invokeServiceOp API function will call the RemereLogin.performServiceOp function.)

Remere Login defines the following.

Remere Login specifies how the browser can contact the user's Login Manager and present the UserLogin items to the user. (By using the LoginManager API.)

(Remere Login is known as a declarative HTML login API.)

Goal Summary

The primary goals are:

See more details in the Design Goals.

No passwords

Passwords are insecure. They should not be given to websites, as storing passwords has proven to be too much for websites to do securely. Instead of passwords, browsers should send websites a single-use "Credential Proof" to prove a user's identity. (Note: This specification sends a Proof Of Identity, A.K.A. a UserId Assertion, to a website. This is is a combination of a "username" and multiple Credential Proof in a single JSON object.) Note that sending a Credential Proof requires that the browser be connected to some type of Login Manager where it can get the data from. (Because the needed data is much longer and more complicated than passwords are, and users should not be required to type it into a web form. i.e. Users must not be required to type the data into a web form. A Credential Proof is created from a Credential Source.)

Instead, users store the longer security keys in a Login Manager (a more advanced version of a Password Manager) and connect it to whatever browser they are using.

Supports digitally signing user data (i.e. form submissions)

The specification supports the digital signing of arbitrary data (i.e. form contents) by the user. This mechanism provides a general means for users to digitally sign contracts or other documents with credentials specific to an RP website. A digital signature allows the RP website to prove, internally or to other entities (the public, etc.), that the user authorized the data. This mechanism can be used to digitally sign documents, create signed statements and comments (prevents editing by a host platform. blog, twitter, etc.), perform verifiable organization voting, etc. (Note that for use outside the RP, the RP website must also have a way to prove that the Credential Verification Rules used to verify the signature were authorized by the user and active during the time when the signature was created.) (See Form Signing and RemereLogin.performServiceOp(opId, opArgs, fiskProfilePath, triggerView) and ServiceOperationArgs.signContents and opId="signForm".)

Comparison to WebAuthn

The first several goals are the same as Webauthn. Do not require users to type anything, Do not use passwords, Use some type of Login Manager. Require browsers to connect to an external Login Manager. But Capis adds several other requirements. (1) Eliminate login forms on websites. (2) Allow the login button in browser chrome. (login integration into the browser chrome.) (3) A Login Manager can be remote, or a program installed on another device. (Users should not be required to buy a separate physical device to store passwords or be required to carry it around with them.) (4) Allow a remote connection to an online Login Manager. (5) Must not require JavaScript. (Although a Javascript browser API is *also* provided, for more advanced use cases.)

See further RML Comparison To Webauthn

Why prompt the user to enter their password, if the goal is to not have them type anything. Browsers should not be required to enable Javascript for websites. Therefore, the login API shoud not require Javascript.

Remere Login Overview Part 2

The Remere Login specification is designed to make the process of users signing into websites easier and more secure. In order to do this, it contains both new browser requirements, and new website requirements.

It is expected that the required browser functions will be implemented natively in browsers in the future. However, a browser extension may be created that provides some of the required functionality.

The Capis specification defines a trigger process, consisting of multiple steps, called the Service Trigger Activation Protocol. The Remere Login specification defines a specific User Authentication process, known as Remere User Authentication. The Remere specification defines a browser API, that gives browsers a common set of User Authentication functionality that websites can invoke to do the collection of the User Authentication credentials. This allows websites to use the new standard, instead of having to do it themselves. (i.e. Instead of a website being required to display a login form and ask the user to enter their username and password, the website can invoke the new browser API, and the browser will do it in a standard way. The browser will collect the "username and password", or equivalent, and deliver it to the website. The browser may collect it automatically from a password manager, without user intervention.) There is a similar browser API for when a user wants to "logout". Using this process lets browsers know the Remere Login Status of a website, if it is logged in or not. The browser can display a Login Status in the UI, etc. Also, because the process has distinct steps, it allows browser extensions to hook into the process at different locations to customize the process. (i.e. To obtain the credentials from a different source, etc.)

The Remere specification assumes that new commands and options will be presented to the user as part of the browser UI. (i.e. As a menu item displayed in the browser menu bar. Not limited to HTML elements in a web page.) The browser can have several different "login" menu items. (Perhaps displayed in a drop-down list.) The options would include (1) Display a Remere Login Status icon, indicating if the current page is logged in or not. (2) Display the current default "Passbook" (and the Login Manager that it is from). (The browser may have a setting to automatically use the default, unless the user does something to get the full list of UserLogin items.) (3) A list of browser commands. Including "login" and "logout" of the current web page, "logout of all" web pages, "change the default profile" that is currently being used, etc. (4) A list of what Login Managers are connected to the browser, and/or (5) An option to connect to a new Login Manager.

This specification does the following, in order to automate the authentication process.

The website (or other entity) that wants to authenticate its users is referred to as the "Relying Party". (i.e. an "RP")

Specification Design Philosophy

This specification was designed to automate the User Authentication process. Its overriding design philosophy is to first streamline the existing sign-in process (remove special cases), and then automate it. Specifically, this includes:

Note that the design eliminates all of a user's custom interactions with RP websites, but it does not eliminate the user interactions with the browser. When the user clicks a special HTML anchor, the browser may pop-up a window and ask the user to make some selection. The benefit is, this user-interaction is isolated to a single party (the browser). This is much better than the user having custom interactions with every website.

Websites need users to login. This means they need proof of user identity and user presence. HOWEVER, websites should not implement their own solution to the problem. Instead, browsers should provide a standard API, and all websites should use it. Thus, instead of users being required to do data entry for EACH website, a user can prove their identity and presence ONCE, to the browser (really, to the Login Manager). The browser can then, for a limited time, be able to sign-in to multiple websites mostly automatically. (Requires a single user click. But no data entry.) Each website may have different requirements (usernames and passwords etc.), but the "Login Manager" has all of that information, and the browser has a standard way to send that data to websites. Once the Login Manager is unlocked, the browser can generate a "proof of identity" for any website that is contained in the Login Manager. And it can send the "proof of identity" to the website along with any other requirements.

Overview

Capis Specification brief overview

The following summarizes the Capis specification. (Also see Summary2.) Remere Login summary

Overview of the Authenticator

This specification requires that users store their User Logins (i.e. usernames and passwords, etc.) in a "User Authenticator". An Authenticator can be a separate physical device, or it can be software installed on a user's mobile phone, or it can be a remote service, accessed over the internet. An Authenticator must support at least one of the standard connection protocols, so that it can communicate with browsers. (i.e. via NFC, Bluetooth, WiFi, USB, or over the internet as a Web Service.) In this way, users can take their Authenticator with them, or access it remotely, and use it with any browser that supports this specification.

In order to login to websites, a user must "connect" their Authenticator to the browser once per browsing session, and prove their identity to the Authenticator. (Using a fingerprint, password, etc.) The act of connecting a browser to the user's Authenticator can be called "signing in to the browser". After connection, a user can login to websites with a single click. Without typing anything. The browser will send the Authenticator a user authentication request for the website, and the Authenticator will create a "proof-of-identity" in response. Then the browser will send the proof-of-identity to the website as part of a login RML Request.

Overview of the changes to HTML

This specification defines a few new HTML attributes to be added to existing HTML elements. These attributes can be used to create a ServiceGuide HTML Element and a few ServiceTrigger HTML Elements. (Trigger examples are the Anchor HTML Element, and Form HTML Elements. The Form is rarely used.) These attributes allow RPs to embed FiskProfile data within HTML markup. (i.e. Descriptions of Remote Services. User authentication is one such remote service.) RP websites should modify their HTML pages to add a ServiceGuide HTML Element, and then replace a login form with an Anchor HTML Element.

The new attributes are: name="serviceguidedata" and the content='{}' attribute. The name="serviceguidedata" attribute contains a FiskDigest JSON object. RP websites can use this attribute to give the browser additional data or instructions. Such as user authentication information.

A Capis compliant HTML page MUST have a ServiceGuide HTML Element in it. The purpose is to allow the browser, in the future, to use the hidden HTML element to display the user authentication information in the browser chrome. (i.e. The RP login status and command buttons.) The items do not have to be visible in the HTML page at all. For example, the browser chrome can contain a "login" button, and a "logoutAll" button, etc.

The ServiceTrigger HTML Elements behave differently than normal. For example, if a Anchor HTML Element is clicked, browsers that understand this specification will perform the Service Trigger Activation Protocol. (i.e. The browser will perform the CapisControl.invokeServiceFromTriggerHtmlElement.) The browser will not use the "href" attribute of the Anchor HTML Elements. The "href" attribute is used exclusively as a "fallback" behavior. Only older browsers that do not understand Capis will use it. (i.e. If the browser does not understand the new attribute rel="servicetrigger".)

Example HTML. HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
The mainFiskCatalog.json contents =
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

The FiskCatalog Overview

The RP must publish a special FiskCatalog document that details the Remote Services that the RP offers. This is a JSON document that describes the RP's public services. In particular, it must include the User Authentication service, and its requirements. See FiskCatalog Detail.

Service Trigger Activation Flow - Overview

The Service Trigger Activation Flow exists so that it can be used by multiple services. It is not limited to User Authentication. (See Remere Login Flow overview for an example of User Authentication.)

In general, the act of making a Remote Service call has four parts. The Service Trigger Activation Protocol has the following steps.

more details

The CapisControl.invokeServiceOp function will search the Page Configuration for the proper FiskProfile to use. Either one that matches the FiskTrigger.protocolId or the specific FiskProfileId. (If a profileId is specified.) It will search through all the FiskDigest objects. (Search through the serviceProfileList inside each FiskCatalog.)

The FiskTrigger.protocolOp property contains the opId. The FiskTrigger.protocolId property contains the Service Protocol Id. The FiskTrigger.offerId property contains the FiskDigestId. The FiskTrigger.profileId property specifies the FiskProfileId. (The FiskProfile contains the Service Protocol Id in the FiskProfile.protocolUsed.)

For example, if the FiskTrigger object is {"protocolOp":"remere/login"}, then the protocolId is "remere" and the opId is "login". When the trigger is activated, the browser will look up the FiskProfile that has the id "remere_1". (In the Page Configuration.) The browser will then extract the Service Protocol Id in the FiskProfile (the protocolUsed), and lookup the associated protocol_handler.
In this case, (due to the naming convention of "remere_1"), the FiskProfile protocolUsed will likely specify the Service Protocol Id value "remere". The protocol_handler is Remere Login. Therefore, the browser will call the RemereLogin.performServiceOp API to start the "Remere Login flow".

Remere Login Flow Overview

See the "Remere Login Flow Detail" for more detail.

The short version.

The detailed version.

When a "user authentication" opId is started, the browser will try to communicate with the active Login Manager. If there is not an active connection to a Login Manager, then the browser will prompt the user to connect to (or login to) a Login Manager. A connection to a Login Manager can expire or otherwise become invalid. If the user has been inactive for some amount of time, or if the RP requests it, then the Login Manager can require that the user re-authenticate himself (to the Login Manager), before the Login Manager will generate the Proof of Identity. (This optional RP request is one of the primary actions, and is called "proveUserPresence".)

RML Comparison To Webauthn

The Remere Login specification defines a login process and HTML integration for the browser. An RP's Capis integration may use the WebAuthn specification as one of the supported login implementations. The differences between Remere Login and WebAuthn are:

Specification Design Goals

Primary Design Goals

The primary design goals are: The primary goals of this specification are:
  1. Automate user authentication. Allow users to login to websites with a single click.
  2. Users should be able to sign into websites with a few clicks. (No typing. No data entry.)
  3. Eliminate the need for users to remember multiple passwords.
  4. Allow users to "use any browser" and "take their password device, etc. with them".
  5. Standardize the use of better security practices in browsers and websites.
    i.e. Prevent websites from storing raw passwords, and thereby eliminate the possibility of passwords being compromised through website breaches.
  6. Prevent browsers from sending raw passwords to websites. Instead, it uses public keys or hashes. This prevents websites from storing raw passwords.
  1. Users must be able to store their login credentials in a "Login Manager" external to the browser. (A Login Manager can be an online service, or a physical device the user carries, etc.)
  2. Users must be able to connect to their Login Manager with any browser when doing sign-in.
  3. Browsers must be able to connect to an external "Login Manager". This should not require installing a browser extension.
  4. The RP websites must be able to inform browsers about its User Authentication requirements. Including any custom settings.
  5. Browsers must be able to get the User Authentication requirements for the RP website.
  6. Browsers must be able to create and send a proper "login request" to the RP website. Including the sign-in information required by the website. (Using the required format and protocol.)
  7. Browsers must know the "Remere Login Status" at each website. (This means that a login and logout RML Response MUST be sent from the RP website and parsed by the browser.)
  8. Browsers must be aware of each User Authentication RML Request, (i.e. login, logout, etc.) and each corresponding RML Response from the RP website.
  9. Browsers must provide a standard "login prompt" for RP websites to use.
  10. The "login prompt" should not be an HTML form. Login forms should be eliminated. (Could use a special HTML element, etc.)
  11. The "login prompt" must be specific and exclusive. The browser MUST not have to guess which HTML elements and/or forms are used for authentication. It must not have to guess what conditions will trigger the login prompt.
    If the "login prompt" is part of the HTML page, then there must be a standard way for an RP to mark which HTML elements it uses to trigger the login process. (i.e. the "login prompt".) (It must not be like the current way things are done, using a regular, unmarked, HTML form to submit the username and password.)
  12. Activation of the special "login prompt" must cause the browser's internal login function to execute.
  13. The "login prompt" should work without JavaScript. Therefore, it should probably be a special HTML element. Dedicated to doing login / User Authentication.
  14. Browsers should have a "login function" that causes a "login request" to be sent to the RP website.

Secondary Design Goals

Secondary goals are:
  1. Eliminate sign-in forms on websites. Replace them with links. (Websites should never prompt users to enter sign-in data.)
  2. Work without requiring any Javascript
  3. Support multiple "opId" values. (Such as "login", "logout", and "proveUserPresence")
  4. Support Multi-factor authentication
  5. Browsers should provide a standard way for websites to ask for confirmation of user presence. After sign-in. (i.e. To confirm the user is still there.)
  6. Browsers should provide a "global logout" button. An easy way for a user to "sign-out from the browser".
    One button that will cause the browser to logout from ALL websites that use the credentials of the Login Manager. The browser MUST send a "user has signed-out" notification to all websites that are logged in using the user's credentials.
  7. Be extensible.
About the Service Trigger Activation Protocol
  • The process must not require the use of JavaScript on RP websites.
  • The process must be customizable. So that RP websites can adjust the process with their own customizations. (different URIs. different protocols, different formats)
  • The process must be extensible. So that it can be automated and extended in the future.
  • An RP website must be able to "declare support" for the standard.
  • The process must have discrete "steps", so that the browser (and browser extensions) can "hook into" or customize the login process.
  • An RP must be able to have confidence that the browser (or some "User Authentication Device") has confirmed the presence of the user. Not just the presence of a device. (Confirm the user by some physical or mental characteristic. Such as having them enter a password into the browser window.)
  • It must be extremely easy for websites to implement. (Eliminate the need for custom scripting.)

User Authenticator detail

A User Authenticator provides cryptographic proof of a user's identity. That is, it creates a proof-of-identity for an RP. It creates this proof from the User Login's credentials. (i.e. The credential secrets. Like a password or private key, etc.)

A Login Manager is a container that stores all of a user's login credentials. (i.e. It stores all the usernames and "Credential Secrets" such as passwords, private keys, etc.) A "Login Manager" is an advanced form of a "Password Manager". It has a different name because it can store other types of login credentials besides passwords. (Such as public/private key pairs, etc.)

An Authenticator needs access to the user's Login credentials, such as passwords, and private keys, to create a proof-of-identity from them. In other words, an Authenticator *requires the use of* a LoginManager. It needs access to the credentials, but it doesn't need to store the credentials itself. It is an additional capability beyond that of a LoginManager. But this functionality is simple enough, that most Login Managers should not have a problem supporting it.

When looking at the LoginManager API, there are 3 required Authenticator functions. collectUserLogins(), createUserLogin(), createUserIdentityProof(). (They usually accept the "fiskClientData" argument.)

An Authenticator has a few required capabilities. Primarily, it can use cryptography to generate a "proof-of-identity" for an RP. It creates this proof from the User Login's credentials. (i.e. The credential secrets. Like a password or private key, etc.) The proof-of-identity is a type of cryptographic digital signature. It proves that the Authenticator knows the credential "secret", without revealing the secret to the RP.

Second, an Authenticator must support at least one of the standard connection protocols, so that a browser can communicate with it. (i.e. via NFC, Bluetooth, WiFi, USB, or over the internet as a Web Service.) Third, it must support a specific software API. This API allows a browser to ask it to create a proof-of-identity for an RP.

A "proof-of-identity" proves who the user is, without revealing the user's password. The "proof-of-identity" is given to the RP website as part of the login request. The proof-of-identity is cryptographically signed. It also includes a timestamp, and the "challengeKey" from the website. (A "challengeKey" is a "cryptographic nonce" value.) In this way, the website can verify that the proof was newly created, and that it uses the correct "challengeKey" value. (i.e. the one that the RP created.) Because of this, the proof-of-identity will only work for one website. (The website that created the "challengeKey" value.) Also, a previous proof-of-identity will not work. This specification uses a JSON Web Signature as the proof.

(An Authenticator can contain multiple "accounts", with each account storing the User Logins for a separate person. Although this condition is rare, it is supported.)

RP Requirements for Remere Login

In order to use the new login methods, RP websites must do the following.
  1. Publish a FiskCatalog. (This details what user authentication the RP supports.)
  2. Set up a Service Endpoint. A URL to receive and process the "RML Requests" from browsers.
    (The endpoint is published in the FiskCatalog.)
  3. Store Credential Verification Rules for users. (i.e. public keys, password hashes, etc.) An RP must be able to verify the types of user authentication that it lists in the FiskCatalog.
  4. Create "fallback" pages for older browsers. Copy or move existing login forms, etc. (i.e. the login form, the password reset form, etc.)
  5. Include a ServiceGuide HTML Element.
  6. Change its HTML pages to include a few ServiceTrigger HTML Elements. (i.e. A ServiceGuide HTML Element and Anchor HTML Element.)
    One of these new attributes must be a "challengeKey" and contain a very long, unique integer value. (This is a cryptographic nonce value.)
  7. An RP MUST use independent account ids and user ids. (Enables a user id to change.)

    Explanation: A user's UserId can change. (A UserId can become invalid, compromised, retired, etc.)

    A common practice is to have a mapping from a user id to an account id. The mapping may be "one-to-one", "one-to-many" or "many-to-many". A "one-to-many" mapping is recommended, as it enables more features. It allows the RP to support Account Sharing. (This allows a user to safely share their account with other people. Where each person has their own user id.)

See RP requirements detail.

Browser Requirements for Remere Login

It is anticipated that browsers will be updated to support these requirements natively. But there will also be JavaScript libraries that will enable it in older browsers.
  1. Able to recognize when one of the new ServiceTrigger HTML Elements is activated. (When one is clicked on, etc.)
  2. Able to perform the Service Trigger Activation Protocol. (When a ServiceTrigger HTML Element is activated.)
  3. Able to extract the FiskTrigger object and a FiskDigest. (From ServiceTrigger HTML Elements.)
  4. Able to retrieve the FiskCatalog from the RP.
  5. Able to connect with a Login Manager. (i.e. a User Authenticator. An external device or remote service.)
  6. Able to query a Login Manager for a list of User Logins. (For the current website and appId.)
    See LoginManager.collectUserLogins(ulFilterOptions, fiskClientData) and performServiceOp("login", opArgs, fiskProfilePath, triggerView) examples.
  7. Able to show a pop up dialog to the user, that asks them to pick from a list of User Login items.
  8. Able to create a RML Request and encode the login values in it.
  9. Able to send the Service Request to the RP.
  10. Able to generate a QR code from the RP endpoint URL and nonce, and show that to the user. If the user asks for it. "https://domainName/path/file.json?ch=nA8rh3tfe83h37C9B2O5Zw4Yn2pV7M"
    Note: In order to allow out-of-band login, the server must be specially configured. It must index the sessions by the nonce value.
  11. For the Authenticator -- Ability to generate passwords or public keys, etc. for user credentials.
  12. For the Authenticator - Ability to generate a proof-of-identity.

Benefits

Benefits of this specification

It modifies the login process in the following ways.

Main points
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login" }'>Login</a>

More Benefits 2

As corollaries, or additional support to the above, this specification also:

The Service Trigger Activation Protocol will cause the browser to retrieve the FiskCatalog (via HTTPS), then display to the user a list of User Logins that match the Remote Service requirements. The user can choose the User Login of their choice, or choose to create a new login. New logins can be created automatically. (new, secure, public/private key pairs are generated.)

The browser will send the website an "Authentication Service Request". The request includes a login proof, and is signed by the appropriate private keys. The request is a JSON Web Signature (JWS). The JWS can also contain credential attestation, for any newly generated public keys / credentials. (If attestation is a requirement of the RP website.)

The list of newly created formats include:

Overview Old

This specification adds behavior to browsers and RPs (e.g. websites) to let users sign in with a few clicks. It eliminates manual sign-in forms on websites.

Specification Summary

This specification does the following:

  1. Defines the Service Trigger Activation Protocol.
  2. Defines the ServiceGuide HTML Element.
  3. Defines a few new ServiceTrigger HTML Elements for the Service Trigger Activation Protocol. These are used by an RP to augment a few HTML elements. These attributes do the following. 1. Mark the HTML elements that are used as triggers to do sign-in with an HTML tag. And 2. Contain a FiskDigest. (The HTML tag vastly simplifies the login process. The browser doesn't have to guess which HTML elements are used to do sign-in. i.e. Which forms submit a username and password, etc. It doesn't have to inspect form inputs or examine JavaScript for clues, etc.)
  4. It defines a standard way for an RP to declare what Remote Services it supports. The services are published in an FiskCatalog. The service definitions include the "Service Endpoints" and communication protocols used.
  5. It defines a standard format for the RML Requests that a browser sends to the RP. This includes a requested opId, and a "proof of identity", which includes a UserId and a set of user credentials. The credentials can be a mix of different types. (The ability to send multiple credentials, of different types, allows an RP to support Multi-Credential Authentication and multi-factor authentication.)
  6. It defines a standard Service Trigger Activation Protocol multi-step process for browsers to follow. It starts when a ServiceTrigger HTML Element is clicked on by the user.
  7. Remere Login. The API defines several functions, including "login", "logout", "proveUserPresence", and "setActiveCredentials" (i.e. "change password").
    This provides an easy, standard way for browser extensions, etc. to hook into the Service Trigger Activation Protocol. Thus, the user experience can be customized easily. It can modify or preempt certain actions, etc. For example, during user authentication, the browser can present additional options, in addition to the UserLogin list items. Such as allowing the user to connect to an "User Authentication Device" or to an online "Login Manager". It can use either of these sources to create proof of the user and their login credentials.
  8. It defines a standard way for an RP to declare a custom behavior for an HTML element. The Capis Trigger Behavior will cause the contained FiskTrigger.protocolOp property to be performed. (The FiskTrigger.protocolOp value should contain a opId that is one of the RML Operation List. i.e. "login", "logout", "proveUserPresence", etc.) This explicitly tells the browser what the purpose of the element is. (i.e. which opId to perform.) This allows the browser to add custom behavior based on the opId. (It may color anchors that have a "offerId":"offer_1" and "protocolOp":"remere/login" differently. etc.)
  9. It defines an optional Javascript API that browsers can implement.

Term Definitions

Relying Party (RP)
An entity that wants to authenticate a user. i.e. The system or service being logged into. For example, when a user logs into a website, the website is the relying party. (An RP "relies" on an authentication system to verify the identity of the user.)
Relying Party Identifier (rpId)
A unique identifier for a Relying Party entity. It is based on the RP's "domain name". Example: "login.example.com" It is not a URL, even though it may look like one. It MUST not include a scheme, port or username.
Multi-Credential Authentication
When an RP requires Proof of multiple credentials in order to authenticate a user. Each credential can be of any type. (i.e. password, private key signature, fingerprint, PIN, etc.) (See Multi-Credential detail.)
Multi-Factor Authentication
When an RP requires two or more "factors" to verify a user's identity. (i.e. CredentialProof of different types.) The three types of authentication factors are something you know, something you have, and something you are. Something you know can be something like a password or PIN. Something you have can be a mobile phone or a special USB key. And something you are can be something like your fingerprint or other biometric identifier. (See Multi-Factor detail.)
User Authentication Application
An Application that performs user authentication services. It is typically used by a Relying Party to provide user authentication services. It implements a User Login authentication system. Every User Login is created to use (at least one) user authentication system (i.e. a User Authentication Application). See Auth Application Id.
A Relying Party SHOULD specify an Application to perform user authentication. (At lest one. It may use more than one.) Each User Login relies on at least one User Authentication Application to perform User Login authentication operations. If a Relying Party does not _specify_ an Id for the User Authentication Application, then it uses an unspecified, or "default", User Authentication Application.
Auth Application Id (authAppId)
The unique identifier for a User Authentication Application. It identifies a specific "implementation" of a User Authentication protocol. (Such as the Remere Login protocol.) A User Login typically contains a "user-authentication-application-id", to identify the application that the User Login was created to interface with.
A authAppId may be a GUID, such as an application ID. (In Android or iOS, etc.) In a browser, the authAppId typically consists of at least two parts. (1) A Relying Party Identifier (rpId). This is typically a website domain name. (It may also contain a subdomain.) And (2) a "user-authentication-application-id". This can include a version number, timestamp, etc. Example: authAppId = "example.com/remereApp/2022010100". In the Remere Login protocol, the authAppId the RP should use (when creating a User Login) is determined by the "FiskProfile.appIdSpec" property.
Auth Application Id 2 (authAppId)
The authAppId is the id under which a Login Manager stores the User Logins for an RP, or organization. An RP tells the browser which User Authentication Application to use (to login) by setting the "FiskProfile.appIdSpec" property. By default, a User Login is only usable by a web page from the SAME domain as the authAppId. (i.e. A webpage at login.example.com cannot use a User Login for "www.example.com") However, this can be a problem with subdomains. However, a larger organization may set up multiple RPs to use the same authAppId. (Known as SSO. Single Sign On.) Access to a authAppId is validated, so that only an "authorized" RP can access User Logins that use that authAppId.
By default, the authAppId is created from the RP's origin. However, a Relying Party can override this behavior, and set the authAppId in the FiskCatalog or a FiskDigest. A authAppId MAY include an optional path. This lets user accounts at an RP maintain their own client logins if desired. (See "FiskProfile.appIdSpec".)
Client Application Identifier (capId)
The unique identifier for the Application (or Entity) that started the user authentication operation. (i.e. The Application that is attempting to login, or to create a new User Login.) The capId is used by CAPIS as an access limiter. A Client Application may attempt to use or create User Logins that use a authAppId different from its own capId. CAPIS creates a request, which includes the capId, and gives it to the Login Manager. The Login Manager may approve or deny the request, depending on the capId.
Foreign RP
Any RP that is not creator of the User Login. (The UserId and the associated User Credentials). See RmlFiskProfile.userLoginCreationRules.allowedRpIdRules and RmlFiskProfile.userLoginCreationRules.capAccessList.
User Identifier (A.K.A. UserId)
A string of characters that uniquely identifies a user to an RP. It is almost the same as a "username" (as in a "username and password"), except that it does not have to resemble a name. This is because it is generally not meant to be displayed to, or remembered by, users. It is often specified by the RP, not chosen by the user. (See UserLogin Creation Methods.) The two main types of UserId are Single-site UserId and Multi-site UserId. QIF is a format for how identifiers should be written. Also see RML Response assignedUserId.
Examples: "little_bo_peep12345", "p93w43m7", "qis:A:H:1:M8h2Dz0Wyv_lViQ4Nr0J"
Register an identifier with an RP
Having an RP associate a user with an identifier. This may be the same as User Login creation. Or the RP may change the UserId of the user. The RP "assigns" the identifier to the user, or allows the user to choose their own identifier. If the identifier is a Locked Id, then the RP must not allow it, unless the user can unlock the ID. Because that would allow someone who does not own the Multi-site UserId to create a login using the UserId. (This is a security risk). A "locked" ID is RESERVED. See UserId registration.
User Login (See LoginItem)
The data needed for a user to login to an RP. (i.e. To authenticate a user to an RP.) This generally consists of (1) The RP identifier. (i.e. The Auth Application Id, authAppId.) (2) A UserId. (3) Some set of "secrets" known only to the user. (i.e. Credential Sources. Such as private keys or passwords.) (4) A set of "secret verification data", stored by the RP. (i.e. Credential Verification Rules. This provides a way for the RP to verify that the user has the secrets.) (Note: A "username and password" is a simple case of a User Login, where the password serves as both the "secret" and the "secret verification data".) Stored User Login data may also contain some additional information for administration purposes. (Such as a lipId.) See User Login Detail.
LoginItem
Any of the items inside a Login Manager, that are stored in relation to a User Login. Typically these are the stored User Login data, and Passbooks. A Login Manager and RP may contain multiple LoginItems, with the two storing slightly different information. The RP does not need to store the RP id (for itself). The RP does need to store the "user secret verification data" (i.e. Credential Verification Rules).
LoginItem Permanent ID (i.e. a "lipId")
A unique identifier assigned to each LoginItem in a Login Manager. Example: "u34". The lipId does not change if the UserId is changed, or if the User Login is moved to a different Passbook. Both User Logins and Passbooks have a lipId.
Leaky User Login
A User Login that reveals some of its credential source secrets through bad Credential Verification Rules. (i.e. It has at least one "leaky" Credential Verification Rule.) A "username and password" is an example. The RP typically stores the password in order to validate login attempts. However, having the RP store the (unencrypted) password is unsafe. Anyone with access to the RP can copy the secret and can impersonate the user.
Single-site User Identifier
A type of UserId that only identifies a user at a single RP. At a different RP, the same UserId can be registered to a different user. It is the opposite of a Locked Id. A Single-site UserId can be written in QIF format using one of the "A:A:" types.
Examples: "little_bo_peep12345" or "qis:A:A:2:M4j-q:XSAErLq"
Multi-site User Identifier
A type of UserId that can identify a single user to multiple RP. (i.e. To all RP.) Each distinct type of Multi-site UserId must have an associated LIAV Method. The RPs use the method to determine if a connected user is authorized to use a claimed identifier. (i.e. A Multi-site UserId is a type of Locked Id.) A common example is an email address. (i.e. A Federated UserId.) It identifies a resource at an organization. And the organization provides a service to the resource.
The identifier contains or references some sort of proof, or lock, etc. that can only be unlocked by the creator or authorized user. Multi-site UserIds are the opposite of a Single-site User Identifier. They are "foreign" to every RP. (Other than its Backer. If it has one.) See Multi-Site UserId detail for more information. (1) Self-contained and (2) Federated UserId.
Examples: "someone@example.com" and "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq"
Locked Identifier (AKA Verified Access Identifier)
A type of identifier that references an "Identifier Lock". The lock is a type of test, usually a type of "input verification method", known as a LIAV Method. The lock provides a way for an entity (i.e. a user or device) to prove (to other parties) that it is authorized to use the identifier. The Identifier Lock can be Self-contained, Certified or Hosted by an organization. (i.e. The lock is hosted on the internet.) Note that a Locked Identifier is just a text string. Despite the name "Locked Identifier", there is no technical enforcement that third parties require proof from the Identifier Lock.

An email address is an example of a Locked Identifier. (i.e. user@example.com) In this case, the "unlock method" is sending email. (The email is addressed to the identifier, and sent to the identifier host, at the specified domain.) A user can prove they have access to the identifier by reading the email that was sent. Note that an email address is not an optimal identifier format, because it does not contain text that directly specifies the "Identifier Lock Id". (It does not include the word "email", etc.) Instead, the Identifier Lock Id is determined by the format the identifier is in.

A Locked Id MUST be written in a standardized format, so that it can be recognized as a Locked Id. (And so that the "Identifier Lock Id" can be determined.) The Locked Id SHOULD contain text that specifies the lock (i.e. the Identifier Lock Id). However, the lock may be specified in some other way.

A Locked Id can also specify a namespace. A namepace is used to specify a standard the Locked Id follows. A standard can define a particular URL to use with the Locked Identifier. For example, the URL to use at the contained domain name, and any required arguments. Example "A:webfinger:1:carol@example.com" (The "A" is the namespace. The "webfinger:1" is the Identifier Lock Id. The "example.com" is the Backer Id) The "A" namespace defines url="example.com/.well-known/webfinger?resource=acct%3Acarol%40example.com" as the URL. The URL is used to retrieve a document that contains the LIAV Method.

For example, an email address cannot specify a Identifier Lock Id. So, all email addresses that are used as a Locked Identifier use the same Identifier Lock Id. (i.e. The Unlock Method is sending email.) However, it is possible to use a Locked Identifier that CONTAINS an email address. Example "goodlock:46:mary4092@example.com". (The "46" specifies an "email" Identifier Lock Id.)

Systems SHOULD not allow a Locked ID to be used unless the user can prove it can unlock the ID.

Correct use of Locked Ids require compliant behavior from systems. For example, an RP must not register a Locked Id to any of its users, unless the user can prove that they can unlock the ID. Because of this requirement, Locked Ids MUST be distinguishable from other types of identifier used by the system, and the LIAV Method used by the Locked Id must be discernible. The system must be able to (1) tell if an identifier is a "Locked Identifier", and (2) verify if a user can unlock it. Using the QIF format is one way to meet these requirements. Locked Identifiers are either Self-contained or Hosted by an organization. If a Locked Identifier is used to identify a single user to multiple RP, it is a Multi-site UserId.

The User Verification process

A "user verifier" (i.e. an RP or other third-party, etc.) must be able to verify if an entity (i.e. a user) is authorized to use the locked identifier. To do this, the user must create an "identifier authorization request" document. The document is then tested that it will "unlock the ID", by calling the LIAV Method and passing the request document in as an argument. If the document passes the test, then the user is authorized to use the identifier.

The request document may be used in one of a few ways. (1) It can be given to the "user verifier", and the verifier then calls the LIAV Method. Or, (2) the user may call the LIAV Method and then give the result to the user verifier. This last case is more involved, as the result must be cryptographically signed, or encrypted, etc. in order to prove to the user verifier that it is a result from the LIAV Method. This method must be used if the request document contains a secret. (Such as a password.)

If the Locked Identifier is hosted, then the host must provide this "unlock verification". This generally is done in one of four ways. First, the host can perform the verification itself. It accepts some sort of "documents or user supplied information", such as a password, and returns some sort of success or error value. Second, the host can receive a secret message. If the user can access the secret, then they have proved access. Third, the verifier can be given the "verification method". Then the verifier can verify the documents itself. The "verification method" can be some sort of algorithm or public keys, etc. Fourth, the verifier can be given the "encryption key". The verifier can then encrypt a secret message. If the user can decrypt the message, then they have proved access. The host can limit any of these methods to only certain verifiers. If the identifier is used as a Federated UserId, then this service may be known as an Identity Provider. Or the host can provide something different.

Examples:
  • "goodlock:45:mary4092" // "goodlock" specifies the namespace, and/or the Backer organization. "45" specifies the Identifier Lock Id.
  • "qis:A:H:1:M8h2Dz0Wyv_lViQ4Nr0TN5BfJ" // Type "A:H:1" contains a base64url encoded JWK public key.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
Identifier Lock
A set of requirements or an LIAV Method that is referenced within a Locked Identifier. This functions as a type of "user authorization test". Only users that are authorized to use the identifier can unlock it.

In general, a lock can be designed for either "private access" or "public verification". If it is used for "access", then unlocking the lock can "grant access" to a restricted resource. This can be secret information, etc. If the lock is used for "public verification", then it is actually for use by an unknown entity (i.e. a third-party, or "the public") to verify if a user can unlock the identifier. A Locked Id is used for public verification. Third parties can challengeKey a user, and then verify that the Identifier Unlock Input (some documents provided by the user) contains the challengeKey value, and will unlock the identifier. The Identifier Unlock Input is verified with the LIAV Method. If the Identifier Unlock Input satisfies the test, then the user is authorized to use the identifier.

Each Identifier Lock is specified by a unique Identifier Lock Id. Each Identifier Lock Id has an associated LIAV Method.

Some other notes. A lock algorithm may include a reference to an online location, a document or a service, etc. (e.g. A Federated UserId commonly requires a Backer VM.) The document can contain public keys, or the service can issue a certificate, etc. that can be used to verify the user. When a locked identifier is used as a public identifier, an RP must not register a Locked Id to any of its users, unless the user can unlock the ID.

See Identifier Use Requirements.
Identifier Lock Id
A text string that identifies the Identifier Lock used in a Locked Id. The Lock Id also identifies the Unlock Method. Note that multiple "Lock Id" values may denote the same Unlock Method. Also note that the Unlock Method may impose additional requirements depending on the contents of the Identifier (i.e. On the payload contents). (For these cases, the LIAV Method will assemble or otherwise determine the actual lock requirements.)
Examples:
Identifier Unlock Input
A document, etc. provided by the user, that is used to unlock a Locked Id. It is usually given by a user to a third party (i.e. an RP) in order to authenticate the user. The third party will verify that the input will unlock the Locked Identifier. It usually contains a challengeKey value, provided by the third party.
LIAV Method (Unlock Method)
An algorithm or protocol, etc. that can verify if an entity (i.e. a user, device, etc.) has authorized access to an identifier. In general, it will verify if an "identifier authorization request" document (e.g. text entered by a user, etc.) will "unlock" the locked ID. (i.e. If it is a valid use code, authorization, etc. for the Locked Id.) (i.e. If it will unlock the Locked Id.) The method is known as a "LIAV Method". Each Identifier Lock will have an associated LIAV Method. It is a way of proving if the user meets the lock requirements that are referenced from within the identifier. This will verify if the user is authorized to use the identifier.

The entity performing the algorithm is known as the "unlock verifier". It may be an RP, a third party, an "authorization verifier", etc.

The LIAV Method usually verifies if some documents created by the user are valid, if they pass the lock's test or meet its requirements. The documents should also pass the test presented by the verifier. (An alternate method is that the user provide a secret, such as a password, to the LIAV Method to prove their authorization. However, this method may reveal the secret to the verifier.)

In order to work, the verifier generally requires two things. (1) The LIAV Method for the Identifier Lock Id. (2) All documents or services required by the LIAV Method. Note that the LIAV Method of the identifier is generally determined by the Identifier Lock Id. The requirement to determine the Identifier Lock Id may require that the identifier be in a standard, recognizable format. The LIAV Method is commonly defined by the "standards organization" that defined the Identifier Lock Id.

In some cases the LIAV Method may require extra documents. Such as for a Federated UserId. In this case, the verifier may need to verify that the backer documents were issued by the backer. So it may need to get the public keys for the Backer via a Backer VM.

The Locked Identifier should include a Identifier Lock Id. The id determines how the Identifier Lock is put together, and what pieces it is composed of. Some pieces can be: (1) the Identifier Lock Id, (2) the LIAV Method, and (3) the "triggerId". The triggerId allows the same lock to be used by multiple users. The triggerId specifies the user of the lock. It is one of the arguments given to the LIAV Method.

In the QIF format, the combination of the "namespace", "idClass" and "subClass" specifies the "type" of the Locked Identifier. Use this to determine the LIAV Method. (Ex: "A:F1:1" specifies a Federated UV Method.)

Examples:
  • "qis:A:H:1:M8h2Dz0Wyv_lViQ4Nr0TN5BfJ" // Type "A:H:1" contains a base64url encoded JWK public key.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
Identifier Unlock Proof
A document (obtained from a service, etc.) that contains proof that an input document (i.e. text entered by a user, etc.) is a valid key for the Locked Id. (i.e. That the input document will "unlock" the Locked Id.) That is, the input document meets the requirements of the identifier. This is also known as "Unlocking the Identifier". The proof must be verifiable to a third party. The proof is obtained by calling the LIAV Method for the Locked Id and passing the input document in as an argument. This proves that the input document passes the "user authorization test" of the Locked Id, and the creator of the input document is therefore authorized to use the identifier.
Self-contained Locked Identifier
A type of Locked Id that contains its lock inside itself, internal to the identifier. For example, the lock can be an encoded public key. In this case, the public key can be used as the "verification method", so the verifier can check itself that the user can unlock the ID. These types of Locked Id do not have a "host". They are written in QIF format using one of the "A:H:" types.
Example: "qis:A:H:1:M8h2Dz0Wyv_lViQ4Nr0TN5BfJ" // Type "A:H:1" contains a base64url encoded JWK public key.
Certified Locked Identifier
A Locked Identifier that is unlocked by a certificate, provided by a Backer. Traditionally, the Backer will publish a set of public keys. It can also create an Identity Certificate based on a user request. The certificate is used as the Identifier Unlock Proof and is given to multiple RP. An RP can use the public keys to verify the certificate was created by the Backer.
Hosted Locked Identifier
A type of Locked Id where the Identifier Lock is available on the internet. It relies on a Host Organization to implement (i.e. to host) the Identifier Lock. The Host Organization may be a single host or a network or federation. It may consist of a single Identity Provider or a network of them. (For example, a Federated UserId uses a Host Organization that is a federation.) A Hosted Locked Identifier does NOT have to contain the Host Id internally. The Host Id may be determined separately. If the Locked Identifier contains the host ID internally, then it is a Backed Identifier.

Because the Identifier Lock is hosted, the identifier must somehow be associated with the host. However, the identifier does not necessarily have to contain the host id internally. The host id may be acquired indirectly. For example, the identification system (the RP being logged into) may store the host ID for a user ID separate from the Locked Id. Or the user may provide the host Id during login. Or the host Id may be a federation of identity providers, etc. If the identifier contains the host ID itself, then it is a Backed Identifier. (In this case, the "host id" is the Backer Organization.)

For example: Some websites allow a user to "Login using Google" or "Login using Facebook". These actions use a Hosted Locked Identifier where the identifier does NOT contain the Host ID. The Host is either Google or Facebook. Those organizations host the Identifier Lock. But the userId can be an unrelated email address. (The email address does not have to contain either the "facebook.com" or "gmail.com" string, etc.)

A Backed Locked Identifier that is used as a user identifier is a Federated UserId. An email address is an example, it references a "user account" at a domain name (i.e. the host). The "lock" or test is if the user can receive email at the address.
Examples of Locked Identifiers. When in QIF format, it should probably use a type similar to "A:F1:1" or "A:F2:1".
  • "little_bo_peep12345" // An identifier without a host id. (Not recommended as a Locked Identifier.)
  • "user123@example.com" // An email address. The domain name is the host id.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
Backed Identifier
An identifier issued by an organization, that identifies an item, etc, that the organization recognizes. The identifier contains at least two things, the Backer ID of the Backer Organization that issued it, and a "registration id" of the item or event, etc. The "registration id" identifies a particular resource, account, user, or transaction, etc. at the Backer Organization. An email address is an example of a Backed Identifier.
Example: "user123@example.com" // An email address is a Backed Identifier, where the domain name is the Backer Id.
A "Provable" Backed Identifier is a Backed Identifier for which there exists some standardized way to prove an entity is authorized by the Backer Organization to access the identifier. (i.e. There exists a standardized Identifier Authorization Service for it.) A Provable Backed Identifier is a type of Locked Id. An email address is an example of a Provable Backed Identifier.
Backer Organization
An organization that creates Backed Identifiers. Every Backer Organization is identified by a unique ID, called a Backer ID. Every Backed Identifier contains the ID of the backer organization that created it. A domain name is an example of a Backer ID. An email address is an example of a Backed Identifier. (someone@example.com)
Backer Document Verification Method
A document or service provided by a Backer or a delegated third-party, such as a Identity Provider, that can be used to verify documents were issued by the Backer. The document or service may also provide public keys, which can be used to verify signed documents were issued by the Backer.
Backer Id
The identifier of a Backer Organization, which creates Backed Identifiers. Every Backed Identifier contains the ID of the backer organization that created it. A domain name is an example of a Backer ID.
Example: "user123@example.com" // An email address is a Backed Identifier, where the domain name is the Backer Id.
A Backer ID must be assigned by a standard system, to ensure that all Backer Ids are unique. A Backer ID may be a domain name or something similar. It may be a permanent PermanentBackerId, assigned by a different standard. All Backed Identifiers issued by a Backer must be in a standard format. This is so that RPs that receive one of the Backed Identifiers, can recognize the item as a Backed Identifier, and extract the Backer ID from it.
Permanent Backer Id
A permanent identifier for a Backer Organization. It must be assigned by some sort of standards organization. It is a way to create permanent Backed Identifiers. (These Backed Identifier do not use a domain name as the Backer Id. Therefore, they do not have the problems of domain names. Domain names are temporary.)
Example: "user123@@ACMA_org" // A Permanent Backed Identifier. Not a regular email address.
Example: The standards organization that controls namespace-C defines its own list of Permanent Backer Ids. These are called type-C Permanent Backer Ids. Example: "qis:A:F1:1:ACMA_org:M4j-q"
The standards organization should publish a single document that contains all the Backer Ids that it has assigned (along with the Identifier Authorization Service associated with each Backer Id). Each entry in the document (the Backer Id and associated service) is permanent and cannot be changed. However, new entries can be added. (Note: The Identifier Authorization Service is the same thing as the Federated UV Method for each Backer Id.)
Identifier Authorization Service
A service that determines if a requestor is authorized by a Backer Organization to use a Backed Identifier. Data from the service (typically the response) is given to a third party as proof that the requestor is authorized by the Backer Organization to use the Backed Identifier. The service is an implementation of an LIAV Method, implemented by the Backer Organization.
For example, the service may receive an "identifier authorization request" document, and issue a response containing if the request was valid. In this case, the request must contain an appropriate Backed Identifier and the required proof for it. The service response must contain proof (digital signature, etc.) of authorization by the Backer Organization that issued the identifier.
Note that email verification is another example of this service. A Backed Identifier authorization verifier (e.g. An identity verifier) can send email to the Backed Identifier (i.e. to the email address), containing a secret code. The "requestor" reads the secret code from the email and submits it to the verifier, thereby verifying that they have access to the Backed Identifier.
Identifier Authorization Provider
An entity that provides the Identifier Authorization Service for a Backed Identifier. The service receives an "identifier authorization request" document, and determines if the document is valid. (The request must contain an appropriate Backed Identifier and the required proof for it.) The response from the service is typically given to a third party as proof that the requestor is authorized by the Backer to use the identifier.
The provider must be authorized by the Backer Organization. The Backer Organization may authorize multiple providers, or it may provide the authorization service itself, in which case it is the provider. The identifier may be for an account, resource, transaction, user, or group, etc. An Identity Provider is a special case of the more general Identifier Authorization Provider.
Identifier Authorization Verifier
A service or algorithm that verifies if a user, etc. is authorized by a Backer Organization to use a Backed Identifier. For example: when a user tries to sign up (register) at an RP using a Multi-site UserId, then an RP must verify if the user is authorized by the Backer Organization to use the identifier. (Note: an Authorization Verifier will only work on a Backed Identifier. An identity verifier is more general than an Authorization Verifier. It will work on other types of identifiers. And proof-of-identity.)
Federated User Identifier
A unique identifier, used to represent an external entity, or "user". All Federated User Identifiers have a "provider". The "Federated Identity Provider" is a single organization that is responsible for authenticating a user to members of the federation. Therefore, each "Federated User Identifier" must be "linked" or associated with its "provider".

A Federated User Identifier is a type of Locked Id that uses an Identity Provider to implement its lock. (i.e. To meet the Locked Id requirements.) The identifier may or may not be a type of Backed Identifier. (i.e. The identifier may or may not contain a provider ID.) The provider may be provided separately. If the Federated User Identifier contains a unique ID specifying the provider, then it is a type of Backed Identifier. Where the "provider" is the Backer Organization. The Identity Provider may be the same as the Backer Organization.

In order to work, the verifier generally requires two things. (1) The LIAV Method for the Identifier Lock Id. (i.e. The Federated UV Method.) (2) All documents or services required by the LIAV Method. Note that the LIAV Method of the identifier is generally determined by the Identifier Lock Id. The requirement to determine the Identifier Lock Id may require that the identifier be in a standard, recognizable format. The LIAV Method is commonly defined by the "standards organization" that defined the Identifier Lock Id.

In some cases the LIAV Method may require extra documents. Such as for a Federated UserId. In this case, the verifier may need to verify that the backer documents were issued by the backer. So it may need to get the public keys for the Backer via a Backer VM. See Federated UserId detail.

The Locked Identifier should include a Identifier Lock Id. The id determines how the Identifier Lock is put together, and what pieces it is composed of. Some pieces can be: (1) the Identifier Lock Id, (2) the LIAV Method, and (3) the "triggerId". The triggerId allows the same lock to be used by multiple users. The triggerId specifies the user of the lock. It is one of the arguments given to the LIAV Method. The triggerId is the "registration id" specifying the user to the provider. (4) The provider.

In the QIF format, the combination of the "namespace", "idClass" and "subClass" specifies the "type" of the Locked Identifier. Use this to determine the LIAV Method. (Ex: "A:F1:1" specifies a Federated UV Method.)

Examples:
  • "user123@example.com" // An email address. The domain name is the host id.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
Federated UserId detail
A Federated User Id is a type of Locked Id that uses a Backer Organization to implement its lock. It is a type of Backed Identifier.

In order to work, the verifier generally requires three things. (1) The Identifier Lock Id. (2) The LIAV Method for the Identifier Lock Id. (i.e. The Federated UV Method.) (3) All documents or services required by the LIAV Method. The requirement to determine the Identifier Lock Id may require that the identifier be in a standard, recognizable format. The LIAV Method is commonly defined by the "standards organization" that defined the Identifier Lock Id. In some cases the LIAV Method may require extra documents. Such as for a Federated UserId. In this case, the verifier may need to verify that the backer documents were issued by the backer. So it may need to get the public keys for the Backer via a Backer VM. See Federated UserId detail.

The Federated UV Method implements the LIAV Method required by a Locked Id. It is the standard way for the RP to verify if the user currently connected to it is actually actually authorized to use the claimed UserId.

A Federated UV Method commonly involves validating documents issued by a third party. The party is either the Backer, or an Identity Provider working for the Backer, etc. (The document may be some sort of UserId certificate, etc.) Therefore, a Federated UV Method commonly requires the use of a Backer VM to validate the documents. The Backer VM must be "authorized" by the Backer but it may be provided by a separate entity. For example, a combination User Authentication Service may provide user verification for Federated UserIds from several different Backers.

In order to use the Backer VM, it must be "findable" by all RPs. All RPs must know the URL and any other information needed to use the Backer VM. Typically the method is published in one of three ways. (1) The Backer domain name publishes the information. (2) The standards organization for the type of UserId publishes the information. (3) The UserId type and method are well known. (e.g. Like an email address.)

All three options require the UserIds of that type to be in a recognizable format. The UserIds must either contain an explicit "type ID" for the type of Federated UserId they are, or they must be in a format where their "type ID" can be inferred. (i.e. An email address format will cause the email address "type ID" to be used. etc.) The QIF format makes this easy as it contains an explicit "type ID" for the UserId. In the QIF format, the combination of the "namespace", "idClass" and "subClass" specify the Federated UV Method. (Ex: "A:F1:1")

In order to login to an RP using a Federated User Id, a few things are needed. First, the RP must support that type of UserId.

During a login attempt, an RP will extract the "type ID" and the "Backer Id" from inside a Federated UserId. (Note that the "type ID" may be inferred from the format of the UserId. For example, an email address format will use an email address "type ID".) Then the RP must execute the Federated UV Method for the type ID, and have it validate the documents provided by the user during the login attempt. The "UV Method" algorithm may have a step that requires using the Backer VM for the Backer of the ID. If this is required, then the Federated UV Method will specify how the Backer VM must be "found".

For example, an email address is a type of Federated UserId, but it may not actually "work correctly". In more detail, the Federated UV Method to use for an email address EXISTS, it directs a special email be sent to the user. However, this may not be desirable. Sending an email to the address may not work correctly, and it also requires user interaction. (The user must click on a link in their email.) There is no standard way for an RP to find what Backer VM the domain name in the email address supports. (1) Not all domain names are listed in a standard (global) index of User Authentication Services. And (2) there is no standard way for an RP to query the domain name in the email address to find what Backer VM it supports. (i.e. There is no standard url for either the Backer VM method, or to query the domain for the Backer VM url.)

Any website may turn its user accounts into Federated UserIds. It only needs to provide (and publish) a Backer VM that RPs can use to prove if a user is registered at the website. (i.e. owns a UserId at the website.)

If RPs allow Federated UserIds to login, it is recommended that they ALSO require Credential Verification Rules (such as passwords or public keys) IN ADDITION TO the proof from the Federated UserId's Federated UV Method. This prevents existing accounts at RPs from being hijacked if a Federated UserId is compromised. Only during user signup (i.e. account creation at an RP) SHOULD the proof from the Federated UV Method be used by itself. This is used to ensure that the requested Federated UserId is actually owned by the new user.

Typically, a Federated UserId is written in QIF format, as the QIF "type" will clearly denote how the Federated UV Method issues identity proof.

Federated Unlock Verification Method
An algorithm by which an RP can verify if a user has unlocked a Federated UserId. That is, if the user meets the lock requirements that are referenced from within the identifier. The method usually verifies if some documents presented by the user are valid. (This method is a Federated UserId implementation of the LIAV Method.)

The Federated LIAV Method always involves a third party, or documents from a third party. (i.e. The Backer, or an Identity Provider, etc.) (Maybe the IdP works for all the UserIds that use a certain Backer, etc.) The Method may be some sort of "unlockable access" or UserId certificate, etc.

The user typically must know some sort of "secret" belonging to the UserId. (A password, cryptographic signature, etc.) With a Federated UserId, the user does not submit the proof to the RP. Instead, he submits it, and the claimed UserId, to a third party. The Backer, Identifier Authorization Provider or an Identity Provider that works for the type of UserId, etc. The service will examine the proof and, if valid, will create some kind of document that states that the user is valid. This document is typically some kind of digitally signed statement, such as a UserId certificate. The document is a part of the proof of identity. (It may consist of other documents as well.) The document is delivered to the browser, which sends it to the RP. The RP then has to validate the document.

The RP must trust the third-party. Or it will not matter what documents the RP receives from the third-party.

Typically, a service will only support a very limited set of identifiers. (For example, identifiers that contain the "Backer Id" of only one or two particular Backers.)

In order for RP to support Federated UserId, the RP must be able to execute the Federated UV Method for that type of UserId. Typically the method requires a Backer VM. The Backer VM is published in one of three ways. (1) The Backer domain name hosts a standard document or lookup service. (2) A standards organization published the standard document or lookup service. Or (3) The account type is well known. (e.g. Like an email address.) All three options require the "type" of the Federated UserId. The UserId must either contain an explicit "type ID" for the type of Federated UserId it is, or it must be in a format where the "type ID" can be inferred. (e.g. Like an email address.)

In the QIF format, the combination of the "namespace", "idClass" and "subClass" specifies what LIAV Method to use. This specifies the Federated UV Method. (Ex: "A:F1:1")

Another way of getting the type is if the Backer Id is written in a format of a PermanentBackerId Standards Organization.

timestamp-identifier
A combination of a timestamp and a small integer, both encoded in base64url. Together, it provides a unique identifier, even if there are multiple identifiers created in the same millisecond. It is composed of a Unix timestamp integer, encoded in base64url, and a small integer, also encoded in base64url. The integer is likely a counter of the number of that type of identifier, that were previously created in the same millisecond.
Example timestamp: "qis:A:F2:2:example.com:M4j-q:XSAErLq" // Type "A:F2:2" contains a domain name, a base64url userNum, and a timestamp-id.
The base64url string "M4j-q" = (integer) 216,154,026
The base64url string "XSAErLq" = (integer) 1599876543210 = The number of milliseconds since Jan 1, 1970. Compare to challengeTime.
Quence Identifier Format (QIF)
A specification defining a format for how a universal identifier should be written. It makes identifiers that are universally unique, and are therefore usable at multiple RPs. This includes user identifiers (a UserId), and Locked Ids. It also includes permanent user identifiers. It also includes a way to specify Single-site User Identifiers, etc.

It adds meta-information to identifiers. It requires every conforming identifier to start with a "format-prefix" followed by "type information". The "type information" specifies what "standard" the identifier follows, what payload format the identifier uses, what Identifier Lock Id the ID uses, and what LIAV Method RPs should use to verify it.

The format is composed of five sections, separated by colon characters. "qis:NID:idClass:subClass:payload". The sections are format-prefix, namespace_id, idClass, subClass and payload. (Note that the payload MAY contain additional colon characters.) The namespace_id specifies the standard that the identifier follows. (i.e. the standards organization) Each namespace defines its own types. (The types are otherwise known as "rules" and "formats".) The idClass and subClass specify the Identifier Lock Id that the Id uses. (And it also specifies the LIAV Method used by the Identifier Lock.) The combination of the namespace_id, idClass and subClass specify the Identifier Lock Id. The subClass specifies the "class" of identifier that the Backer Organization created. It can be used to specify the rules that the payload contents should follow, etc. This includes the order and encoding of any items in the payload.

The combination of the namespace_id, idClass and subClass specifies what LIAV Method should be used. (Ex: "A:F1:1") For example, the "A:F1:1" type specifies a Federated UserId type and a particular Federated UV Method to use, and the method specifies the Backer VM to use. The format allows various characteristics of the identifier to be determined. The format allows RPs to determine what Identifier Use Requirements each identifier requires.

"NID" stands for "namespace identifier". The namespace_id may include only alphanumeric, underscore and hyphen characters. It has a minimum length of 1 character.

When users want to use their own UserId value (create a login or setUserId. ["registerUserLogin", "setUserId"]), the RP needs to easily determine the type of the UserId, and if it can perform the required operations. (e.g. An RP must not register a Locked Id to any of its users, unless the user can unlock the ID.)

There are three main categories of UserId. "Single-site", "Self-contained", "Federated UserId".

The "A:A:" type specifies a Single-site UserId, while all the other types specify a Multi-site UserId. This special type allows a single database, etc. to contain both Single-site UserIds and Locked Ids.

Examples:
  • "qis:A:A:1:M4j-q" // Type "A:A:1" contains a base64url userNum.
  • "qis:A:A:2:M4j-q:XSAErLq" // Type "A:A:2" contains a base64url timestamp-id.
  • "qis:A:A:3:1234567890" // Type "A:A:3" contains a decimal integer.
  • "qis:A:A:9:little_bo_peep12345" // Type "A:A:9" denotes a "basic character string".
  • "qis:A:H:1:M8h2Dz0Wyv_lViQ4Nr0TN5BfJ" // Type "A:H:1" contains a base64url encoded JWK public key.
  • "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k" // Type "A:H:2" contains a base64url encoded COSE_algId public key.
  • "qis:A:H:3:P83_f82hdgT6B9NqL0Dz" // Type "A:H:3" contains a base64url encoded SHA-256 hash of the user's id.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq" // Type "A:F1:2" contains a type-C PermanentBackerId, a base64url userNum and a timestamp-id.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
  • "qis:A:F2:2:example.com:M4j-q:XSAErLq" // Type "A:F2:2" contains a domain name, a base64url userNum, and a timestamp-id.
  • "qis:A:F2:3:example.com:M4j-q" // Type "A:F2:3" uses "ua protocol F3". It is authenticated via a different protocol than "A:F2:1".
  • "qis:A:email:1:joe@example.com" // Type "A:email:1". Payload is an email address.
  • "qis:A:webfinger:1:joe:example.com" // Type "A:webfinger:1". Payload is a user account and domain name.
  • "qis:A:IndieAuth:1:example.com/asmith" // Type "A:IndieAuth:1". Payload is a domain name and page.
  • "qis:A:A:4:f987b543" // Type "A:A:4" contains a hexadecimal integer.
  • "qis:A:F1:4:ACMA_org:f987b543" // Type "A:F:4" contains a hexadecimal integer and a type-C PermanentBackerId.
  • "qis:A:F2:4:example.com:f987b543" // Type "A:F2:4" contains a hexadecimal integer and domain name.
The payload is the contents of the identifier. The idClass must follow the rules of the namespace. The payload must follow the rules of the subClass. (See QIF Detail.)

All QIF UserIds are reserved at RPs. An RP MUST NOT allow the creation of an account tied to a QIF UserId, without first verifying that the user has proved ownership of the UserId. This prevents the creation of accounts using someone else's UserId.

Identifier Use Requirements
The requirements an RP must meet for it to support the use of a type of identifier. (For example, for an RP to support User Authentication using the specified type of identifier.) There are three main categories. "Single-site", "Self-contained" and "Federated UserId". The "Single-site" type requires the RP to store some set of "Credential Verification Rules". (The RP can choose the number of CVR and what types to support.) The "Self-contained" type requires the RP to support the CVR type(s) contained in the identifier. The "Federated UserId" type requires the RP to remotely connect to a federated server for user authentication. (i.e. It uses a Backer VM.)

Note: With "Self-contained" and "federated" the RP can always require its own CVRs *in addition to, or instead of* those required by the UserId. When using the CVR belonging to the Locked Id to authenticate a user, these types complicate the "password reset" problem. This is because the RP does not control those CVR, and therefore cannot change them. Also, the RP must guard against unauthorized "registration" of Locked Id IDs.

UserId Registration (A.K.A. User Registration.)
A type of record at an RP that assigns a UserId to a user. The record contains the UserId and some way of verifying the user in the future. (i.e. Some set of Credential Verification Rules.) Creating a UserId registration is the same as User Login creation. (i.e. See "registerUserLogin".) The UserId in a registration can be a Single-site UserId or a Locked Id. (i.e. It can be created by the RP, or it can be created externally to the RP, and claimed by the user.)

User Login creation may not be the same as "creating an account" at the RP. (Because some RPs may require an additional step to "create an account".) It identifies a user to the RP, and allows the user to "login" to the RP again at a later date, but it may not necessarily create a separate "service account" at the RP. This is because an RP may allow multiple UserIds to share a single "service account". An RP can allow multiple users to access a common account. This is done with each user being assigned a unique UserId. (A UserId may identify a person or a device, etc.) An RP can create its own UserIds (i.e. a Single-site UserId) or allow a user to register a foreign UserId (i.e. a Locked Id). When attempting to register a Locked Id at an RP, the RP must ensure that the user can unlock the ID.

QIF Type Specifier / User Identifier Type
The "type" of a QIF formatted UserId is the combination of its namespace_id, idClass and subClass values. For example, the type of "qis:A:A:2:M4j-q:XSAErLq" is "A:A:2".
Multi-site User Login
Another name for a Multi-Site UserId. A single User Login used to identify a user at multiple RPs. (i.e. It is a single Locked Id and the associated unlock mechanism, private key, etc.) Any Multi-Site UserId, such as an email address or a "Federated UserId" etc., can be used as a Multi-site User Login. (As long as it is used at multiple RP.)
Important Note: This specification does NOT implement a Multi-site User Login, as that would result in reduced user privacy. If the same User_ID were used at multiple RPs, that would make it easy to track a user at different RPs. RP websites could collude together, or they could sell advertising statistics based on User IDs. RPs or advertising agencies would be able to easily track user activity, or correlate a user's behavior at different RPs, based on the UserId. This specification uses *site-specific* user identifiers (a different UserId at each RP) specifically to make it more difficult to track a user at different RPs.
Passbook
A way of organizing User Login Information into groups, in order to simplify the Login System for users and hide a lot of the internal complexity. A Passbook is a collection of User Logins, that together represent a single "online identity". A single Passbook can be used all over the internet, at each and every RP that exists. A Passbook provides a consistent "identity name" that can be displayed to users across different RP. They allow the Login System to hide the internal "username" stored for each online account. Using Passbooks allows users to only have to remember a few Passbook names, instead of hundreds of "usernames". See Passbook Detail.
Passbook Detail

Passbooks are a way of organizing User Login Information into groups, in order to simplify the Login System for users and hide a lot of the internal complexity. A Passbook is a collection of User Logins, that together represent a single "online identity". A single Passbook can be used all over the internet, at each and every website that exists. Passbooks provide a consistent "identity name" that can be displayed to users across different websites. They allow the Login System to hide the internal "username" stored for each online account. Using Passbooks allows users to only have to remember a few Passbook names, instead of hundreds of "usernames".

A person typically needs only one or two Passbooks, no matter how many online accounts they have at different websites. A user only needs another Passbook if they want more than one account at some websites, and they want to organize those additional accounts together in a separate group. (i.e. The new group would be a different "online identity".)

Internally, a Passbook contains a collection of User Logins, usually only one User Login for each website. (Note: A User Login is the login information for an account at a website. It contains things like a "username" and "password", etc.) The detailed information of each User Login (i.e. the "username", etc.) is mostly unnecessary to users, and should usually be hidden. A Passbook provides an "identity name" that can be displayed to users instead of the internal "username".

During login to a website, the Login System will display a list of all the valid User Logins available for the website. Each list item will display the "identity name" of the Passbook that contains the User Login. Prior to Passbooks, the list items would have to display the internal "username" stored for the account at the website. This resulted in users having to remember hundreds of "usernames".

A Passbook is a facade. It is designed to look like a single "online identity" to users. It is a way to give users the benefit of using a single "online identity" everywhere online, while not sacrificing user privacy. A Passbook looks like it contains an internal Locked Id", but it doesn't. Instead, it contains individual User Logins. It does this so that it can give each RP a different UserId. (So that the same UserId is not used at multiple RP.) This is done to preserve user privacy and prevent user tracking.

Passbook information is NEVER given to any website. (i.e. the "identity name", etc.) Passbooks are a user convenience. (For display to users.) They are only used internally to the browser and the Login Manager that stores them. User Logins can be moved between Passbooks. So they can be reorganized later. Passbooks preserve user privacy and prevent user tracking.

A Passbook should usually contain only one User Login for each website. Because that makes the display simpler. (Each additional User Login needs to be modified with a number, or disambiguation name.) User Logins can be moved between Passbooks. So they can be reorganized later.

Passbooks make the login process much easier, because the User Login list only needs to display the name of the *Passbook* that the User Login is contained in. The individual User Logins usually do not have a name. (i.e. The user can select which *Passbook* they want to use. The user does not need to know anything about the User Logins that the Passbook contains.)

Passbooks work better when they contain only one User Login for each website. Because it allows the list displayed to the user during login to be simpler. If a Passbook contains more than one User Login for a website, then that makes the list slightly more complicated, and logging into that website slightly more difficult.

If a Passbook contains multiple User Logins for the same website, that will cause nearly "duplicate entries" in the "existing User Login" list. (i.e. Multiple entries in the list will have the same Passbook name.) To prevent confusion, each of the "duplicate entries" is assigned an individual name or id number to help tell them apart, and the "duplicate entries" in the list will display the extra name. This adds another thing that users must remember.

User Tracking and Online Privacy

It would be easier for users to use the same Locked Id everywhere online. However, doing that would result in severely reduced user privacy. Because doing that would make it very easy to track user activity at multiple RPs. Currently, RP websites sell advertising by installing third party scripts on their websites, and they sell their user data to marketing companies. If users were represented by the same ID at different websites, then that would make it easy for data to be aggregated together and be used to track user activity between multiple websites.

Websites could collude together to aggregate data and track users. Websites could sell advertising statistics based on User IDs. RPs or advertising agencies would be able to easily track user activity, or correlate a user's behavior at different RPs, based on the UserId.

In order to preserve user privacy, this specification uses *site-specific* user identifiers (a different UserId at each RP) specifically to make it more difficult to track a user at different RPs. (These are likely Single-site UserIds.)

Passbook implementation

A Passbook is a special kind of group of User Logins. It contains a "Map" or "associative array" that has one "slot" for each RP. Each "slot" contains contains all the information for that RP, including one or more User Logins.

When logging into a website, the system will find the UserLogin(s) for the website that is being logged into. If there is more than one such User Login, then the system may ask the user to choose between them.

A single Passbook should not be shared between multiple users. Each user should have their own Passbook. (A user may have multiple Passbooks.) A Passbook typically contains a "name", some "master keys" (backup keys for User Logins) and a map of RP domain names (which contain the User Logins).

A Passbook has a name and an id, and possibly some other information. The Passbook ID is used in the browser to identify which Passbook is to be used. No Passbook information is ever given to any RP. A User Login is retrieved from inside the Passbook and that is used to create a "proof of identity" that is sent to the RP. A Passbook is stored in a Login Manager (or in a User Authenticator or a browser). The Passbook id is kept secret from RP websites. If websites were given a "Passbook id" as part of an identifier, then it would tell the website that the user has multiple accounts at the website, and which Passbook the user selected to login with.

If users need multiple Passbooks, then each Passbook will likely be assigned a user role. (One for each of: Work, Personal, Family, etc.)

Passbooks greatly simplify using User Logins for users. They reduce the UserLogin list items that are displayed. Passbooks, and User Logins hide allow the system to hide all individual UserIds, and instead only display the Passbook ID to the user. (This allows the actual UserId values to never be seen by the user.) This greatly simplifies things for users. For example, when logging into an RP, the system does NOT have to display to the user a list of UserIds registered for the RP. This would likely have to display to the user a list of long, 65 character alpha-numeric strings with little significance. Instead of this confusing mess, the system only displays the Login Passbooks that contain a UserId registered for the RP. As a second example, regardless of what RP the user wants to log into, the same set of Passbooks can be displayed to users. (If a Passbook contains a User Login for the current RP, then the Passbook is displayed as a login option. Otherwise the Passbook is displayed as a "create account" option.) The user selects which Passbook to login with, and the system automatically uses the contained UserId for the current RP. (This allows the actual UserId values to never be seen by the user.)

Passbooks work better with only one User Login per slot. Slots with multiple items increase the complexity when using a Passbook. For this reason, it is recommended that, most of the time, users should only store one User Login per RP domain name. This maximizes the benefits of Passbook. However, the Passbook are capable of holding multiple User Logins per RP. This allow users to store two or more User Logins for certain RPs that merit the additional complexity.

Login Manager
A piece of software that stores user authentication data (A.K.A. User Logins), necessary to login to RPs. It stores UserIds, passwords, public and private keys and any other secrets needed to login to RPs. (i.e. It stores "credential sources".) It is a more advanced version of a "password manager", because it stores more than passwords. (It can also be called a "credential manager", etc.) It allows the User Login data to be viewed and/or edited, etc. A Login Manager can easily also work as a User Authenticator. To do so, it must be able to generate a "proof of identity" and must support the connection protocols required by a Login Manager, so that a browser can connect to it. (i.e. NFC, Bluetooth, WiFi, USB, or as a Web Service.) A Login Manager can be located on a physical device such as a "Security Key", on the internet as a Web Service, or on the user's local machine, or mobile phone etc. Typically, all "credential sources" should be stored in a Login Manager, as it is more secure. And Login Managers are usually either portable or usable remotely. See LoginManager API.
User Authenticator
A software or computing device designed to generate proof of a user's identity. It must support connection protocols so that it can be connected to via a browser. (i.e. NFC, Bluetooth, WiFi, USB, or as a Web Service.) It works by receiving a request from a browser, for an RP, and returns a "proof of identity" for use at the RP. Generating a cryptographic "proof" allows it to keep any credential sources (such as private keys) secret. A User Authenticator may also work as a Login Manager, if it provides sufficient User Login and LoginItem editing capabilities. When looking at the LoginManager API, there are 3 required Authenticator functions. collectUserLogins(), createUserLogin(), createUserIdentityProof(). (They usually accept the "fiskClientData" argument.) See User Authenticator Detail.
User Authentication Device
A physical computing device that stores or generates user identity credentials. It is a type of User Authenticator. It may connect to a computer though a physical connection, or wireless communication protocol. Some examples are a USB key, or a chip on a keyring using Bluetooth, etc. A mobile phone may be used as an authentication device. i.e. as a digital key to unlock features in a different device, such as a computer, TV set, etc.
User Credential
Data given to an RP to provide proof that a user is who he claims to be. (Generically, a credential is data one entity presents to another in order to authenticate the former to the latter [RFC4949].) A User Credential must include the claimed user identifier, and some sort of proof. Some examples of a credential are a "username and password", a "username and digital signature" (i.e. the signature of a private key ), a Credential Proof, or a federated credential. The Remere Login specification recommends using User Credentials that are a Credential Proof. (That are the result of a one-way cryptographic function.)
Federated Credential
A credential that uses an "identity provider". See Federated UserId.
Authentication Assertion
Another name for a RML Request that uses the rml.opId "login" opIdReq. (Copied from FiskTrigger.protocolOp.)
Credential Source
The secret data that is used to create a Credential Proof. It is commonly a password or private key, etc. Typically, a Credential Proof is created from a "credential source" in response to a challengeKey from an RP. A Credential Proof is time-stamped and is single-use. In contrast, a credential source can be used multiple times. A User Login can contain multiple credential sources. A Credential Source is created from rml.opId "registerUserLogin" and "setActiveCredentials".
Credential Proof
A set of values, created from a Credential Source, that is given to a recipient to prove that the sender has possession (or ownership) of the Credential Source. The purpose is to prove possession of the CredSource, without revealing the CredSource to the recipient. The recipient typically stores some data (a CVR) related to the credential, used to prove the validity of the CredProof. But it does not know or store the CredSource. (The CredProof is given by the browser to the RP to prove that the user has possession of a Credential Source.) A CredProof is typically the result of a one-way cryptographic operation on a Credential Source using some unique input. (Such as a nonce value or the current time.) A CredProof is (hopefully) better than a password for a couple reasons. (1) The proof cannot be used to obtain the Credential Source. (2) A good proof algorithm will create a single-use value. (The proof must be re-created each time.) It is not valid if stored and re-used at a later date.

If a recipient can store multiple Credential Verification Rules, then each Credential Proof sent to the recipient must be labeled with the identifier for the CredSource it is for. (Each Credential Verification Rule contains an Id for the CredSource it is for.)

Single-use credentials are common in most computer systems that use public key signatures. These credentials are designed to be proof that the user has access to the specific "credential source". (i.e. The CredSource referred to in the Credential Verification Rule.) These types of credentials can also be called a "credential assertion".
Credential ID
A string of characters that uniquely identifies a Credential Source that belongs to a User Login. Example: "cd2". A credential id is also used to identify the Credential Verification Rule that matches the Credential Source, and every Credential Proof that is created from the Credential Source. (For example, Credentials are contained inside an Authentication Assertion, and sent from the browser to the RP.) A Credential Id is typically permanent, and never reused, even if the Credential Source is "deleted". (Because a Credential Id is stored by the RP, to identify a Credential Verification Rule. i.e. a public key, etc.) A Login Item exists in a Login Manager or User Authenticator. A Login Item is uniquely identified by a LoginItem Permanent ID, "lipId". The combination of a lipId and a credential id is also guaranteed unique within a Login Manager.

The Credential ID (possibly with some additions) is used several times in the RML Request to identify the Credential Verification Rule for a particular User account. In the credentialReg."cid_r" property of the credRegSec section. In the credentialProof."cid_p" property, of the "credentialProofList" section. In the "kid" value of the signature header.

Credential Verification Rule (CVR)
Data or algorithm used to determine if a credential is valid or not. It is a test, or a required condition, to determine if the credential was created from the appropriate Credential Source. (Note that in this specification, a credential is single-use. It is created from a Credential Source, which is kept secret. It is usually some type of digital signature.) There must be an associated CVR for each and every Credential Source. For example, a CVR may be, or contain any number of, a public key, a password, a certificate, an algorithm, a database record, an API call to another website, etc. In order to be useful, CVR values must be known. They can be public, or they can be stored by a verification entity (such as an RP).
CVR values are used during "User Authentication". A CVR value is usually a part of the User Authentication Credentials stored by the RP. (Or it can be public, etc.) A CVR is usually the opposite of a "credential". A CVR is a required condition, while a "credential" is proof of a condition. (However, in the case of a "password", both could be the same value.) See Remere User Authentication.
During a login attempt, an RP typically requires that a "proof of identity" must satisfy some subset of the CVR values in order to be valid.
"Leaky" Credential Verification Rule
A Credential Verification Rule that reveals credential source secrets. (i.e. A CVR that can be used to determine the secret "credential source" that it verifies.) In contrast, a "no-leak" CVR can only be used to verify a "proof", and cannot be used to determine the "credential source". A leaky CVR means there is a problem with the one-way function used to create the "proof" from the "credential source".

A regular "password" is an example of a CVR that "leaks". In order to use a "password", an RP must know the secret "credential source" (i.e. the password itself is the source). A public-key is an example of a "no-leak" CVR. In this case, the RP only knows the "public key", which is NOT the "secret". The "secret" is a private key.

Multi-Site UserId Credential Verification Rule
The Credential Verification Rules that are "tied to" a Multi-Site UserId. (i.e. A type of Locked Id.) They are applicable to all RPs. They must be available to and usable by all RPs.
For Example, with a Federated UserId, they are commonly published in a "UserId Certificate" document, that is signed by the "Backer". They can also be published at the "Backer" domain name, or the UserId certificate containing them can be delivered to the RP by the user during the authentication process. The Self-contained type contains the CVR itself. (i.e. in the payload.)
All compliant RPs must verify all "Multi-Site UserId CVR" before using them. Before allowing a user to use a UserId to "sign-up" or "register" himself. When a user tries to sign-up, or "register" using a Locked Id, all compliant RPs MUST require that the user unlock the ID. i.e. Provide valid proof of user-verification of the UserId. The RP must (1) recognize that the UserId is "a Multi-Site UserId", (2) obtain the "Multi-Site UserId CVR" for the UserId, and (3) validate that the proof the user submitted satisfies the Credential Verification method.
Credential Verification Method
A process by which a credential can be proven to be valid or not. Each credential must have an associated method by which its validity can be verified. Usually, credentials of the same "type" share the same verification method (e.g. public-key, password, federated), and each credential only has a small Credential Verification Rule to test the results of the method against. However, in advanced cases, an RP may allow a credential to define its own Credential Verification Method.
User Authentication Credentials
The collection of all data or algorithms stored by an RP to potentially authenticate a user. It includes all Credential Verification Rules, as well as all backup keys, stored by the RP for a UserId. (See opId="setActiveCredentials".)
Capis specification
This specification. See Capis definition and Capis Specification Detail. It includes the ServiceGuide HTML Element and ServiceTrigger HTML Elements.
Remote Service
A service offered by a host over the internet. Usually offered by the RP. The service requirements are specified in a FiskProfile. An RP typically publishes a list of the services it offers in a FiskCatalog. Or in the a FiskDigest in an HTML page. The document contains a list of FiskProfile objects, describing the service's abilities, requirements and Service Endpoints. "User Authentication" is one type of service that can be offered. See Remere Login. The service receives an Service Request (such as a RML Request)
Remote Service Operation
An operation offered by a Remote Service. A Remote Service can provide any number of operations. Example: See RML Operation List.
FiskProfile
A Service Configuration Profile. (Service Order Configuration Settings) Pronounced "Sock Profile". A JSON object that contains a set of ConfigSettings that describe a Remote Service. It can include capabilities, requirements, service options, and how the Remote Service is to be called. FiskProfile objects are usually defined together in a FiskCatalog. (In the serviceProfileList property.) See FiskProfile detail and RML FiskProfile.
FiskDigest
The JSON object embedded inside the name="serviceguidedata" attribute of a ServiceGuide HTML Element. A FiskDigest represents an advertisement, menu or application form by which services can be ordered. It contains a set of FiskProfile Configuration Settings. Each FiskProfile describes a Remote Service the RP offers, etc. A FiskDigest can contain a reference to a separate FiskCatalog document. (In the "serviceCatalogUri" property.) And/or it may contain any of the FiskCatalog properties directly. (Such as the serviceProfileList property.) This is because the FiskDigest structure inherits from the FiskCatalog structure. See FiskDigest Detail.
Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'>
FiskCatalog (FiskCatalog for an RP)
A JSON object, usually a JSON document, published by an RP, that describes the RP and a group of services that it offers. It must contain a serviceProfileList property, which contains a list of FiskProfile objects. Each FiskProfile object provides a description of a Remote Service offered by the RP. A FiskProfile can specify abilities, requirements, service options, and how the service should be invoked.
A FiskCatalog can also include other Configuration Settings, besides FiskProfile objects. It is written using Capis specification format. The FiskCatalog structure is inherited by the FiskDigest structure. See FiskCatalog Detail.
Soc Site Catalog
A FiskCatalog published by a site, that provides the primary list of the Remote Services that the site offers. It provides a directory listing of the services the site offers. The site does not have to be a "web site". See defaultValues.socSiteCatalogDocUri for the default location.
FiskTrigger
The JSON object embedded inside the "servicetriggerdata" attribute of a ServiceTrigger HTML Element. (For example, in a Anchor HTML Element.) It contains a set of Configuration Settings. The FiskTrigger configures the Remote Service call. See FiskTrigger Detail.
Example FiskTrigger:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
ProtocolConfig
The Protocol Configuration object. A JSON object inside the FiskCatalog.protocolConfigList. Functions as a map from a protocolId to a FiskProfile object. Contains one or more FiskProfile Id entries.
Service Request
A communication from the browser, or other agent, to a Service Endpoint, asking the RP to perform a Remote Service. For an example implementation, see RML Request.
Service Response
A return communication from the RP, to the requestor. In response to a Service Request. For an example implementation, see RML Response.
Service Endpoint
A URI (at an RP) that has a Remote Service listening to it. It listens for a Service Request and will perform the requested operation. A Service Endpoint is specified in a FiskProfile. (e.g. See endpointBlock.) Endpoints typically receive HTTP POST requests and return an HTTP response. The response typically contains a single JSON object, a JSON document, that contains either return values (such as a success code) or an error object. That describes an error. For example, see RML Response Detail.
Protocol Handler
An executable program, or browser extension, etc. that performs tasks required by the specified protocol. In general, it takes some type of arguments and returns some result. Or modifies a specified resource with the output. The Capis specification, and browsers in general, employ Protocol Handlers to perform the tasks required by various protocols. See API ProtocolHandler.
Service Trigger
An element in a computer program, used to invoke a service, particularly a Remote Service. It is commonly an HTML element, such as an anchor, button or form, but it can also be a menu item or other UI element in the browser chrome. (The trigger is usually a UI element.) For example, an HTML form is commonly used as a trigger. Submitting the form sends data to the RP, and receives a response. (In other words, it invokes the "service" at the RP.) See ServiceTrigger HTML Element, FiskTrigger.
ServiceTrigger HTML Element
Any HTML Element that can be "activated" and contains a FiskTrigger JSON object inside a special attribute. (It may also be marked as a Service Trigger. e.g. It may have the rel="servicetrigger" attribute,) For example: the Anchor HTML Element uses the "servicetriggerdata" attribute. Examples: Anchor HTML Element, Form HTML Element. See ServiceGuide HTML Element.
A ServiceTrigger HTML element differs from a regular HTML element (anchor, button and form) in that: (1) It is MARKED as a Service Trigger. (rel="servicetrigger") (2) It must contain a FiskTrigger JSON object. (The FiskTrigger must contain at least the "protocolOp" property. And maybe the protocolId property. Which contains the Service Protocol Id of the service.)
Example ServiceTrigger HTML Element:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
login form (Traditional)
A regular HTML form requesting the username and password in order to login the user. The submission of this form is commonly used as a Service Trigger.
ServiceGuide HTML Element
A Meta HTML Element (or other element) that contains a FiskDigest JSON object inside a special attribute. For example: the Meta HTML Element has the special attributes name="serviceguidedata" and content='{}'. See Meta HTML Element detail. See ServiceTrigger HTML Element. See FiskCatalog Detail.
Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.
Meta HTML Element
A Meta HTML Element with the special attributes name="serviceguidedata" and content='{}'. It is a ServiceGuide HTML Element. See Meta HTML Element detail.
Anchor HTML Element
An Anchor HTML Element with the special attributes "servicetriggerdata" and rel="servicetrigger". It is a ServiceTrigger HTML Element. See Anchor HTML Element detail.
Form HTML Element
A Form HTML Element with the special attributes "servicetriggerdata" and rel="servicetrigger". It is a ServiceTrigger HTML Element. See Form HTML Element detail.
Configuration Settings
A collection of JSON data values, published by an RP, that describe the RP, or the services offered by the RP. They are typically embedded within HTML markup. They exist to configure a browser's behavior at the RP. The JSON values are an alternative to an HTML page using Javascript to control how the browser behaves. The ConfigSettings are embedded within HTML markup as a single JSON object in a special HTML attribute. The ConfigSettings give the browser data or instructions about Remote Services without using JavaScript. This makes creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.
The ConfigSettings currently come in two types. First, as a FiskDigest object in the name="serviceguidedata" attribute of a ServiceGuide HTML Element. Second, as a FiskTrigger object. See Configuration Settings Detail.
Example ConfigSettings:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
Service Arguments
The arguments to call a Remote Service with. (To send to the Protocol Handler.) Also known as a ServiceOperationArgs object. It is passed into the CapisControl.invokeServiceOp function as the opArgs argument. They are often read from a FiskTrigger object.
CapisAPI.opArgs
The "opArgs" argument supplied to the CapisControl.invokeServiceOp function. This argument customizes what Service Request is created. It is a JSON object. It is commonly created from the FiskTrigger object of the Service Trigger HTML element that was activated. (e.g. A Anchor HTML Element.) Note that a Service Trigger HTML element does not need to be activated. The API can be called directly, and the "opArgs" can be specified by the caller.
Service Protocol Id
A text string that identifies a particular Service Protocol. (And an associated Protocol Handler.) It is often specified in the FiskTrigger.protocolId property. Example: "remere".
(It should equal the base name from the FiskProfile.protocolUsed value. Example: "remere") (Example FiskProfile.protocolUsed = "remere")
(The FiskTrigger.protocolOp property contains the opId. The FiskTrigger.protocolId property contains the Service Protocol Id.)
FiskProfileId
A text string that identifies a particular FiskProfile. (It should equal the contents of the FiskProfile.id property.)
FiskDigestId
A text string that identifies a particular FiskDigest. It is often specified in the FiskTrigger.offerId property. (It should equal the contents of the FiskDigest.id property.)
Service Operation
One of the operations that can be performed by a Remote Service.
Service Operation Identifier (opId)
A text string that identifies a particular service operation.
The "opId" argument to the CapisControl.invokeServiceOp function, identifies the operation to be performed by the Service when a Service Request is made. See OpId Detail. The list of valid values depends on the protocol used. Example: "login"
CapisAPI.fiskProfilePath (fiskDigestId, fiskProfileId)
The combination of a FiskProfileId and a FiskDigestId. Example: {offerId:"offer_1", profileId:"remere_1"}
For comparison, the FiskTrigger.protocolOp is the combination of a Service Protocol Id and a opId. Separated by a slash. Ex: "remere/login"
RML Request
A custom implementation of a Service Request, defined by Remere Login. A communication from the browser to the Service Endpoint, asking the RP to perform a user authentication service, such as "login". It consists of a single JSON object. It contains a requested user authentication opIdReq and a "proof of identity", along with any other necessary data. See RML Request Detail.
RML Response
A communication from the RP to the browser, in response to a RML Request. It consists of a single JSON object. It is an implementation of a Service Response. See RML Response Detail.
RML FiskProfile
A JSON object format that describes a Remere Login Remote Service. It is an implementation, or subtype, of the FiskProfile. See RML FiskProfile Detail.
Remere Login Status
The status of the Remere Login User Authentication at an RP. The RemereLogin.getLoginStatus API call returns this value. There are 4 values. "logged_in", "logged_out", "pending_login", "pending_logout".
The browser maintains a "Login Status" record for each known Capis authAppId item. This lets the browser perform the RemereLogin.logoutAll() function. The "login" and "logout" opId functions work together to update the "Login Status" for the current authAppId. The "login" function updates the "Login Status" after every login attempt, with the success/fail result. (It either adds a "Login Status" record, or updates the existing record.) The record contains the lipId, the domain (origin) and the "login status" value.
Proof of Identity
A set of data that proves a user is who he claims to be. It contains a "claimed" UserId and a set of Credential Proofs showing that the user knows the "secrets" for the claimed UserId. (Credential Proofs such as cryptographic signatures or passwords.) (i.e. A secret can be a Credential Source, or a private key, password, etc.) A Proof-of-Identity may also contain a challengeKey from the RP. One type of Proof Of Identity is a UserId Assertion. (A UserId Assertion uses private key Credential Sources.) See LoginManager.createUserIdentityProof().
More details.

This specification uses a UserId Assertion and a UserId certificate as the component parts of the "proof-of-identity". The JSON Web Signature is the UserId Assertion. It is sent from a browser to an RP in an "RML Request".

A "proof of identity" contains a UserId, an array of "Credential Proofs", a creation date, an expiration date, and the "audience" that it is intended for. (Note that all of the "credentials" included in a "proof of identity" must be for the same UserId.) The ability to contain multiple credentials, each of a different type, allows a single "proof-of-identity" to be used for Multi-Credential Authentication and multi-factor authentication. An RP stores some set of Credential Verification Rules for each UserId. The RP can decide which rules it requires, under which circumstances. A "proof-of-identity" contains a set of credentials. Each of the credentials must specify the "id" of which rule it is for. In order to login, the "proof of identity" must have a credential for each of the required rules. (Usually, the RP requires all the rules to be proven, so the proof should have one credential for each of the rules.)

Nonce value
A very long, unique value, only used once. It is used to verify that a cryptographic operation occurred. The FiskDigest.challengeKey is a nonce.
Capis Trigger Behavior
The new behavior defined for ServiceTrigger HTML Elements. (The Meta, Anchor and Form.) These HTML elements will perform the CapisControl.invokeServiceFromTriggerHtmlElement. The invokeServiceFromTriggerHtmlElement function will in turn call the CapisControl.invokeServiceOp function, using the FiskTrigger object as a source for the arguments. (Example: "protocolOp".) If the browser understands this specification, then a Anchor HTML Element (i.e. An HTML element marked with the rel="servicetrigger" attribute) will perform the CapisControl.invokeServiceFromTriggerHtmlElement, and then perform the Capis opId. It will not load the URL contained in the "href" attribute. The opId has a defined format, however, there are no defined values in Capis. (Note that Remere Login has defined several opId values.)
User Authentication (Generic)
The process by which a user's identity is verified. The process by which a user "signs in" to a website. For Example: The RP web page typically contains a Service Trigger, which the user activates. (Clicks an anchor, or fills out a login form and submits it.) Remere User Authentication is an example of one such process.
Remere User Authentication
The User Authentication process defined by the Remere Login protocol. By which a user's identity is verified using Capis. It relies on the Service Trigger Activation Protocol to invoke Remere. See Remere Login Flow overview.
Service Trigger Activation Protocol
Details the steps Capis-compliant browsers should implement when a ServiceTrigger HTML Element is activated. Defined by the Capis specification. See Capis Spec. A detailed overview of the process is provided in the Service Trigger Activation Flow.
User Identity Verification
The process by which a user's identity is verified. The process by which a "proof of identity" is verified. The user claims a UserId and provides a "proof of identity". The RP then verifies the user's claims are valid. If the proof is valid, then it proves that the user is who they claim to be. Verification ensures the proof is valid, was not faked or tampered with, and meets all the requirements of the RP. In particular, an RP can require multiple factors of authentication. See User Authentication Detail.
Multi-User Account Access, (A.K.A. "Account Sharing" and "Account User Access Control")
The ability for multiple UserIds to access the same account at an RP. The ability for an account to grant access to multiple authorized users. This feature allows an account owner to "share" their account with other users, without giving the others the owner's login details. (i.e. the owner's username and password, etc.) The other users are assigned an account_permission_role. The other UserIds can be people, or can be authorized devices or services. This ability is usually implemented by an account maintaining a list of approved UserId, and each UserId is assigned its own account_permission_role. This ability usually requires the RP to use a "one-to-many" UserId to account_id mapping. See RP Requirements.

This ability also provides an easy way for an RP to support Multiple-account Access. Since a single UserId can be listed as an authorized user on multiple accounts at the RP. (For example, a user can have their own account at the RP, and can also be listed as an authorized user on other accounts.) If a single UserId can access multiple accounts, then the RP may allow the user, after logging in, to decide which account he wants to access.

More

Because each user has their own UserId, it allows the RP to manage and track how much each user uses the account.

RPs MAY allow accounts to impose additional credential requirements on its authorized users. But this is uncommon. Typically, the UserId does not need any additional User Credentials to access the different accounts at the RP. (i.e. One set of credentials to login to the RP, other passwords or public keys for different accounts, etc.)

This ability also allows the same user to register at the RP with multiple UserId. The user can access their account with any UserId that is authorized. (i.e. This allows a user to change his UserId or to maintain multiple UserId.)

Multiple-Account Access
The ability for a single UserId to access multiple accounts at an RP. After a user logs into the RP, they can somehow select which account to access. This ability does not necessarily require the RP to use a one-to-many UserId to account_id mapping. (But it is an easy way to implement it.) See RP Requirements.

An easy way to implement this feature is to first support Account User Access Control. (i.e. Account Sharing.) Then, have each of the accessible accounts include the specified UserId as an authorized user. (And give the UserId an account_permission_role.) See grant access.

The RP must provide some way to allow users to select which account they want to use. Typically, this is done in one of the following ways. (1) During login. The user can include an account_id in the login request. (2) Immediately after login. The RP displays a list of what accounts the user has access to, and the user must pick one. (3) Allow switching at any time. The RP adds a link (or similar) to the HTML page, providing a way for the user to view the list of accessible accounts and select the one they want to access. (4) Multiple accounts at the same time. The RP provides an interface that allows a user to view or make changes to multiple accounts at the same time.

Authorized User
A UserId that is allowed to access an account at an RP. Typically, each account either has a single authorized user (i.e. the "owner" of the account), or has a list of authorized users. Each of the authorized users is also assigned an account_permission_role, which defines what features of the account the user can access. See Account User Access Control. Each authorized user is a UserId, and has its own Credential Verification Rules that it uses to log into the RP. (i.e. Some set of passwords or public keys, etc.) Once a UserId has logged into the RP, the RP can then allow it access to the accounts that it is authorized to access.
Attestation
A statement that a party has confirmed or authenticated that a process or output works correctly and meets the necessary requirements. When describing credentials, an attestation is a signed statement, backed by the device manufacturer, etc. stating that the User Authenticator and its process has been certified. That the authenticator has created the item (the credential) in the appropriate manner. (According to the standard.) Attestation is a promise (a guarantee) that the Authenticator, and the data that it emits, follows a standard. Attestation is also a way of identifying the creator of an Authenticator, and specifying the Authenticator's capabilities. (An RP may use the attestation data to restrict what Authenticators it will accept.)
Typically, a Login Manager is created in such a manner that it can only create credentials (key pairs, etc.) in a certain manner. The authenticator contains two special attestation items, put in the software or on the device during manufacture, by the creator (the device manufacturer, etc.). The two special items are, an attestation key pair, and an Attestation Certificate. The certificate contains the attestation public key, and is signed by the device manufacturer. Both of these attestation items are typically NOT unique. The same items are inside a large number of other authenticators of the same class. (Typically tens of thousands.) At registration time, a Login Manager is instructed to create a new key pair (for an RP website). The authenticator creates the new key pair, then it takes the newly generated public key and "signs" it with the special attestation private key. The Authenticator creates a result object (it may be a JSON object), puts in it the new public key, the signature, and the Attestation Certificate, then sends it back to the caller. The two attestation items provide proof that the newly generated public key was created in a certified manner. That it follows a certain standard.
JSON Web Signature
A JSON object that contains a "payload" string and a "signatures" array. (i.e. The array must contain one or more public key signatures.) This specification uses the "standard JWS format" and does not use the "compact format". The payload is the base64url encoded version of some original data. (Typically another JSON object.) Each "signature" can use a different signature algorithm and key. A "signature object" has three properties, a "header" JSON object, a "protected" header string, and a "signature" base64url encoded byte string. The "protected" header specifies the cryptographic algorithm. The "header" specifies the key id. (See the JSON Web Signature RFC.)
Example JWS: JWS = {"payload":"mJh48Q...", "signatures": [{"protected":"eyJhb...", "header": {}, "signature":"cC4h..."}, {}] };
Remere Login
A Service Protocol Id and Remote Service that performs User Authentication. It integrates into the browser using the Capis specification. The browser must implement the Protocol Handler and the RP must implement the Remote Service. See Remere Login Overview.
Capis (Automate Internet Service discovery)
An acronym that stands for "Common Architecture for Publishing Internet Service configuration settings". It is a specification that defines a standard way of embedding Internet Service descriptions (as JSON text) into an HTML web page. It also defines a set of associated browser APIs. This allows a browser to automatically discover and use the services. See Capis Spec.
The CAPIS specification enables the embedding of a collection of FiskProfile JSON objects in HTML. It is a way for websites to publish what services they offer, and how to configure the service calls, inside of HTML markup. It also includes a browser API to act on the FiskProfile. See Capis Spec. Capis is a way to specify website services and protocol data in HTML files, without using JavaScript. (It's JSON.) Remere Login is a way to use Capis to perform User Authentication. Capis defines the Service Trigger Activation Protocol, ServiceGuide HTML Element, ServiceTrigger HTML Elements and CapisControl API. The primary example of a Remote Service is for "User Authentication". It is called Remere Login.
Remere Login (Alternate)
A User Login specification that makes use of CAPIS to allow web browsers to automate the login process. May change the name to something else. CHUAL. Common Hawk User Authentication and Login. This is the name of the standard defining one type of authentication communication between the browser and the RP. The names and behavior of the Configuration Settings, request options, the definition of the "FiskCatalog document" and having the "authentication request" deliver a single JSON item, these are all a part of Capis.
Note that the changes to HTML are a part of the Capis specification, and NOT a part of Remere Login. The two are separate specifications. Capis can be used with an alternative to Remere Login to communicate between the browser and the RP. (Capis includes the HTML idea of a ServiceGuide HTML Element. i.e. The name="serviceguidedata" attribute that contains a FiskDigest. And that the web browser can automatically get a separate document from the RP describing how it does authentication. All communication and passing data between the browser and the RP are defined by a different specification. The "Web browser and HTML extensions to support the passing of authentication data between the browser and the RP". This is "WESPAD".
Camua
Abbreviation for "Common Architecture for Managing User Authentication". Pronounced "kam-ew-a". Includes the format for the RML Request.
Identity Provider
A service that provides a "user identity". That is, a UserId Certificate. The certificate is used with a Locked Id. Usually a person "signs into" the identity provider in order to receive a "UserId certificate". The user can then present the certificate to any RP as part of the User Authentication process. (The user must also present the appropriate credential(s) showing they have possession of the private keys that correspond to the public keys contained in the certificate.)
User Identifier Certificate
A digitally signed statement that contains a "Federated UserId" and a set of public keys, and binds them together. (i.e. It is a UserId binding document".) It is a signed claim from the certificate Issuer, stating that the UserId proved "ownership" of the public keys. (i.e. That the UserId knows the set of private keys that correspond to the public keys.) Therefore, an entity that claims the UserId must be able to use the private keys. A UserId Certificate is different from a regular "Public Key Certificate" in that it uses a UserId as the "owner", instead of an organization, domain name, email address, etc. A UserId Certificate should be issued by the Backer of the UserId, or by an "identity provider" that is delegated by the Backer. (Note: if possible, a UserId Certificate may also contain other types of "Credential Verification Rules" beyond public keys.) See UserId Certificate Detail.
Certificate Issuer
The entity that signed a certificate. (It can be a Public Key certificate or a UserId certificate, etc.) The issuer is supposed to verify the contents of the certificate before signing it. Users of the certificate must be able to get a sig verification keys for each signature in the certificate (if there is more than one), in order to verify the certificate. These are the public keys that were used to sign the certificate. Therefore, the public keys that were used by the issuer to sign the certificate are usually either published publicly or available from the issuer upon request. I.e. From a Backer VM.
User Identifier Assertion
A digitally signed statement that claims that the issuer of the assertion (i.e. the user or browser) is authorized to use a UserId. It provides proof that the issuer has possession of, or access to, the set of secrets (i.e. private keys) that are used in the UserId. A UserId Assertion contains the UserId, a nonce value, the date, etc. and a set of signatures of the private keys. The private keys used to sign the assertion must match the public keys that are associated with the UserId. These public keys may be stored locally by the RP (i.e. as Credential Verification Rules), or they may be bound to the UserId in a UserId certificate or other secondary document. The RML Request contains a UserId Assertion. (i.e. It contains one in the "JWS" property.) See UserId Assertion Detail.
Signature Verification Key
A public key that can be used to verify a cryptographic signature. In this specification, this is primarily used to verify two types of signatures. (1) A signature in a UserId Assertion, and (2) A signature in a UserId certificate. (The public key for the certificate was created by the Issuer of the certificate.) A signature verification key is similar to a Credential Verification Rule, except that a CVR does not have to be a public key. (It can be a password, algorithm, etc.)
WebAuthn
A specification that automates users logging into websites. It differs from Remere Login in that it requires Javascript and forms, custom coding, and that users carry around a separate physical User Authentication Device with them. See RML Comparison To Webauthn and RP requirements.
HAM-JSON
A slightly modified version of the JSON format, for use inside an HTML attribute. (HTML Attribute Modified JSON) See HAM-JSON detail. Property names and string values MAY be enclosed with the caret character (^) instead of the double quote character.

HTML Examples:

See the "FiskCatalog Detail" for an example of the FiskCatalog. It may declare support for additional verification types. (FiskProfile.credentialReg.credentialReg.ruleTypes)

The ServiceGuide HTML Element uses the attributes name="serviceguidedata" and content='{}'. A ServiceGuide HTML Element MUST contain a "challengeTime" and "challengeKey" property.

Example HTML ServiceGuide:
<head> <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> </head>
See the example_mainFiskCatalog.json document.
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login" }'>Login</a> ... <a href="remere://example.com/remereServiceOp?op=logout" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/logout" }'>Log OUT</a> ... <a href="remere://example.com/remereServiceOp?op=proveUserPresence" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/proveUserPresence" }'>Confirm your presence</a>
Example HTML Form:

Example 1: Sign a form. This form collects user info before asking the browser to sign the form.
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm" }' onsubmit="return validateForm(event)"> // User supplied data. <input type="text" name="first_name" placeholder="Enter your first name" /> <input type="text" name="user_email" placeholder="Enter your email address" /> <input type="submit" value="Sign and Submit the Form" /> </form>
The mainFiskCatalog.json contents =
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

Use of a Form HTML Element is only necessary for certain unusual tasks. Mainly this is limited to signForm. Usually, forms are NOT needed for User Login creation, as the user DOES NOT need to provide a new "username" to use with the account.

Forms can also be used to do authentication (login, password changes, etc.). This may be useful for some situations. The RP may want to do things with the UserId before the "authentication request" is sent to the RP. Perhaps the RP wants to help the user pick out which UserId to login with, validate the UserId format, etc. Maybe the RP wants to force the user to select from a list of only certain supported UserIds. When doing any of these things, custom JavaScript on the RP web page may be required.

Capis Specification Summary

  1. An RP can publish a FiskCatalog. This document describes the Remote Services the RP offers, and their URI and abilities. This includes what types of authentication the RP supports. It also explicitly declares if the RP supports the Remere Login protocol.
  2. Defines the ServiceGuide HTML Element.
  3. Defines a few new ServiceTrigger HTML Elements. (HTML Meta elements, forms and anchors can be augmented with some new HTML attributes. Such as (meta) name="serviceguidedata" content='{}', (form) rel="servicetrigger")
  4. One of the new HTML attributes is the ServiceGuide HTML Element name="serviceguidedata" attribute. It contains FiskDigest. In particular, the FiskTrigger.protocolOp property specifies the operation to be performed by the RP.
  5. The browser will perform all the "onSuccess.actions.actionItem operaions. This may include navigating the current window to the "redirectUri" address after authentication is performed.
  6. The Anchor and Form ServiceTrigger HTML Element do not use their "href" and "action" attributes, respectively. Instead, they will perform the Service Trigger Activation Protocol. They will fetch the FiskCatalog, create a "service request" (that includes a "proof of identity"), and send it to the "Service Endpoint". They do not send it to the URI in the "href" or "action" attribute.
  7. Because the "href" attributes of Anchor HTML Elements (and the "action" attributes of Form HTML Elements) are not used, they can be set to a special "fallback" URI. If a browser follows the URI, it means the browser does not support the Capis specification.
  8. The ServiceTrigger HTML Elements have a special invokeServiceFromTriggerHtmlElement behavior.
  9. The RML Request is a JSON object. It can contain multiple request properties. It also contains the "user_identity" ("request_object.crProofSection"), that contains the credentialProofList. (Which contains the various "credentialProof" objects.)
  10. The browser sends the Service Request to the "Service Endpoint" in whatever manner the RP designated in the FiskProfile. (i.e. In the FiskCatalog.) Usually the method is an HTTP POST. (The default name of the HTTP parameter is capis_request.)
  11. The defaultValues.socSiteCatalogDocUri of the FiskCatalog is a "well-known" URI. (Example: "https://RP_example.com:port/.well-known/mainFiskCatalog.json".)
  12. An HTML page MUST contain a special "ServiceGuide HTML Element", which defines the a FiskDigest. These apply to the entire HTML document. The FiskDigest can include the "serviceCatalogUri" and similar properties and the "href" attribute. These determine which "Remote Service" is used.
  13. Service Triggers (i.e. ServiceTrigger HTML Elements that are not a ServiceGuide HTML Element) can specify their own FiskTrigger object. The FiskTrigger object is used as a ServiceOperationArgs object and given to the CapisControl.invokeServiceOp function.
  14. Browsers can have special behavior for ServiceTrigger HTML Elements. The browser can display them in a special color. When a user hovers the mouse over an anchor, the browser can display some of the FiskTrigger properties. The browser can be set to use a "default" identity on a regular click, and only bring up a menu when using a right-click (or shift-click). Using a right-click, etc. would display a dialog asking the user which passbook to use, or which identity to log in as. A left-click would log in using the "default" identity.
A Capis Relying Party must implement the following:
  1. Publish a FiskCatalog.
    An RP must explicitly declare, in the form of a document hosted on their domain, that they support Capis. This document must contain a list of FiskProfile objects. The services should define the various user identifier types and Credential Verification Rule etc. that the RP supports.
  2. Create an Endpoint.
    An RP must have a working endpoint, declared in the FiskCatalog. A user's browser must be able to interact with the RP's endpoint at the time that the user is signing into a website to prove their identity to the RP and establish a session. (The path to the endpoint is given in the "endpoint.sUri" property of the RML FiskProfile.)
  3. Include a ServiceGuide HTML Element in the HTML page.
  4. Include some ServiceTrigger HTML Elements in the HTML page.
    The RP must notify the browser, by including some changes in the HTML page. These changes can be the inclusion of the new HTML attributes, or the use of the Javascript API.
A Capis primary identity authority (IDP) must implement the following:
  1. A declaration of support: An IdP must explicitly declare that they support the Capis spec, by publishing a document hosted on their domain. The contents of this document may include paths to supporting resources, as well as a path to public keys which allows for the verification of assertions generated using certificates that the primary has issued.
  2. Authentication page: A user must be able to interact with their IdP at the time that they are signing into a website to prove their identity to the IdP and establish a session.
  3. Provisioning page: A web page must be provided which is capable of provisioning a user that is authenticated to the IdP with a certificate.

RP Requirements Detail

This specification requires that all compliant RP websites use "account ids" that are distinct from user identifiers. In other words, an RP website must not use a UserId as an account_id. For example, it must not use a "username" as an account id, it must not use a Capis UserId as an account_id, etc. This enables a UserId to change, without changing the account_id, and (with an additional requirement) it enables a single UserId to access multiple accounts at the RP.

The term "UserId" refers to a string of characters that uniquely identifies a user to an RP website.

This specification requires that all RP websites have a mapping from a UserId to an account_id. (i.e. A database table, or similar, that maps from one to the other.) This is a basic requirement whenever an RP website uses UserIds and account_ids that are different from each other. When a user logs in, the user provides their UserId, then the system uses the UserId to look up the account_id. RP websites could use a simple "one-to-one" mapping or a more complicated "one-to-many" mapping between UserIds and account_ids. A "one-to-one" mapping can only hold one account for a single UserId. A "one-to-many" mapping can hold multiple accounts for a single UserId. The "one-to-many mapping can be used to allow a single user to access multiple accounts. It is common for a mapping from UserId to account_id to also contain other user information. If it does, then it can be called a "User Table". (A "User Table" is a database table that stores user information. It keeps user information separate from account information.)

All RP websites must have a mapping from UserId to account_id. A mapping is necessary because, at the very least, a user's UserId can change. (A UserId can become invalidated, compromised, retired, etc.) The one-to-many mapping is strongly encouraged. The "one-to-many" mapping is necessary for the RP to implement the "Account Sharing" feature. (i.e. the "grantAccess" opId and the "grant_user_access" request option.) The simpler "one-to-one" mapping, cannot support the "Account Sharing" feature.

  • It can not support "grant_user_access". The website will have no way for account owners to add additional "authorized users" to the account. So a user cannot have the system create an invitation for someone else to join the account. This may be acceptable for certain RP websites. For example, small blog websites. These websites have no reason for them to allow multiple users to use a single account.
  • A single UserId cannot access multiple accounts. (Because the system is not capable of storing multiple account_ids for a single UserId.)
  • Multiple users can still access a single account. However, it is NOT possible for any of these UserIds to access any account OTHER than the single account. (Because the system is not capable of storing multiple account_ids for a single UserId.) In order to do this, the system would add an entry to the User Table, where the new UserId would map to the same account as the original UserId.
  • A user can still change their UserId. (Add an additional UserId, then remove the original UserId.)
An extra, optional feature is "grantAccess" grant_user_access. An RP website can implement this whenever they get around to it. (It requires a more advanced *User Table*.)

This specification already requires that a UserId and account_id be different. This is because users must be allowed to to change their UserId. (A UserId can be lost, compromised, etc.) If the UserId were used as the account_id, then when a UserId is changed, then the account_id would also change. This is a very bad idea. And would have many complications and edge cases. Because of this, using a UserId as the account_id is not advised. It is a basic requirement that the account_id be independent of any UserId.

RP websites should already be using a mapping, from a "username" to an account_id. At least, this is the way that usernames should currently work at the RP website. (This assumes that the website implemented usernames correctly, and it did not use the "username" as the account_id.) Usernames are just another type of UserId. The username to account_id mapping could be either one-to-one or one-to-many, both will work. However, using the one-to-many mapping is strongly encouraged, as it will allow Account Sharing. Note that it is possible for RP websites to use both usernames and Capis UserIds concurrently. They are just two different types of UserId, mapping to the same account. Therefore, a Capis UserId database table can be used as a drop-in replacement, or an addition to, a username and password setup.

In order for an RP website to implement "grant_user_access", it must do the following.
  • Update the User Table to support a "one-to-many" relationship between UserIds and account_ids.
  • Implement some sort of *account switching* in its web pages. So that users can access the different accounts they have access to.
  • Provide a way for account owners to add additional "authorized users". Probably allow them to create invitation codes.
  • Provide a way for users to be added to an existing account. i.e. A way for users to use an invitation code and be added as an "authorized user" to an existing account.

    This could be done by user IDs that are unknown to the website (and have no account), or by user IDs that already have access to an account at the website. (They could be the owner, or an "authorized user" of the account.)

How to upgrade the User Table.
  • The *User Table* must be made "one-to-many", instead of one-to-one. A single UserId can be used to access multiple accounts.
  • The "User Table" needs to hold all the information about the UserId. For example, it needs to hold the public key, the backup key, and all status codes. (if the UserId is locked or compromised. etc.) Therefore, the UserId information must be moved from the account table to the User Table.
  • The User Table must have a *permission level* or *account_permission_role* for each UserId and account_id combination. Possible levels could include: owner, full, read-only, restricted1, restricted2, none.

Some Ideas for Possible Remere Login Additional Features

Features
  • Temporary access on any computer. The Login Manager can temporarily authenticate a computer for 2 hours. (login and passwords will expire after 2 hours)
  • Automated public key change (i.e. Automated password change for a website.)
  • Can enable multi-factor login
  • Can mark a device as "trusted", and use single login (Not multi-factor login)
  • A
  • A

For Automatic public key change, the changed password (private key) is encrypted locally (on the user's computer) before syncing with the user's password manager, never allowing the password manager to access the raw data. Just like all other data on the user's Password Manager account.

Can mark a device as "trusted". A random, unique identifier is created for that device. It is then encrypted using Windows "crypt protect data" functions. It encrypts it based on the user's credentials. So that another user of the device cannot decrypt it. A hash of the ID is stored in the Password Manager server database. During login, the ID is decrypted on the device, a hash is created, and sent to the login server.

For mobile devices, a randomly generated id is stored in secure phone storage.

Remere Login User Authentication Flow - Detailed walkthrough

Here are the detailed steps. (For example, in order for a user to sign-in.)

  1. The browser will retrieve the FiskCatalog for the website. (via HTTPS)
    In the document is the list of FiskProfile objects. The browser will find the appropriate one. This FiskProfile describes what types of authentication the website supports, i.e. what it needs to automatically log the user into the website. The ServiceProfile should provide as much information as possible, in order to narrow the choice of the user's credentials.
  2. The browser starts the Proof Creation Algorithm in an attempt to create a "proof of identity". See API function RemereLogin.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView).
    The browser tries to create a "proof of identity" that meets the requirements of the RP's RML FiskProfile. This process uses the internal API. This process is done mostly automatically, but the browser may require the user to (1) select which UserId to use, and/or (2) login to the identity provider. (If the UserId is a "Locked Id" then it uses a "Multi-Site UserId Credential Verification Rule". This may exist in an "UserId certificate". If the "UserId certificate" stored in the browser has expired, then the user must login to the IdP.) Note that the "proof creation" process may fail, particularly if the browser does not have credentials of a type required by the website.
  3. The browser decides which User Login to use.
    The browser may have a user setting telling it which User Login to use in which situation. Or, the User Login to use can be obtained from the data that was stored locally (in cookies, in local storage, in the password manager) after a previous registration or User Login import, etc. Or it can be determined by other means such as prompting the user which ID to use. This step is encapsulated in the API step RemereLogin.buildServiceRequest.
  4. The browser creates a RML Request.
    A RML Request typically contains a single JSON object. The browser puts all the data to be sent to the RP into the object. The "request_object" contains various request properties. These include: the "proof of identity" (a.k.a. the payload"), the user_id_certificate_chain, and the credRegSec, which details desired changes to the Credential Verification Rule stored by the RP. It also includes whatever else is needed to perform the opId. is sometimes needed. (For example, a Set Active Credentials (i.e. public key change request) needs a new public key, "credRegSec", object. etc.)
    The browser extracts the FiskTrigger object, from the Service Trigger (i.e. from the HTML anchor that the user clicked). It then creates the ServiceOperationArgs object from the values. (Given to the CapisControl.invokeServiceOp function as the opArgs argument.) It also copies some of the values into the RML Request. (For example, the FiskTrigger.protocolOp and "onSuccess" configuration properties are put into the RML Request as "payload.opIdReq" and "payload.redirectUri" properties, respectively.)
    From the a FiskDigest, the "challengeKey" property is copied into the "payload.challengeKey" field in the RML Request.
  5. The browser sends the RML Request to the Service Endpoint.
    All the request data is combined into a single JSON object. (A RML Request "request_object") This includes all request options and the "proof of identity". The request is sent to the "Service Endpoint" in whatever manner the RP designated in the FiskCatalog. See RML Request Detail.
  6. The RP website receives the RML Request. (i.e. the HTTP POST)
    The RP reads the "service request" and attempts to verify the "proof of identity". If it is valid, the website performs the requested opId. (e.g. logs the user in.) It sends a RML Response (via HTTP) to the browser stating what happened. (If the opId succeeded or failed.) If the proof is not valid, the RP can return an error. Otherwise the RP can return a success code. In addition to the "success code", the RP can return a redirectUri, or it can direct the browser to refresh the current page or part of the page. (So that the "opId":"login" anchor becomes a "opId":"logout" anchor. etc.) The RP can simply return a simple "success" status by itself, without any extra data. The simple "success" status will cause the browser to do the default behavior, to refresh the current page.
  7. The browser receives the RML Response.
    If it is a "success" response, the browser usually just refreshes the page. This will cause the page to refresh, and to display any previously restricted material. If the response was "failure", then the browser will display the HTML document delivered with the response. (The document returned with a "failure" code should be an HTML document containing some error message to the user.)

Capis opId Detail

RP services can perform various operations. The Operation to be performed by the Service is specified in the Operation Identifier. (The "opId".) Each protocol defines it's own List of valid Operation Identifiers. (See RML Operation List.)

The FiskTrigger.protocolOp property contains the opId. The FiskTrigger.protocolId property contains the Service Protocol Id.

For example, if the FiskTrigger.opId is "login", then the opId is "login". (If the profileId is "remere_1", then the use of "remere_1" likely means the Service Protocol Id of the FiskProfile is "remere".)
The service method may be one of the RML Operation List values.
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>

RML OpId Detail

A Remere Login Service "Operation Id" is an action that the browser requests the RP to perform. The Method value is specified by the browser and sent to the Service Endpoint in the RML Request. In other words, the Method is the operation that the browser asks the RP to perform. The currently spported values are listed in the RML Operation List.

The opId value is usually specified in the FiskTrigger.protocolOp property. (This is in the "servicetriggerdata" attribute of an ServiceTrigger HTML Element.) The opId value is sent to the RP in the RML Request. (The opId value is copied into the opIdReq property.)

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials" }'>Set Active Credentials</a>
See the example_mainFiskCatalog.json document.

RML Operation List

The list of User Authentication opId values:

These values can be sent to the RP in the RML Request opIdReq property. The value is typically specified in the FiskTrigger.protocolOp property.

RML OpId - Value: "login"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to log the user in. The handler will EITHER create a login package and a proof of identity and send it to the RP, or it will create a new User Login.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>

The browser will select a UserId to login with, and send a proof of identity to the RP in the RML Request. The request tells the RP to login the user with the supplied information.

The RML Request.opIdReq value will be EITHER "login" or "registerUserLogin".

RML OpId - Value: "logout"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to log the user out. The handler will create a logout package and send it to the RP.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=logout" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/logout", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Logout</a>

The RML Request.opIdReq value will be "logout".

The RP should tear down the user's session or redirect the user. The RP needs to at least refresh the page to remove sensitive information.

RML OpId - Value: "loginToggle"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to perform a login toggle operation. If currently logged out, then it will send a "login" request and a proof of identity to the RP. Otherwise it will send a "logout" command.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=loginToggle" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/loginToggle", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>

The RML Request.opIdReq value will be EITHER "login" or "logout".

If currently logged out, then the browser will select a UserId to login with, and send a proof of identity to the RP in the service request. The request tells the RP to login the user with the supplied information.

RML OpId - Value: "proveUserPresence"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to perform a "user-presence" operation. Will send proof of user presence to the RP. (force authentication)

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=proveUserPresence" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/proveUserPresence", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Confirm your presence to see the next page</a>

The RML Request.opIdReq value will be "proveUserPresence".

Tells the browser to send proof of user presence to the RP. Similar to a login. The proof requires user interaction. (force authentication)

This is to prove to the RP that the user is currently using the computer or device. (The user has to touch something or type something.) This takes the place of an RP asking the user to type in their password again, before displaying sensitive information such as credit card numbers. The RP can require proof of user presence at any time.

The "proveUserPresence" value in the RML Request.opIdReq property tells the RP to confirm the "proof of user-presence" results and then to re-authenticate the user. (forced authentication)

The RP may require that user presence be checked before displaying sensitive information. (Before allowing credit card numbers to be changed, etc.) To force the user to confirm their presence, the RP can omit the sensitive data on the page, until the user presence is proved, and then refresh the page or a part of the page.

Proof of user presence is generated by the browser, or by an "authentication device". Such as a Login Manager. This has the device authenticate the user presence through some sort of signature or biometric, etc. The proof contains a timestamp when the user presence was generated. The same proof of user presence can be used at multiple RPs. An RP should generally accept a proof of user presence if the timestamp is less than 2 minutes old.

Possible methods of checking for user presence

A list of all possible ways to prove user presence.

RML OpId - Value: "registerUserLogin"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to create a new User Login. The handler will create a "User Login creation" package and send it to the RP.

The RML Request.opIdReq value will be "registerUserLogin". (This command is alternately known as "sign-up". From "login/sign-up".)

The request may contain the new "User Login" information. (The RP can supply a username, or the browser can create one.)

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=registerUserLogin" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/registerUserLogin" }'>Sign Up/Register</a>

IMPORTANT: The "registerUserLogin" FiskTrigger.protocolOp value should generally not be used in HTML pages. The "registerUserLogin" value should generally only be used in the opIdReq property of a RML Request. In HTML pages, it is almost always a better idea to use the "login" value. This is because using the "login" FiskTrigger.protocolOp value can cause EITHER the "login" or the "registerUserLogin" value in the opIdReq property of the RML Request. Using a "login" FiskTrigger.protocolOp value leaves the decision of which action to perform up to the browser and/or the user. (Using "registerUserLogin" in an HTML page removes the choice of "login" or "registerUserLogin" from the popup shown to the user.)

Warning. It is usually better to use "login" rather than the "registerUserLogin" value in the HTML FiskTrigger.protocolOp. (i.e. "remere/login") Because it gives the browser and/or the user the opportunity to *decide* what to do. It can *either* perform a login or create a new User Login. For example, the browser can check if the Login Manager has no stored credentials for the RP, and the user can choose to use one of the existing user logins, instead of simply creating a new User Login.

The "registerUserLogin" value in the RML Request.opIdReq property tells the RP to create a new account for the user, with the supplied information. (Tells it to create a new account using the supplied UserId and the Credential Verification Rule.) (In the future, the user should be able to login with the supplied UserId and appropriate credentials.)

The "RmlFiskProfile.userLoginCreationRules.userIdTypes" property of a service limits what type of identifier can be used to create an account. (e.g. "single_site_userId", "qis:A:H:9:ri", etc.) The property is in the "FiskProfile".

See the full Login Method List.

In order to create an account, the browser must use one of the RmlFiskProfile.userLoginCreationRules.createMethods.

RML OpId - Value: "placeHold"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to disable the user's account at the RP. (For security) The handler will create a "placeHold" package and send it to the RP.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=placeHold" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/placeHold", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Place Hold</a>

The RML Request.opIdReq value will be "placeHold".

Tells the browser to send a "placeHold" request to the RP. Tells the RP to disable logins by the user id. The RP should not allow a login from the UserId until the hold has been removed. ("releaseHold") This can be activated when the user suspects that their password (etc.) has been compromised. Note that this only disables the UserId, it does not necessarily lock or disable the account. (There can be other UserIds that can log into the account.)

RML OpId - Value: "releaseHold"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to re-enable the user's account at the RP. The handler will create a "releaseHold" package and send it to the RP.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=releaseHold" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/releaseHold", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Remove Hold</a>

The RML Request.opIdReq value will be "releaseHold".

Tells the browser to send an "releaseHold" request to the RP. Tells the RP to remove the hold on the UserId. This requires sending the RP a special code, that is checked against the "backup_credentials ("bkcr")" stored by the RP.

RML OpId - Value: "signForm"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to sign the form. Only valid when used on an HTML form. The handler will create a "signed-form" package and send it to the RP.

The RML Request.opIdReq value will be "signForm". It tells the RP to receive a signed form. (It indicates that the sent value is a signed form.) (See Form Signing)

RML OpId - Value: "signData"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to sign the form. Only valid when used on an HTML form. The handler will create a "signed-form" package and send it to the RP.

The RML Request.opIdReq value will be "signData". It tells the RP to receive a signed form. (It indicates that the sent value is a signed form.) (See Form Signing)

RML OpId - Value: "setActiveCredentials"

Also known as Set User Credential Verification Rules.

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to change the user's login credentials. (i.e. passwords or public keys, etc.) The handler will create a "setActiveCredentials" package and send it to the RP.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

The RML Request.opIdReq value will be "setActiveCredentials".

This function lets the user change their password or public key. It is analogous to "change user password". If the user's private key is compromised, has a weakness, etc. the Credential Verification Rule can be changed.

The browser will create a RML Request that asks the RP to make changes to the Credential Verification Rules that it stores for the user. The command can add (register) new Credential Verification Data and select which existing CVR are active. (From an existing list.) The request contains a "credRegSec" section. The "credRegSec" section contains two lists, "existing" and "new". The first is a list of ids of existing Credential Verification Rules that should be de-activated (or deleted). The second is a list of new Credential Verification Rules that should be added.

Each of the items in the crList of "new CVR" must have a credentialReg."value (val_r)".

The "setActiveCredentials" opId cannot be used to set the UserId. It can only change the Credential Verification Rule. In order to change the UserId, use "setUserId" opId.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", "sub": "remere/setActiveCredentials", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:A:2:M4j-q:XSAErLq", // The QIF UserId "usk": "q3jC9-h" }, "user_auth_method": "stored_cred", "redirectUri": "/some/path/page.html", "useCount": 42,
/* The user_id_certificate_chain is absent when using the "stored_cred" uauth_method. // user_id_certificate_chain "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], */
// crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, // "credRegSec" credentialRegSection. Required for "registerUserLogin" and "setActiveCredentials" opIdReq. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } };

When using "setActiveCredentials", the UserId sent in both the tokens must match the existing UserId stored by the server (the RP).

Note: The credentialProof."value" value (usually a JWT) and the "signatures" are verified by using the Credential Verification Rule (CVR) stored at the RP. (i.e. the stored_cred.) (i.e. The service request is signed with the existing CVR.)

Some of the known Credential Verification Rule (CVR) are a public key fingerprint, or storing the entire public key.

The RP can create a "User Login" with one or more CVR. It doesn't matter whether the account uses a "Locked Id" or a "Single-site UserId". (Such as the username in a regular username and password.) Both types of accounts can potentially have CVR.) Or an account can be created without any CVR at all. In this case, such an account must use a Locked Id. As without a CVR, the "proof of ownership" of the Locked Id is the only way a user can authenticate themselves and login to the account. See the full "Login Method List".

Depending on what abilities the RP offers, a user can:
Example FiskCatalog:
{ "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] // Note that "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] is the default. }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] }, "requirement_section": {} }
] }
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Change your public key</a>
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", "sub": "remere/setActiveCredentials", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:A:2:M4j-q:XSAErLq", // The QIF UserId "usk": "q3jC9-h" }, "user_auth_method": "stored_cred", "redirectUri": "/some/path/page.html", "useCount": 42,
/* The user_id_certificate_chain is absent when using the "stored_cred" uauth_method. // user_id_certificate_chain "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], */
// crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, // "credRegSec" credentialRegSection. Required for "registerUserLogin" and "setActiveCredentials" opIdReq. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256", "sct":192} "header": { "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d" // "kid": "userIdPK" // "userIdPK" means the public key is in "uid". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" } ] } };

The JWT must be signed with the "current" public key.

Note that changing the "Credential Verification Rule" uses the same URL as the site login. This is intentional, so that automated tools can change the user's public key (or other CVR) on multiple sites easily. A browser can remember the URLs used to login to RPs previously. The "remembered RP endpoints" can be called sequentially. With the old CVR and the new CVR in order to replace the CVR stored for the Locked Id at every RP.

RML OpId - Value: "setUserId"

Sets the UserId value. When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to change the UserId. (Done in a single operation.) The handler will create a "setUserId" package and send it to the RP.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setUserId" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setUserId", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Change UserId</a>

The RML Request.opIdReq value will be "setUserId". The request will also contain a "credRegSec" section. It will contain the user's selected option to create the new user id. It includes the Credential Verification Rules for the new UserId.

This function lets the user change their UserId. It is analogous to "change username". This command can be used if the UserId is ever compromised, stolen, copied, etc.

The user can also change their UserId the long way. By adding a new UserId to the account, as an authorized user. Then changing the "owner" of the account to the new UserId. And then removing the original UserId.

RML OpId - Value: "grantAccess"

When used in the FiskTrigger.protocolOp, this value tells the Remere Login Protocol Handler to grant another UserId access to the user's account at the RP. The handler will create a "grantAccess" package and send it to the RP. It must include a (different) UserId for use with the account. And an account_permission_role.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=grantAccess" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/grantAccess", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Grant Access</a>

Important Note! RPs commonly do not provide ServiceTrigger HTML Elements that use the opId="grantAccess". However, this does not mean that the RP does not support the "grantAccess" opId. The Remere Login Protocol Handler in a user's browser can be used to create a "grantAccess" RML Request even if the RP does not provide such a Service Trigger.

The RML Request.opIdReq value will be "grantAccess". The request will contain a "grant_user_access" property.

This is how the RP implements the "Account Sharing" feature. Upon activation, the browser creates a RML Request that contains the "grant_user_access" property, and sends it to the RP.

The RML Request tells the RP to give the specified UserId certain access rights to the account. Account owners MUST have an AUTOMATED way of ADDING a UserId to their account, in order to change their own UserId. (Because a UserId or a "User Login" may be compromised.)

Grants or changes the level of account access given to another UserId. The other UserId may be a person or a service. Non-person users can be called "robot" users. The new UserId may be an account the user has at a different RP. Such as an aggregator account, etc.

The "grantAccess" command allows a User to add and remove authorized UserId from their account at the RP. However, it goes one step further. In addition to adding and removing UserId, it also assigns an "account_permission_role" to each authorized UserId. The basic account_permission_roles should exist at every RP. These are "owner", "full" and "none". RPs can optionally implement other account_permission_roles.

The account_permission_roles are, from highest to lowest, owner, full, manager, read-only, restricted2, restricted1, none. Users with higher account_permission_roles cannot be modified by users with lower account_permission_roles. (A user with "full" access cannot be modified by a user with "manager" access, or any lower access.)

The highest account_permission_role is "owner". An "owner" has complete, unrestricted access to everything, and can make any changes. A user with "full" access has almost the same account permissions as an owner (i.e. the account_permission_role), except that he cannot make changes to "owners". A "full" user has full account access and can add, remove and modify most authorized users, but cannot make any changes to users that have "owner" access. A user with "account-only" access can read and change all account settings, (including billing information) but cannot add, remove or modify users. In comparison, a user with "full" account access can also modify other user accounts (but not "owners").

The "none" account_permission_role is used to turn off the account access of a user ID, without removing the user ID from the account. Example: temporarily suspend access of a child, student, employee, or group member, etc. (This can be done as a type of punishment or as a response to a perceived security threat.) The "none" level is also the default level for all pending "authorized users". That is, when an "invitation code" is used by a user ID to add themselves as an "authorized user" to an existing account, those user IDs should have (1) a "pending" status, and (2) an account_permission_role of "none". A manager must explicitly change the account_permission_role of "pending" user IDs. By default they are "none".

The "account_permission_role" to give the other UserId must be specified. For example: (none, limited, read-only, manager, full) Only the "none" and "full" levels are required. The highest account_permission_role is "full". The "full" level is even higher than any "manager" account_permission_role, as "full" access only gives the UserId **access**, it usually does not include the ability to change account access of other UserIds. The "full" account_permission_role may be assigned when the account owner wants to change their UserId, and wants to grant the new "UserId" full account control.

A UserId cannot grant another UserId greater access than it has itself. Additionally, an RP can choose to restrict the "grantAccess" ability to only certain users. An RP can limit it to only a manager of the account (or even to an "owner"). So only a manager of an account can change the access rights of any other UserId.

An RP only needs to implement 2 parts of the "grantAccess" API. The account_permission_roles "owner" and "none". In other words, granting a UserId all account access, and revoking all account access from a UserId. All other account_permission_roles are optional.

OLD SPECIFICATION. These are the two abilities needed to change the account owner's UserId. (The account owner can grant the new UserId access, then login as the new UserId and revoke the old UserId access.) The account owner MUST have an AUTOMATED way of changing their UserId. So that the owner does not have to log in to every RP website to change their UserId. Instead, a script can be run to set the UserId at every RP. However, the RP SHOULD "lock" this feature and mandate additional verification. For example, it can send the account owner an email to verify that they want to grant "owner" access to the other UserIds. An RP may choose to implement additional account_permission_roles other than "owner" and "none", or it may implement such levels through some other interface. (For example, the RP can require the owner to use an HTML page, etc. to grant account access.)

An account MUST have at least one "owner". The "grantAccess" command must NOT reduce access of a UserId if that UserId is the only "owner" on the account. Such an attempt will result in an error.

Works with the "account_permission_role" option. Can choose to grant a UserId less than "owner" access.

For example: a mother may give "read-only access" to her music account to her daughter. Such access SHOULD not allow the daughter to access credit card information or make any purchases. If the daughter performs "grantAccess", nothing will happen to the mother's account, because the daughter does not have the ability to "grantAccess" to the mother's account to other UserIds.

The "new UserId" cannot be the same as the current UserId. This command can be locked for safety purposes. It may need an email verification, etc. This is also the first step in changing the UserId of the account owner. (The username the owner logs in as at the RP.)

Changes to the HTML specification. Detail

This specification defines a few changes to the HTML specification. It adds a few new HTML attributes to existing HTML elements.

List of changed HTML Elements.

These elements allow RPs to embed FiskDigest data (about Remote Services) within HTML markup. (User authentication is one such service.) RP websites should modify their HTML pages to use the new ServiceGuide HTML Element and Anchor HTML Element instead of a login form. These HTML elements have a few new HTML attributes. The attributes are name="serviceguidedata" and content='{}'. The name="serviceguidedata" attribute contains a FiskDigest. RP websites can use this attribute to give the browser additional data or instructions. Such as user authentication information. Note that using the new type of HTML element does not require JavaScript. The RP does not have to include any JavaScript code in its web pages, and the browser is not required to have JavaScript enabled for the RP.

The FiskDigest (inside a ServiceGuide HTML Element) is the primary means for RPs to communicate user authentication information to browsers within HTML markup. It is REQUIRED. A Capis compliant HTML page MUST have a proper ServiceGuide HTML Element in it. All FiskTrigger HTML elements are optional. They are designed to be used in conjunction with the FiskDigest. Because of this, there are only a few properties allowed in a FiskTrigger. These are the RML FiskTrigger properties. (Examples: FiskTrigger.protocolOp and the "onSuccess" properties.)

A Capis compliant HTML page MUST have a ServiceGuide HTML Element in it. The purpose is to allow the browser, in the future, to use the hidden HTML element to display the user authentication information in the browser chrome. (i.e. The RP login status and command buttons.) The items do not have to be visible in the HTML page at all. For example, the browser chrome can contain a "login" button, and a "logoutAll" button, etc. Alternatively, the browser chrome can contain a combined "login/logout" button. It would track the login state at the RP, and change its label and opId between "login" and "logout".

If a browser understands the Capis specification, then the ServiceTrigger HTML Elements (i.e. ServiceTrigger HTML Elements) will behave differently than normal. For example, if a Anchor HTML Element is clicked, the browser will not use the "href" attribute. It will not load the URL that it contains. Instead, it will perform the Service Trigger Activation Protocol. (i.e. The browser will call the CapisControl.invokeServiceFromTriggerHtmlElement function. That will perform the operation specified by the FiskTrigger.protocolOp property.) The "href" attribute is used exclusively as a "fallback" behavior. Only older browsers that do not understand the Capis specification will use it.

The ServiceGuide HTML Element has two special HTML attributes. The attribute name="serviceguidedata" is the tag that marks the element, and the content='{}' contains FiskDigest. It identifies the element's role, and gives it a special behavior.

In order to support older browsers, the HTML pages at the fallback URLs will need to contain traditional login forms, etc. Similarly, the Form HTML Element uses the "action" attribute as a fallback behavior. (Note: The ServiceGuide HTML Element uses its name="serviceguidedata" attribute to point to a FiskCatalog.)

Example ServiceTrigger HTML Elements:
<meta name="serviceguidedata" content='{
  "challengeTime": 1599876543210,
  "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK",
  "serviceCatalogUri": "./_capis/mainFiskCatalog.json"
}'/>
...
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/login" }'>Login</a>
...
<a href="remere://example.com/remereServiceOp?op=logout" rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/logout" }'>Log OUT</a>
...
<a href="remere://example.com/remereServiceOp?op=proveUserPresence" rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/proveUserPresence" }'>Confirm my presence</a>
...
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials" }'>Set Active Credentials</a>
...
<form ... rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/login" }' onsubmit="return validateForm1()">
...
</form>
The mainFiskCatalog.json contents =
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

Using the (rel="servicetrigger") attribute does two things. It marks the HTML element as performing a Capis Trigger Behavior, and it causes the element to behave differently. The elements will perform a special Capis Trigger Behavior instead of their normal behavior. Clicking on a Anchor HTML Element SHOULD NOT cause the browser to follow the href value. (i.e. To send an HTTP GET command to the "href" URI in the anchor.) Instead, the browser should perform the Service Trigger Activation Protocol. (i.e. Retrieve the FiskCatalog, and perform the FiskTrigger.protocolOp property specified in the name="serviceguidedata" attribute.) For user authentication, this will send a RML Request (via an HTTP POST) to the "Service Endpoint" specified in the FiskCatalog.

The Anchor HTML Elements and Form HTML Elements SHOULD NOT use their "href" and "action" attributes, respectively. Because of this, these attributes can instead be used as a trap to detect older browsers that do not support this specification. The URI used in the "href" attribute should link to an error page or a fallback operation at the RP website. (Or some other website the RP trusts to do the fallback operation.) The web service at the linked URI could attempt an alternative login operation, or display an error message to the user. Such an error message may be something like: "Your browser does not support the Capis specification. Please do xyz."

Capis HTML Attributes

This specification defines a few new HTML attributes. They can be applied to the Anchor, Form and Meta elements. (To create a ServiceGuide HTML Element.)

List of Capis HTML attributes.

The Button HTML element has a "servicetriggerdata" attribute.

Currently, only the anchor and form HTML elements can be marked as ServiceTrigger HTML Elements. The ServiceTrigger HTML Elements use a special value for the "rel" attribute. The Anchor HTML Element uses rel="servicetrigger". And Form HTML Element element uses rel="servicetrigger".

The ServiceGuide HTML Element element uses name="serviceguidedata" and content='{}'. (Note: The ServiceGuide HTML Element uses its "content" attribute to point to a FiskCatalog.)

HTML elements that are "marked" with the special HTML attribute will perform a special behavior, the Capis Trigger Behavior, instead of its normal behavior. These HTML elements DO NOT use their "href" or "action" attributes. (i.e. A Anchor HTML Element will not use the "href" attribute and will not perform a web page redirect. A Form HTML Element will not use the "action" attribute and will not perform a regular form submission.) Developers should set these attributes to an error page or a secondary "fallback" login behavior. This will detect older browsers that do not support ServiceTrigger HTML Elements.

Example ServiceTrigger HTML Elements:
<meta name="serviceguidedata" content='{
  "challengeTime": 1599876543210,
  "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK",
  "serviceCatalogUri": "./_capis/mainFiskCatalog.json"
}'/>
...
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/login" }'>Login</a>
...
<form ... rel="servicetrigger"
 servicetriggerdata='{ "protocolOp":"remere/login" }' onsubmit="return validateForm1()">
...
</form>
See the example_mainFiskCatalog.json document.

Anchor HTML Element detail

An Anchor HTML Element can be used as a ServiceTrigger HTML Element. To do this, it needs two special attributes. These attributes are used for the Service Trigger Activation Protocol.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "test":"Eat at Lefty&apos;s in \"Smallville\"!", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a>

Anchor HTML Element: List of attributes

Anchor HTML Element - Attribute: rel="servicetrigger"

Type string; Required. (A value must be specified.)
rel="servicetrigger"
Used on Anchor HTML Elements. It marks the element as being a ServiceTrigger HTML Element. It marks the element as performing a Capis Trigger Behavior. This overrides its normal behavior.

Anchor HTML Element - Attribute: href

Type string; Optional.
href="url"

The href attribute is used exclusively as a fallback for older browsers that do not understand Capis. Used on Anchor HTML Elements.

The ServiceTrigger HTML Elements behave differently than normal. For example, if a Anchor HTML Element is clicked, browsers that understand this specification will perform the Service Trigger Activation Protocol. (i.e. The browser will perform the CapisControl.invokeServiceFromTriggerHtmlElement.) The browser will not use the "href" attribute of the Anchor HTML Elements. The "href" attribute is used exclusively as a "fallback" behavior. Only older browsers that do not understand Capis will use it. (i.e. If the browser does not understand the new attribute rel="servicetrigger".)

It will perform a Capis Trigger Behavior. This overrides its normal behavior.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>

Anchor HTML Element - Attribute: servicetriggerdata (longName: "fubar")

Type Object; Required. (A value must be specified.)
servicetriggerdata='{...}'

Contains a FiskTrigger JSON object. (Contains service Configuration Settings.) Used on Anchor HTML Elements.

The "servicetriggerdata" attribute is REQUIRED if the HTML element has the rel="servicetrigger" attribute. Because the anchor MUST contain a FiskTrigger object, which MUST contain the FiskTrigger.protocolOp property.

The HAM-JSON format is a slightly modified JSON format for use inside an HTML attribute. The property names and string values MAY be enclosed with the caret character (^) instead of the double quote character. A caret character can be included by using "%caret;". A single quote character can be included by using "%sq;".

// Allow double quoted HTML attributes to contain double quotes. Ex: attr="{^prop1^:"pick up", ^prop2^:false}"
// Allow single quoted HTML attributes to contain single quotes. Ex: attr='{"prop1":"pick up Steven%sq;s food"}'
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "test":"Eat at Lefty&apos;s in \"Smallville\"!", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a>

Example HTML Anchor: using caret quote characters.
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata="{ ^protocolOp^:^remere/login^, ^test^:^Eat at Lefty's in \^Smallville\^!^, ^onSuccess^:{^actions^:[{^redirectUri^: ^/some/path/page.html^}]} }">Login</a>

Form HTML Element detail

A Form HTML Element can be used as a ServiceTrigger HTML Element. To do this, it needs two special attributes. These attributes are used for the Service Trigger Activation Protocol.

Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm" }'> <input type="text" name="first_name" placeholder="Enter your first name" /> <input type="text" name="user_email" placeholder="Enter your email address" /> <input type="submit" value="Sign and Submit the Form" /> </form>
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "test":"Eat at Lefty&apos;s in \"Smallville\"!", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a>

Form HTML Element: List of attributes

Form HTML Element - Attribute: rel="servicetrigger"

Type string; Required. (A value must be specified.)
rel="servicetrigger"
Used on Form HTML Elements. It marks the element as being a ServiceTrigger HTML Element. It marks the element as performing a Capis Trigger Behavior. This overrides its normal behavior.

Form HTML Element - Attribute: action

Type string; Optional.
action="url"
Same as the Anchor href attribute. It's used exclusively as a fallback for older browsers that do not understand Capis.
Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm" }'> <input type="text" name="first_name" placeholder="Enter your first name" /> <input type="text" name="user_email" placeholder="Enter your email address" /> <input type="submit" value="Sign and Submit the Form" /> </form>

Form HTML Element - Attribute: servicetriggerdata

Type Object; Required. (A value must be specified.)
servicetriggerdata='{...}'

See general-attribute servicetriggerdata

Meta HTML Element detail

A Meta HTML Element can be used as a ServiceGuide HTML Element. To do this it needs two special attributes. These attributes are used for the Service Trigger Activation Protocol.

The name="serviceguidedata" attribute marks the element as being a ServiceGuide HTML Element. The FiskDigest settings apply to the entire HTML page document.

Usually there should be only one ServiceGuide HTML Element in a page. (Only one that contains the name="serviceguidedata" attribute). A ServiceGuide HTML Element must be located in the page's head element. Otherwise it's not a valid HTML Element. (This is the case with all HTML Meta Elements,)

If a required value is not provided in the ServiceGuide HTML Element, then the Capis Default Value will be used.

The FiskDigest in the ServiceGuide HTML Element applies to all ServiceTrigger HTML Elements in the page. (i.e. To all Anchor HTML Element elements in the page, etc. All HTML elements that have the "servicetriggerdata" attribute, or the rel="servicetrigger" attribute. And Form HTML Element elements that have rel="servicetrigger".)

The a FiskDigest in the "ServiceGuide HTML Element" override the Capis Default Values.

The ServiceGuide HTML Element must be in the page's "head" section.

Example HTML ServiceGuide:
<head> <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "https://RP_example.com/some/path/mainFiskCatalog.json", // serviceCatalogUri is an alternate to using href // Specifies alternate FiskProfile for protocol "remere". "protocolConfigList": [{"protocolId":"remere", "serviceLineup":["remere_2"]}] }'/> ... <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> </head>
See the example_mainFiskCatalog.json document.

Meta HTML Element: List of attributes

The following attributes are defined for the "link" ServiceGuide HTML Element.

Attributes for Meta element:

Meta HTML Element - Attribute: name="serviceguide"

Type string; Required. (A value must be specified.)
name="serviceguide"
This attribute marks the element as being a ServiceGuide HTML Element. Used on ServiceGuide HTML Elements.
Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

Meta HTML Element - Attribute: content='{...}'

Type Object; Required. (A value must be specified.)
content='{...}'

Used on ServiceGuide HTML Elements.

Contains a FiskDigest JSON object. (Contains service Configuration Settings.)

This attribute could alternately be named "data-serviceguidedata". The "data-" prefixed name could be used temporarily until an actual HTML specification attribute name is standardized. Attributes that use the "data-" prefix are custom data attributes. Custom data attributes allow HTML page authors to store extra information on standard, semantic HTML elements without hacks such as non-standard attributes, extra properties in the DOM, or Node.setUserData().

The HAM-JSON format is a slightly modified JSON format for use inside an HTML attribute. The property names and string values MAY be enclosed with the caret character (^) instead of the double quote character. A caret character can be included by using "%caret;". A single quote character can be included by using "%sq;".

// Allow double quoted HTML attributes to contain double quotes. Ex: attr="{^prop1^:"pick up", ^prop2^:false}"
// Allow single quoted HTML attributes to contain single quotes. Ex: attr='{"prop1":"pick up Steven%sq;s food"}'

The name="serviceguidedata" attribute is REQUIRED if the ServiceGuide HTML Element has the name="serviceguidedata" attribute. Because a ServiceGuide HTML Element MUST contain a FiskDigest, which MUST contain the challengeKey property.

Example HTML ServiceGuide: Using double quote characters.
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a>
See the example_mainFiskCatalog.json document.

Example HTML ServiceGuide: Using caret quote characters.
<meta name="serviceguidedata" content="{ ^challengeTime^: 1599876543210, ^challengeKey^: ^8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK^, ^serviceCatalogUri^: ^./_capis/mainFiskCatalog.json^ }"/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata="{ ^protocolOp^:^remere/login^, ^test^:^Eat at Lefty's in \^Smallville\^!^, ^onSuccess^:{^actions^:[{^redirectUri^: ^/some/path/page.html^}]} }">Login</a>
See the example_mainFiskCatalog.json document.

Note that the property values do not have to be strings. They can be of any JSON type. Including a JSON object.

Default Location of the SocSiteCatalogument

The default location of the Soc Site Catalog is to use the "well-known" URI.

The "well-known" URI of the Soc Site Catalog is created by appending "/.well-known/mainFiskCatalog.json" to the HTTPS version of the page origin. Example: "https://RP_example.com:port/.well-known/mainFiskCatalog.json" (Note that the "well-known" URI uses "https", and can have a port number.)

The default location is used if the "serviceCatalogUri" attribute of the ServiceGuide HTML Element is missing or invalid.

Note that if a "serviceCatalogUri" is used that does not have a path component, (does not contain anything after the domain name. Not even a "/" character.), then the socSiteCatalogDocUri that is used also appends the "/.well-known/mainFiskCatalog.json" string.

See the other Capis Default Values.

User Login Detail

A User Login is the data needed for a user to login to an RP. (i.e. To authenticate a user to an RP.) It is also a name for the stored "user identity" at the RP. (A "user identity" may not be the same as an "account" at the RP, as multiple users may be able to log into a single account. See Account Sharing.) In general, it is the combination of (1) a unique user identifier (i.e. a UserId) at the RP and (2) some means for the user to prove ownership of (or authorized use of) the user identifier.

A User Login has at least five types of components, although some may be omitted. (1) The RP id. (Or the Auth Application Id, authAppId.) (2) A user identifier. (i.e. a UserId) (3) A set of "secrets" known only to the user. (i.e. Credential Sources. Such as private keys or passwords.) (4) A set of "user secret verification data" stored by the RP. (i.e. Credential Verification Rules. A way for the RP to verify the user has the secrets.) (5) A UserId binding document (or a database entry at the RP). This associates the UserId with the "user secret verification data". Special Note, a "username and password" is a simple User Login, where the password serves as both the "secret" and the "user secret verification data". A more advanced version can also add a "backup_credential ("bkcr")" key pair, a "login counter", and a "last use timestamp". (See "User Login").

In order to sign-in, the user generates a "proof of identity" from their secrets, and sends the proof to the RP. The RP receives the proof, retrieves the Credential Verification Rules for the UserId (by using the binding), and then verifies the proof against the CVR.

There is a type of User Login, known as Federated UserId, where the RP uses an intermediary to verify the user. This intermediary is called an identity provider. This type of User Login requires that the RP trust the identity provider. With this type of User Login, the user provides some type of proof to the RP, and the RP checks that the identity provider verifies the proof. (Note that the user proof contains the UserId. Also, the UserId typically contains (or otherwise references) the domain name of the identity provider.) The user proof can be verified in 2 ways. (1) The RP submits the user proof to the identity provider, and lets the identity provider validate the proof. (This gives the UserId to the identity provider, allowing it to track when it is used, and by which RP.) Or (2) the RP can verify the user proof itself, using the identity provider's public keys, etc. (i.e. This requires that the user proof contain digital signature(s). The RP retrieves the set of public keys from the identity provider. It does not give the identity provider the UserId.) In this case, the user proof must include a document that "binds" the UserId to a set of "Credential Verification Rules". And this document must be signed by the identity provider. (It may be indirectly signed, and include a certificate chain. etc.) This document is typically a UserId certificate. It can be minutes old, or months old. It gives the RP the Credential Verification Rules, and then the RP can evaluate if the user provided proof-of-identity meets the requirements of the Credential Verification Rules.

Storing User Logins at an RP.

An RP must store User Logins in order to authenticate its users. An RP may store User Logins either inside individual accounts, or outside of accounts, in a "User Table" (it may give it some other name). The downside to storing User Logins inside accounts is: (1) A user must identify the account they are trying to access before logging in. (2) It does not allow Account Sharing. For maximum effect, the RP will need to store User Login information *outside of* accounts. (in a User Table inside a database, etc.) This is one way of providing two features. It provides a way for an RP to allow multiple users to access the same account, and it provides a way for an RP to allow a single user to access multiple accounts. For both of these features, each user will need their own UserId and Credential Verification Rules. Rather than storing these in multiple locations (in multiple accounts), the User Login can be stored in a location distinct from the accounts.

User_id Binding Method. Binding CVR to User_id

A "binding method" is a means of proving an association between a UserId and a set of Credential Verification Rules (such as public keys). It is a required part of a User Login. A simple example of a "binding method" is an entry in a database at the RP, where the RP stores a UserId and a list of the associated Credential Verification Rules. Another example is a UserId Binding Document.

User_id Binding Document

A small document that contains a UserId and a set of Credential Verification Rules. This associates the two items (the UserId and the various CVR) and "binds" them together. It is an example of a "binding method". In the case of such a "binding document" created by one entity and used by another, the document must contain a cryptographic signature from a reliable source, so that it cannot be faked. An example of this is a "UserId certificate", created by a Backer.

User Identifier Types (UserId Types)

A "user identifier type" is either a "Single-site UserId" or a namespace and type of a Locked Id. For example, "single_site_userId", "qis:A:H:9:ri" or "qis:A:H:9", etc. An RP can choose to trust only QIF identifiers in the "A9" namespace, or only "A9" namespace and a certain subtype (such as "ri"), etc.

An RP declares what types of UserId it supports in the "RmlFiskProfile.userLoginCreationRules.userIdTypes" property. A complete "supported type description" might include the UserId format, only certain issuers of "UserId certificate"s, a communication protocol, etc.

To login to an RP, a user must prove they are who they claim to be. Several ways to do this are: to prove access to a shared secret, such as with a password, or prove possession of a private key, or by proving ownership of a user identifier. It can prove ownership either directly, by cryptography (QIF "qis:A:H:"), or by using a host or backer of the identifier.

Values for an RP "RmlFiskProfile.userLoginCreationRules.userIdTypes".

single_site_userId value:

The "single_site_userId" value means that the RP supports browsers sending a "Single-site UserId" to login. This means that the RP must be capable of storing some kind of Credential Verification Rule (CVR) values for its users. (e.g. a password, or public key, etc.) When the RP receives the "proof of identity", it will validate the proof using the Credential Verification Rule stored for the claimed UserId. The storage of CVR values is required when browsers do not have to send a Locked Id to the RP. Because the RP cannot rely on the lock mechanism inside the Locked Id during the user verification process. (Since it may not exist.)

An RP can add a CVR to any UserId type. For example to single-site, Federated UserId or Self-contained. If the RP (1) accepts Single-site UserIds then it must (2) store Credential Verification Rule for its users.

Multi-Site UserId detail

There are two main types of Locked Id, Self-contained and Federated UserId. The difference is where the lock requirements are stored. A "Self-contained UserId" contains the requirements inside itself. This is usually some sort of cryptographic key or signature. (For an example, see the "A:H:1" QIF type.) A Federated UserId stores the lock requirements online at a third-party. At a Backer, Identifier Authorization Provider or Identity Provider

Multi-Site UserIds require cooperation from RPs. A compliant RP MUST NOT register a Locked Id to any of its users, unless the user can unlock the ID. That proves that the user satisfies the identifier's lock requirements. (i.e. An RP must not allow the creation of an account that uses a Multi-Site UserId, without verifying that the user satisfies that identifier's lock requirements.) This is the primary reason why the "type" of Multi-Site UserIds must be easy to read, and why they must be easy to distinguish from Single-site UserIds. For easy RP compliance. If an RP does not support an identifier's lock method, then the RP must not allow the use of that type of identifier. (Locked Ids could alternately be called "Multi-site UserId" or "requirements UserId", etc.)

In order to make RP compliance easier, Multi-Site UserIds MUST have an easy-to-identify "format" and "type". This makes Multi-Site UserIds easy to distinguish from Single-site UserIds. The "type" clearly identifies what Identifier Lock Id the identifier uses, and the method of user-verification that it uses. The QIF standard is one such format.

During both login and "create account" attempts, the user gives the Multi-site UserId to the RP. The RP will extract the "type ID" from inside the UserId. (If the UserId is a Federated UserId then the RP will also extract the "Backer Id".) Note that the "type ID" may be inferred from the format of the UserId. For example, an email address format will use an email address "type ID".

Then the RP will confirm that the user "owns" the UserId. For a Federated UserId, the RP will execute the Federated UV Method, and have it validate the documents provided by the user during the login attempt. Then the RP must find the Backer VM for the type ID and Backer.

If the UserId is a Self-contained User_id, then the RP will need to parse the UserId into something, or extract data from inside the UserId. (For example, it can be parsed into a public key.) The RP will then use the result to validate the user. The RP uses the "type ID" to determine what to parse the User_id into.

========

A "Federated UserId" uses some type of online host. The UserId must contain a "Backer Id" and a "registration ID". The "Backer Id" represents an organization. The "registration ID" represents the user or resource at the organization. Each Backed UserId must contain a distinct "registration ID".

A Backer must have some way to authenticate its users. It may issue a certificate or require users to login, etc. If the Backer generates a timestamped "proof of login", then all UserIds that use that Backer are "Federated UserIds". A Federated UserId will issue a "user_account" or the UserId requirements that users must meet in order to use the UserId, etc. The UserId itself only needs to contain a "Backer Id" and a "registration id". For Example: an email address is a "Federated UserId". The "domain name" is the "Backer Id", and the "user account name" is the "registration id". The server running the email system (i.e. the "Backer") can be used to verify if a user has access to the UserId.

A "Federated UserId" usually depends on the Backer continuing to behave correctly. Although, a specially designed registration systems can have UserId that only need a working Backer during their creation. But this is unusual.

This category of UserId is (1) universally unique, (2) persistent across all RP (it identifies the same user at all RP), (3) allowed access is provable, (4) its use is restricted at RPs, (5) can only be used by the "owner". RPs are required to obtain proof of ownership from a user before allowing the user to use it.

A Locked Id is capable of being used at multiple websites. However, this does not mean that it MUST be used at multiple websites. For privacy reasons, it might be preferable to use a different Locked Id at each RP website.

RPs must not register a Locked Id to any of its users, unless the user can unlock the ID. i.e. Prove he owns it. In this manner, the UserId is guaranteed to be available at an RP for the owner to use. A single user can have multiple "Locked Id". Each one can be used independently.

A "Locked Id" generally comes in one of two forms. Either the UserId contains the result of some sort of cryptographic "one-way function", (like a "public key"), or it contains the "PermanentBackerId" of the entity that created it. This allows a verifier to identify the Backer of a UserId. (The "host" can also be known as the "issuer" of the UserId.) The verifier can contact the Backer, to request that it provide some sort of additional information about the user that it issued the UserId to. The Backer can give the RP a verification requirement, such as a public key. Or the Backer can verify the user itself, and give to the RP an assertion that the user is the owner of the UserId. (This last option is typically called a "Federated UserId". Where the RP must trust the "assertion" created by the "issuer" of the UserId.)

In order for "Locked Id" to work correctly, their use must be restricted at all RP. A Multi-Site UserId must never be used by anyone other than the "owner". This means that all conforming RP MUST (1) recognize Locked Id and (2) not allow the use of a Locked Id by anyone other than the owner. (i.e. An RP must not register a Locked Id to any of its users, unless the user can unlock the ID.)

In order for a "Locked Id" to work correctly, there MUST be a way for RP to verify a user's claims of owning the UserId. For Federated UserId, there must be a Backer VM for the type of federated UserId. And it must be "findable" by all RPs.

For example, a "universally unique" Federated UserId can be created by combining any "Single-site UserId" with the domain name of the RP where it is used. However, In order for those identifiers to be a Federated UserId, there must be a Backer VM for that type of federated UserId. And it must be "findable" by all RPs. If it is not "findable" by all RPs, then the UserId is not a "working" "Locked Id".

Using a Locked Id skips the hardest part of traditional User Login creation, having the user find (and remember) an unused Single-site UserId. With the traditional means of a username and password, creating an account at an RP requires the user to find an unused username at the RP.

When creating a new account with a Locked Id, the user does not have to "find an available id" at the RP, as the "UserId" is reserved for his exclusive use, at every RP. All RPs must restrict use of a Locked Id to only the party that can provide proof of ownership. Thus, a Locked Id functions as a "reserved username" at all RPs. When using a Locked Id, an account can be created automatically, without user intervention or negotiation with the RP. However, an RP SHOULD NOT automatically create an account for a Locked Id in response to a login RML Request. (i.e. When a user tries to login with a Multi-Site UserId, and the RP has VERIFIED that the user owns the UserId, but the RP does not have an account for that UserId.) Confirmation from the user is required before User Login creation. (To prevent User Login creation due to user error. For example, if the user accidentally selected the wrong User Login to log in with, etc.) See User Login creation.

Multi-Credential Authentication

Multiple Credential Authentication is when an RP requires proof of multiple credentials in order to authenticate a user. Each credential can be of any type. (password, private key signature, fingerprint, PIN, etc.) (See Multi-Credential definition.)

Multiple Credential Authentication allows RPs to combine multiple authentication mechanisms to authenticate a user. Such as fingerprint + PIN. For example, a bank may want to have very strict requirements for logging in. It may want a HOTP code in addition to a regular password. Or a fingerprint in addition to both the HOTP code and password.

Note that multi-factor authentication is a subset of Multiple Credential Authentication. Each "factor" can be transmitted to the RP as a different type of credential. (Also note that 2nd factor authentication, U2F, etc. are forms of Multiple Credential Authentication.)

An RP website can store various Credential Verification Rules to authenticate a user. The RP can require multiple credentials before it will authenticate a user. (These things do not require Multiple Credential Authentication.)

Using "Multiple Credential Authentication", an RP is able to list what credentials it requires. (These credentials can be "authentication factors".) Using "Multiple Credential Authentication", the browser is able to deliver to the RP, in a single request, all the required credentials. This lets RPs create their own security requirements. They can mix different existing standards. (Or even create their own.)

Multiple Credential Authentication is implemented in the RML Request sent from the browser to the RP. The request normally includes both (1) a signatures section (for private key signatures), and (2) a credentialProofList section (for all other types of credential proof). The "set credentials" version of the request also includes a list of Credential Verification Rules, which is used to deliver to the RP the list of Credential Verification Rules that the RP should store. Note that each credential in the list can be of a different type. And new types of credentials can be created. The only requirement is that both the RP and the Login Manager support the new credential type.

An RP can even do more complicated things, like define a set of credentials and require a minimum number of them. For example, it can require that a user provide proof of 3 out of 4 credentials in the set.

Multi-Factor Authentication

Multi-factor authentication is where an RP verifies a user's identity by requiring two or more "factors" of authentication. The three types of authentication factors are something you know, something you have, and something you are. Something you know can be something like a password or PIN. Something you have can be a mobile phone or a special USB key. And something you are can be something like your fingerprint or other biometric identifier.

A prerequisite of "Multi-factor" authentication is Multi-Credential Authentication. (i.e. that a login may require multiple credentials.) In addition to multiple credentials, Multi-factor also requires that a type of credential exist for each type of factor that the RP requires. The user can send proof of all the required "factors" to the RP at the same time, using multiple credentials.

For example, an RP can require the user type in a password, have a USB key, and scan their fingerprint. Proof of all of these "factors" can be sent to the RP using credentials. The USB key can contain a private key or implement a full Login Manager API. (i.e. send a private key signature to the RP.) A Login Manager (or browser) can use cryptography to create a private key out of the password. (i.e. send a private key signature to the RP.) A User Authenticator or browser can scan a fingerprint and send it to the RP.

User Authentication Detail

User Authentication is the process by which a user's identity is verified. It is the process by which a user "signs in" to a website. In order to login to an RP, a user must prove their identity to the RP. Typically, a user submits a "proof of identity" to the RP. This "proof" may be as simple as a username and password. An RP may store a Credential Verification Rule (CVR) in order to authenticate a user. This list of CVR. The RP typically requires that the "proof of identity" must satisfy some subset of the CVR criteria in order to be valid. (e.g. during an attempt to login to the RP.) An algorithm that uses the set of CVR to verify a "proof of identity" is called an "identity verifier".

The CVR can consist of a public key, a password, a certificate, an algorithm, a database record, a website response, etc. See the RP User Authentication Process. An RP usually stores different CVR for each user identity in an internal database record. The RP may allow a user to add, remove, or set the CVR for themselves. (For example, an RP can allow a user to change their password.) See the opId="setActiveCredentials". For example, an RP may allow a user to turn on and off Multi-factor authentication.

A "Credential Verification Rule" is usually the opposite of a "credential". A "CVR" is a test for a required condition, while a "credential" is proof of a condition. (However, in the case of a "password", both could be the same value.)
Each CVR must have an associated "type" or "Credential Verification Method". This "type", or algorithm, defines how to verify if a "proof of identity" satisfies the CVR requirement. For example, If the CVR is a public key, then the "type" of the CVR denotes what format the key is in, and how to verify if a digital signature was created by the matching private key. An RP must know the "Credential Verification Method" for each type of CVR it supports. An RP can publish what types of Credential Verification Rule it supports in the FiskProfile.credentialReg property.
There are two main types of Credential Verification Rule (CVR). There is "Site-specific" (i.e. Regular) and "multi-site". CVR are only applicable to a single RP. They are usually a type of "password" or "public key" and are usually stored in a database at the RP website. An RP is free to use any type of CVR it wants, and that browsers support. The "Multi-site UserId Credential Verification Rule" are applicable to all RPs. They are not "tied to" or limited to a single RP.

RML Request: Set Credential Verification Rules Detail

The RML Request, sent from the browser to the RP, can contain a list of Credential Verification Rules. (i.e. called the crList.) This list is how the user delivers to the RP the set of Credential Verification Rules that the RP should store for future user authentication.

The RP stores a set of "Credential Verification Rules" for each user. It uses these CVR to verify the credentials sent to it during login. (i.e. during the authentication process.) The user sets what CVR the RP stores by using the "setActiveCredentials" command. (i.e. It performs similar to a "change password" operation on systems that use a username and password.)

The list of CVR is located in the "credRegSec" section of the RML Request. (i.e. In the "credRegSec" object.) This allows the user to set all the CVR in a single request.

Note that each Credential Verification Rule (both stored by the RP, and in the crList) can be of a different credential type. And new types of credentials can be created. The only requirement is that both the RP and the Login Manager support the new credential type. The mix of different types stored by the RP allows it to support Multi-Credential Authentication and Multi-factor authentication.

The RML Request can also contain multiple "credential proofs". This is for user authentication, when a user wants to login. (i.e. It can be used to perform Multi-Credential Authentication.) This is how browsers can send multiple user credential proofs to an RP, all in the same request. The service request has two different sections for proofs, (1) the signatures section, for private key signatures, and (2) the credentialProofList section, for all other types of credential proof.

The "signatures" object can hold the signatures for multiple "public key" type credentials. The credentials can be digital signatures (i.e. public key cryptography) or they can be keys generated from passwords or federated credentials. Note that EACH signature in the "signatures" object has has digitally signed the entire payload of the Service Request. The RML Request values, "opIdReq" or "exp", etc. cannot be changed without generating a new signature. Because of this, signatures are preferred over other types of proof.

The "credentialProofList" section can hold the proof for any credential that is not a public key. It can contain multiple "credentialProof." objects of different types. Use the credentialProof."format (fmt_p)" property to hold the type of proof.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "login", "sub": "remere/login", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "encReqValue": "xxxxxxxx", "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId "encForLogin": "q3jC9-h" // Use "encForLogin" with "login" opId. }, "user_auth_method": "only_MSUID", "redirectUri": "/some/path/page.html", "useCount": 42,
// user_id_certificate_chain (Not available when using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ],
// crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, /* "credRegSec" credentialRegSection. Not required for "login" opIdReq. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } */ };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256", "sct":192} "header": { "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }, { "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256", "sct":13} "header": { "kid": "userIdPK" // "userIdPK" means the public key is in "uid". }, "signature": "MG4mdj-hdfYHqJRnUxKa6e ..." }] } };
Example Certificate: The "UserId certificate" JWT. (uid_cert_chain[0]) header and claims:
let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, "exp": 1533064012, "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "uid": { "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "usk": "q3jC9-h" }, "cert_pubKeyList": [ { "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." } ] };
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. "csvm": "idr_keystore" // Get the sig verification key from an "Backer keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

Configuration Settings Detail

Configuration Settings are a collection of JSON data values, published by an RP, that describe the RP, or the services offered by the RP. They are typically embedded within HTML markup. They exist to configure a browser's behavior at the RP. They are an alternative to an HTML page using Javascript to control how the browser behaves. The ConfigSettings are embedded within HTML markup as a single JSON object in a special HTML attribute. The ConfigSettings give the browser data or instructions about Remote Services without using JavaScript. This makes creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.

There are two different types of Configuration Settings in HTML. First, the FiskDigest object is inside the name="serviceguidedata" attribute of a ServiceGuide HTML Element. These ConfigSettings apply to the entire HTML page.

Second, the FiskTrigger object is inside the "servicetriggerdata" attribute of a Service Trigger HTML element. (For Example, in a Anchor HTML Element.) An FiskTrigger object is only applicable to that single HTML element.

In order for Capis to work, an HTML page MUST contain a ServiceGuide HTML Element. And the FiskDigest MUST contain at least the "challengeKey" property.

An RP can put multiple Service Trigger HTML Elements on a single page, and use different Configuration Settings in each one.

All Configuration Settings are case-sensitive. Both the property name and the value. Example: FiskTrigger.protocolOp="remere/login" Where the opId="proveUserPresence", "serviceProfileList", etc.

The names and values of the FiskProfile are defined by the sdSpec that the ServiceProfile uses. (If there is no sdSpec specified, then the default sdSpec for the protocolUsed is used.) Different specifications can define different ServiceProfile properties and values. Note the difference between FiskProfile properties and FiskDigest properties. Also, there are a few special FiskDigest properties. Those that are used before the FiskCatalog is retrieved. These are serviceCatalogUri, serviceProfileList and challengeKey.

Example HTML. HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a> ... <a href="remere://example.com/remereServiceOp?op=proveUserPresence" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/proveUserPresence", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Confirm my presence</a>
The mainFiskCatalog.json contents =
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }
Example FiskDigest:
let fiskDigestCts = {"id":"offer_1", "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceProfileList": [...]};
let fiskProfilePath = {};
fiskProfilePath.fiskProfileId = "remere_1";
let fiskDigestId = "offer_1";
let opArgs = {"protocolOp":"remere/login",
  "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} };
remereLogin_api.performServiceOp("login", opArgs, fiskProfilePath, triggerView);
// TODO "fiskDigestCts" is not in opArgs. It should be an argument to performServiceOp?

Page Configuration Detail (pageConfig)

The collection of all the (public) configuration data stored for Capis by the browser. In particular, this includes the pageConfig."fiskDigestList", pageConfig."baseFiskClientData" and pageConfig."fiskClientData".

The "pageConfig" object contains all the configuration objects for the current HTML page. The "pageConfig" object includes the "fiskDigestList" array. The "fiskDigestList" is a list of all the FiskDigest info blocks in the HTML page. (i.e. A list extracted from all the ServiceGuide HTML Element in the HTML page.) Each FiskDigest item has a "fiskDigestCts" sub-object, which contains the FiskDigest data.

The "baseFiskClientData" object contains the merged data from the fiskDigestList in the current HTML page.

The FiskCatalog.protocolConfigList "ProtocolConfig" object contains the "primaryService", "fiskProfileIdList" and "svFromDirList" lists.

FiskDigest Detail

A FiskDigest is a JSON object embedded inside a special attribute of a ServiceGuide HTML Element. (The name="serviceguidedata" attribute.)

It contains a set of Configuration Settings. Each FiskProfile describes a Remote Service the RP offers, etc. A FiskDigest can contain a reference to a separate FiskCatalog document. (In the "serviceCatalogUri" property.) And/or it may contain any of the FiskCatalog properties directly. (Such as the serviceProfileList property.) This is because the FiskDigest structure inherits from the FiskCatalog structure.

The FiskDigest (and other ConfigSettings) are an alternative to an HTML page using Javascript to control how the browser behaves. The FiskDigest gives the browser data or instructions about the Remote Services without using JavaScript. The ConfigSettings make creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.

A FiskDigest represents an advertisement, menu or application form by which services can be ordered. A FiskDigest contains a "FiskDigestId".

There are two different types of Configuration Settings in HTML. First, the FiskDigest object. These apply to the entire HTML page.

Second, the FiskTrigger object is inside the "servicetriggerdata" attribute of a Service Trigger HTML element. (For Example, in a Anchor HTML Element.) These are only applicable to that single HTML element.

In order for Capis to work, an HTML page MUST contain a ServiceGuide HTML Element. And the FiskDigest MUST contain at least the "challengeKey" property.

All Configuration Settings are case-sensitive. Both the property name and the value. Example: FiskTrigger.protocolOp="remere/login" Where the opId= "proveUserPresence", "serviceProfileList", etc.

The FiskDigest structure inherits from the FiskCatalog structure. It may contain any of the FiskCatalog properties directly. Such as a serviceProfileList property. (Which contains a list of FiskProfile objects.) It may also reference a separate FiskCatalog document.

The FiskTrigger object is used to create a ServiceOperationArgs object. Which is passed into the CapisControl.invokeServiceOp function, as the opArgs argument.

A FiskDigest object will be accessed from inside the Capis API functions. For example, the CapisControl.invokeServiceOp function will look up the FiskDigest.challengeKey value.

Not all Configuration Settings are usable by all ServiceTrigger HTML Elements. In particular, the FiskTrigger.protocolOp property is only valid inside an Service Trigger. It is not valid inside a FiskDigest object. Similarly, the "challengeKey" is only valid inside the FiskDigest. It is not valid inside any Service Trigger FiskTrigger object.

FiskDigest Detail

A FiskDigest is a JSON object inside a special attribute of a ServiceGuide HTML Element. (Typically inside the name="serviceguidedata" attribute.)

FiskDigest Purpose

The FiskDigest (along with other ConfigSettings) provides a way for the RP to give the browser data or instructions about Remote Services without using JavaScript. This makes creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.

FiskDigest Contents

A FiskDigest contains a set of Configuration Settings. It is written using Capis specification format. A FiskCatalog applies to the entire HTML page.

A FiskDigest JSON object can contain a reference to a separate FiskCatalog document. (In the "serviceCatalogUri" property.) And/or it may contain any of the FiskCatalog properties directly. (Such as the serviceProfileList property.) This is because the FiskDigest structure inherits from the FiskCatalog structure.

The FiskDigest typically contains the following. (1) (REQUIRED) A "challengeKey" property. (A securely generated, long random number.) It must be different each time the page is refreshed. (2) (OPTIONAL) A serviceProfileList. (3) A "serviceCatalogUri" property. Or the ServiceGuide HTML Element has a name="serviceguidedata" attribute that contains a reference to a FiskCatalog.

The FiskDigest settings can contain properties that are applicable to multiple services. (For Example, the challengeKey property can apply to multiple services.)

FiskCatalog References

A ServiceGuide HTML Element in an HTML page can contain a reference to a FiskCatalog document in the name="serviceguidedata" attribute. A FiskDigest in an HTML file can contain a reference to a FiskCatalog document in the "serviceCatalogUri" property.

FiskCatalog References

The FiskDigest settings are one of the two types of Configuration Settings. The other type of Configuration Settings are FiskTrigger object. An FiskTrigger object is contained in an Service Trigger HTML element. (Such as the Anchor HTML Element.) An FiskTrigger object is only applicable to the single ServiceTrigger HTML element that contains them. Therefore, they usually contain only Configuration Settings for a single service.

FiskDigest Use

When an RP service is invoked, the CapisControl.invokeServiceOp API function will call the CapisControl.ensurePageConfig function.

The CapisControl.ensurePageConfig function will extract all the FiskDigest objects from the HTML page. The CapisControl.invokeServiceOp function will then look inside the extracted FiskDigest object for the FiskProfile specified by the FiskProfileId argument. The invokeServiceOp function will use the FiskProfile for any Remote Service that it may call. Note the protocolUsed that it contains. The browser will use the FiskTrigger to create a ServiceOperationArgs object. Which is passed into the CapisControl.invokeServiceOp function as the opArgs argument.

The CapisControl.ensurePageConfig function will do the following. It will find all the ServiceGuide HTML Elements in the HTML page, then it will extract the FiskDigest object from each of them. It will then read and evaluate each FiskDigest object. For each of them, the function will look for the serviceCatalogUri property. It will then retrieve the FiskCatalog from the RP, using HTTPS. The socSiteCatalog is known as the "externFiskDigest". The function will then merge the pageFiskDigest with the externFiskDigest. The pageFiskDigest values taking priority.

Example HTML ServiceGuide:
<head> <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "https://RP_example.com/some/path/mainFiskCatalog.json", // serviceCatalogUri is an alternate to using href // Specifies alternate FiskProfile for protocol "remere". "protocolConfigList": [{"protocolId":"remere", "serviceLineup":["remere_2"]}] }'/> </head>
See the example_mainFiskCatalog.json document.
Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

FiskDigest Detail - List of Properties

FiskDigest - Property: FiskDigest.id (longName: "fubar")

Type string; Default value: "offer_n" Example: "offer_1"

Specifies the id of the FiskDigest. If the "id" is not specified, then a value is automatically assigned. The default value is of the format "offer_n". Where "n" is the index of the FiskDigest in the HTML. (Starts with 1.) Example: "offer_1"

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "id": "offer_1", "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ] }'/>

FiskDigest - Property: FiskDigest.serviceCatalogUri (longName: "fubar")

Type string;

Specifies the URI of the RP FiskCatalog. Overrides the defaultValues.socSiteCatalogDocUri of the FiskCatalog. This option may ONLY be used in the ServiceGuide HTML Element.

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "https://RP_example.com/some/path/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

The "serviceCatalogUri" property ONLY exists in a FiskDigest. If a ServiceTrigger HTML Element has a "serviceCatalogUri" value, it will do nothing.

Rules

Allows an RP to put the FiskCatalog somewhere other than the defaultValues.socSiteCatalogDocUri (i.e. in the "/.well-known" directory.) This is particularly useful if the RP does not have control over the "/.well-known" directory.

Allows different pages on the same website to use a different FiskCatalog. Allows a single domain name to host multiple RPs. The login anchor for each RP can specify its own FiskCatalog, at a different URI location.

Note that using a non-standard "serviceCatalogUri" may require that the "audience" in the "proof of identity" be changed too. See the FiskCatalog FiskProfile.appIdSpec property.

Must start with either "/" or "https:", or it is invalid. If it is invalid, then the browser MUST throw an error.

If the location of the FiskCatalog is not defined, (If the ServiceGuide HTML Element does not have either the "serviceCatalogUri" property or the "href" attribute), then the defaultValues.socSiteCatalogDocUri is used.

If the ServiceGuide HTML Element has both an "href" attribute and a "serviceCatalogUri" property, the "serviceCatalogUri" property has priority.

When this property is present in an ServiceGuide HTML Element, it overrides the defaultValues.socSiteCatalogDocUri default behavior for all ServiceTrigger HTML Elements on the page.

The only reason to set this property is to override the default. Perhaps to change the directory, or add a path component, or to remove the port number. (i.e. It doesn't make any sense to set the value to be the base domain name, as that is the default. Example: "https://RP_example.com")

A "serviceCatalogUri" is a url using the https scheme. It identifies a JSON document at the RP. For convenience the address of the JSON document can be written starting with just a slash "/". If it is written like this, then "https" and the domain name (and port) of the RP website are automatically prepended to it.

The second type of URI, is simply a domain name using https. The URI can contain a sub-domain and a port, but it does not contain any path or a trailing slash. If the URI does not contain a path component, then the defaultValues.socSiteCatalogDocUri is automatically appended to the URI. (i.e. "/.well-known/mainFiskCatalog.json")

FiskDigest - Property: FiskDigest.challengeTime (longName: "fubar")

Type string; Contains base64url encoded binary data. Required. (A value must be specified.)

Specifies the time the challengeKey was issued by the RP. It is a timestamp value. It is the number of milliseconds since Jan 1, 1970. (i.e. Unix Timestamp.) (See similar use in timestamp-id, and RML Request.issuedAt.)

The challengeTime value is required as part of the Service Request to confirm User Authentication. In order to perform User Authentication, the browser must respond to a challengeKey with the required credentials, within a short time frame. And include the challengeTime in the proof. For Example: The challengeTime is copied into the RML Request rml.request.cht value in the RML Request. The challengeTime value may be used by the RP to help determine what user, session, etc. made the Service Request, or the freshness or staleness, etc. of the Service Request.

Note that the challengeTime value is not necessarily the same as the time when the HTML page was created. That time may be off by several seconds. And it is not equal to the time that the client (the browser) received the HTTP transmission (containing the HTML page).

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, // challengeTime timestamp "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

FiskDigest - Property: FiskDigest.challengeKey (longName: "fubar")

Type string; Contains base64url encoded binary data. Required. (A value must be specified.)

A "cryptographic nonce" value, created by the RP. base64url encoded. It is a securely generated nonsense value. (i.e. It is a long random number.) It is a binary value encoded using base64url. It is a type of "cryptographic nonce" value, used to perform user authentication. This option is REQUIRED in the ServiceGuide HTML Element.

A "nonce" value is simply passed back to the RP. It is a nonsense value used to distinguish a particular client session of an RP. Because it was created by the RP, and is passed back to the RP during authentication, it prevents certain CSS (Cross Site Scripting) attacks.

The "challengeKey" value, as with all "nonce" values, is to make sure that a cryptographic function is NEWLY performed, in response to a specific prompt. It prevents any previously generated cryptographic message from being used. The RP wants NEW proof that the user has a private key, so the RP generates a "nonce" value, and requires that the value be returned to it, inside the user login request. The User Authenticator creates a RML Request message, includes the challengeKey in it, signs or encrypts the message with the private key, then sends the message to the RP. The presence of the challengeKey in the message will prove that the signed or encrypted message was NEWLY generated. Therefore, the browser must currently have the private key.

A challengeKey value is REQUIRED in the ServiceGuide HTML Element.

The "challengeKey" value is a base64url encoded binary value (i.e. an integer). Therefore, it can only contain alpha-numeric, dash and underscore characters. (0-9, a-z, A-Z, '-', '_')

The RP can institute a time limit on between the page was sent and when the challengeKey must be answered.
Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

FiskCatalog Detail

A FiskCatalog is a JSON object, usually a JSON document, published by an RP, that describes the RP and a group of services that it offers.

FiskCatalog Document

A file containing a FiskCatalog JSON object. The default place to find this file is on a web server at "/_capis/mainFiskCatalog.json".

FiskCatalog Purpose

The FiskCatalog (along with other ConfigSettings) provides a way for the RP to give the browser data or instructions about Remote Services without using JavaScript. This makes creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.

FiskCatalog Contents

A FiskCatalog contains a set of Configuration Settings. It is written using Capis specification format. A FiskCatalog applies to the entire HTML page. (The client is typically a browser.) The FiskCatalog structure is inherited by the FiskDigest structure.

A FiskCatalog must contain a serviceProfileList property, which contains a list of FiskProfile objects. (i.e. serviceProfileList.fiskProfileItem objects.) Each FiskProfile object provides a description of a Remote Service offered by the RP. A FiskProfile can specify abilities, requirements, service options, and how the service should be invoked.

A FiskCatalog can also include other Configuration Settings, besides FiskProfile objects. Such as the protocolConfigList.

FiskCatalog Use

The FiskDigest settings are one of the two types of Configuration Settings. The other type of Configuration Settings are FiskTrigger object. An FiskTrigger object is contained in an Service Trigger HTML element. (Such as the Anchor HTML Element.) An FiskTrigger object is only applicable to the single ServiceTrigger HTML element that contains them. Therefore, they usually contain only Configuration Settings for a single service.

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.
Example FiskCatalog:
{ "id": "main_2", "sgSpec": "capisSgSpec 0.1", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
,
{ "id": "remere_2", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog Detail 2

A FiskCatalog is a JSON document, published by an RP, that describes the RP and a group of services that it offers. It must contain a serviceProfileList property, which contains a list of FiskProfile objects. Each FiskProfile object provides a description of a Remote Service offered by the RP. A FiskProfile can specify abilities, requirements, service options, and how the service should be invoked.

A FiskCatalog can also include other Configuration Settings, besides FiskProfile objects. It is written using Capis specification format. The FiskCatalog structure is inherited by the FiskDigest structure.

The FiskCatalog document must contain (1) a "id" value, (2) a "rpInfo" property, and (3) a "serviceProfileList".

The defaultValues.socSiteCatalogDocUri of the "Capis Document" is a "well-known" URI. (Example: "RP_example.com/.well-known/mainFiskCatalog.json".)

Each FiskProfile contains (1) The "protocolUsed" of the service (REQUIRED), (2) An "endpointBlock" item. An endpoint must have an address URI (the "endpoint.sUri" is REQUIRED), (3) The other abilities and requirements of the service. (These are listed in the "supported" section.) (4) The "id" of the service.

Example FiskCatalog:
(Note that comments, denoted by the // characters, are not allowed in JSON, and must be omitted.)
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "serviceProfileList": [ // Array of "FiskProfile" objects.
{ "id": "remere_1", "protocolUsed": "remere", // "protocol" is REQUIRED "sdSpec": "remereSdSpec 0.1", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": { "id": "endpoint_1", "http_method": "POST", // Default http_method is "POST" "sUri": "/_capis/remere/requestOp.json", // REQUIRED. Define the endpoint URI. "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]}, "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "mediation": "silent", "userLoginEditRules": { "userLoginAuthProfileList": [ { "id": "cfg1", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"pubKeyFirst"} ] }, { "id": "cfg2", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"passwordCred2"} ] } ], "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } ] }, "credentialReg": { // The types of "Credential Verification Rule" the RP supports. "ruleTypes": {"pubkey1":"1"}, "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}], "required_ruleTypes": {"pubkey1":"1"} // The RP requires a "public key" authentication. }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] // The certificate verification protocols the RP supports. }, "requirement_section": { } }
, { "id": "remere_2", // "id" is REQUIRED ... } ] }

FiskCatalog Detail - List of Properties

List of FiskCatalog Properties

FiskCatalog - Property: FiskCatalog.id (longName: "fubar")

Type string; Default value: "catalog_n" Example: "catalog_1"

Specifies the id of the FiskCatalog. If the "id" is not specified, then a value is automatically assigned. The default value is of the format "catalog_n". Where "n" is the index of the FiskDigest in the HTML. (Starts with 1.) Example: "catalog_1"

Example FiskCatalog:
{ "id": "main_2", "sgSpec": "capisSgSpec 0.1", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.sgSpec (longName: "fubar")

Type string; Default value: "capisSgSpec 0.1"

Specifies the name and version of the specification that the FiskDigest is written in.

Valid values are in the form of "capisSgSpec 0.1". They must contain the word "capisSgSpec". See Capis Default Values.
Example FiskCatalog:
{ "id": "main_2", "sgSpec": "capisSgSpec 0.1", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
,
{ "id": "remere_2", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.protocolConfigList (longName: "fubar")

Specifies the list of ProtocolConfig objects. These may include RP services. Each item contains 2 values. The protocolId and the serviceLineup.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
,
{ "id": "remere_2", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.ProtocolConfig (longName: "fubar")

Type Object;

Specifies a Protocol Handler. Each item contains 2 values. The protocolId and the serviceLineup.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
,
{ "id": "remere_2", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.rpInfo (longName: "fubar")

Type Object;
Information with which to identify credentials of the relying party. The properties in the "FiskDigest.rpInfo" section override the FiskCatalog properties with the same name. Also note that the RML FiskProfile FiskProfile.loginConfig may contain an "id" property.
Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

The "siteName" specifies the display name of the RP.

The "smallIcon" specifies the URI for a custom logo to display to the user in the identity login box.

The "siteName" was previously known as "siteName". The "smallIcon" was previously known as "siteLogo".

FiskCatalog - Property: FiskCatalog.rpInfo.siteName (longName: "fubar")

Type string; Default value: algorithm(The website domain name and port. -- The origin, without the protocol.)

Specifies the display name of the RP. The properties in the "FiskDigest.rpInfo" section override the FiskCatalog properties with the same name.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.rpInfo.smallIcon (longName: "fubar")

Type string;

FiskCatalog - Property: FiskCatalog.serviceProfileList (longName: "fubar")

Type FiskProfile[]; (Array of FiskProfile) Required. (A value must be specified.)

An array of FiskProfile objects. This option is typically used in the ServiceGuide HTML Element.

A FiskDigest may omit a serviceProfileList only if it includes a reference to a FiskCatalog. (The FiskCatalog must itself contain a serviceProfileList.) For example: An FiskDigest that exists in an HTML page, may contain a reference to a FiskCatalog in the ServiceGuide HTML Element. i.e. In the name="serviceguidedata" attribute. In the serviceCatalogUri property.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
,
{ "id": "remere_2", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"]}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.serviceProfileList.fiskProfileItem (longName: "fubar")

Type Object;

Defines a FiskProfile. This specifies the set of properties for a Remote Service. At a minimum, it must include three properties. An "id", a "protocolUsed" and an "endpointBlock" (or "endpointList"). A fiskProfileItem can be defined in either the ServiceGuide HTML Element, or in the FiskCatalog. For an example implementation, see RML FiskProfile and the RML ServiceProfile List of properties.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "protocolConfigList": [ {"protocolId":"remere", "serviceLineup":["remere_2"]}, {"protocolId":"exampleProtocol", "serviceLineup":["exampleProtocol_1"]} ], "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }

FiskCatalog - Property: FiskCatalog.documentId (longName: "fubar")

Type string; Optional.

Specifies the id of the document. Each document revision should have its own unique id.

Example FiskCatalog:
{
  "id": "main_2",
  "rpInfo": {
    "siteName": "ACME Corporation",
    "smallIcon": "/images/logo.png"
  },
  ...
  "serviceProfileList": [
    ...
  ]
}

FiskCatalog - Property: FiskCatalog.generate_challenge (longName: "fubar")

Type string;
Specifies the URL of the generate challenge JSON file. Will generate a "cryptographic nonce" value. base64url encoded.

The FiskCatalog may contain a "generate_challenge" property, to specify a way to generate a challengeKey.

Example FiskCatalog:

json_doc = { ... "generate_challenge":"/_capis/generate_challenge.json", ... }

{
  "id": "main_2",
  "generate_challenge": "/_capis/generate_challenge.json",
  ...
  "serviceProfileList": [
    ...
  ]
}

FiskTrigger - Detail

A FiskTrigger is a JSON object embedded inside a special attribute of a Service Trigger. (The "servicetriggerdata" attribute of a ServiceTrigger HTML Element HTML element. Such as a Anchor HTML Element.) It contains a set of Configuration Settings. The FiskTrigger object contains the set of Service Arguments to call the service with.

When the trigger is activated, the browser (in the CapisControl.invokeServiceFromTriggerHtmlElement function) extracts the contents of the servicetriggerdata attribute into a FiskTrigger JSON object. (See CapisControl.extractFiskTriggerFromHtmlElement.) The FiskTrigger object is used to create a ServiceOperationArgs object. Which is passed into the Capis Control.invokeServiceOp function, as the opArgs argument.

Not all Configuration Settings are usable by all ServiceTrigger HTML Elements. In particular, the FiskTrigger.protocolOp property is only valid inside an Service Trigger. It is not valid inside a ServiceGuide HTML Element. Similarly, the "challengeKey" is only valid inside the FiskDigest. It is not valid inside any Service Trigger FiskTrigger object.

The Trigger Options provide a way for the RP to give the browser data or instructions about Remote Services without using JavaScript. This makes creating Capis enabled HTML easier, and Capis will work without requiring the browser to enable JavaScript for the RP.

The Trigger Options MUST contain a FiskTrigger.protocolOp property. The opId value is part of the Capis specification. All other properties are defined as part of a different specification, such as the Remere Login Specification.

The Trigger Options are only used when that trigger is activated. They do not apply to the entire HTML page. The FiskTrigger is one of the two types of Configuration Settings. The other type is a FiskDigest.

There are currently two instances of Service Trigger in HTML. These are the Anchor HTML Element and the Form HTML Element. (The servicetriggerdata attribute.)

Example HTML. HTML ServiceGuide, ServiceTrigger HTML Elements:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a> ... <a href="remere://example.com/remereServiceOp?op=proveUserPresence" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/proveUserPresence", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Confirm my presence</a> ... <form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm" }'> <input type="text" name="first_name" placeholder="Enter your first name" /> <input type="text" name="user_email" placeholder="Enter your email address" /> <input type="submit" value="Sign and Submit the Form" /> </form>

FiskTrigger - List of Properties

FiskTrigger Detail - Property: FiskTrigger.protocolId (longName: "fubar")

Type string; Optional. Default value: "remere"

Specifies the Service Protocol Id to be used by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.)

The 'protocolId' property does not exist in the ServiceGuide HTML Element.

The "protocolId" property contains the Service Protocol Id. The "protocolOp" property contains the opId. The Service Protocol Id and opId specify which Remote Service the "opId" should invoke.

This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to invoke.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolId":"remere", "opId":"setActiveCredentials", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.opId (longName: "fubar")

Type string; Required. (A value must be specified.)

Specifies the service operation to be performed by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.) Usually the contained opId value is sent to the RP in the RML Request.opIdReq property, and the RP executes the requested operation.

The 'opId' property does not exist in the ServiceGuide HTML Element.

The "protocolId" property contains the Service Protocol Id. The "protocolOp" property contains the opId. The Service Protocol Id and opId specify which Remote Service the "opId" should invoke.

This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to invoke.

The opId value must be one of the RML Operation List. The following are possible values for the opId. "login", "logout", "registerUserLogin", "setActiveCredentials", etc.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolId":"remere", "opId":"setActiveCredentials", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.protocolOp (longName: "fubar")

Type string; Optional. Default value: "login"

Specifies both the protocol and the operation (the protocolId and opId) to be performed by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.) Usually the contained opId value is sent to the RP in the RML Request.opIdReq property, and the RP executes the requested operation.

The 'protocolOp' property does not exist in the ServiceGuide HTML Element.

The "protocolOp" property contains the protocolId value and the opId value. Separated by a slash. The protocolId and opId specify which Remote Service the trigger should invoke. This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to invoke.

The opId value must be one of the RML Operation List. The following are possible values for the opId. "login", "logout", "registerUserLogin", "setActiveCredentials", etc.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.offerId (longName: "fubar")

Type string; Optional. Default value: "offer_1"

Specifies the FiskDigestId to be used by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.)

The 'offerId' property does not exist in the ServiceGuide HTML Element.

The "offerId" property contains the FiskDigestId. The "protocolOp" property contains the opId. The Service Protocol Id and opId specify which Remote Service the "opId" should invoke.

This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to invoke.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "offerId":"offer_1", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.profileId (longName: "fubar")

Type string; Optional. Default value: "login"

Specifies the id of the FiskProfile (The FiskProfileId) to be used by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.) Usually the contained opId value is sent to the RP in the RML Request.opIdReq property, and the RP executes the requested operation.

The 'profileId' property does not exist in the ServiceGuide HTML Element.

The "profileId" property contains the FiskProfileId. The "protocolOp" property contains the opId. The FiskProfileId and opId specify which Remote Service the trigger should invoke.

This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to invoke.

The opId value must be one of the RML Operation List. The following are possible values for the opId. "login", "logout", "registerUserLogin", "setActiveCredentials", etc.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "profileId":"remere_1", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.catalogId (longName: "fubar")

Type string; Optional. Default value: "login"

Specifies the FiskCatalogId. Example: {offerId:"offer_1", profileId:"remere_1", catalogId:"cat_1"}

The FiskCatalog and the FiskProfile (the FiskCatalogId and FiskProfileId) to be used by the Service Trigger. (i.e. When the anchor is clicked or the form is submitted.) Usually the contained FiskProfileId value is used in the CapisControl.invokeServiceOp function, when the browser needs to create the specified service request.

The 'catalogId' property does not exist in the ServiceGuide HTML Element.

The "catalogId" property contains the FiskCatalogId value. The FiskCatalogId and FiskProfileId specify which FiskCatalog and which FiskProfile the trigger should use. This specification allows a single HTML page to use multiple services and/or multiple methods offered by the services. There must be a way to specify which service an Service Trigger wants to use.

The FiskProfileId value must be one of the FiskProfile.id values that exist in a FiskCatalog.serviceProfileList.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=setActiveCredentials" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/setActiveCredentials", "catalogId":"cat_1", "profileId":"remere_1", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Set Active Credentials</a>

FiskTrigger Detail - Property: FiskTrigger.opArgs (longName: "fubar")

Type Object; Optional.

Specifies the opArgs to be used. Given to the CapisControl.invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView) function.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "opArgs": {"fName":"Mickey", "lName":"Mouse", "car":"Jaguar"}, "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Sign Data</a>

FiskTrigger Detail - Property: FiskTrigger.signContents (longName: "fubar")

Type Object; Optional.

Specifies the data to be signed. Only used with signData opId.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=signData" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signData", "signContents": {"fName":"Mickey", "lName":"Mouse", "car":"Jaguar"}, "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Sign Data</a>

FiskTrigger Detail - Property: FiskTrigger.onSuccess (longName: "fubar")

Type Object[]; (Array of Object) Optional.

Specifies the things to do on the service response.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "https://example.com/some/path/page.html"}]} }'>Login</a>

RML FiskTrigger - Detail

The RML FiskTrigger structure inherits from the FiskTrigger structure.

The RML Specification extends the Capis specification, and defines its own properties. (The RML "remere" Service Protocol Id)

RML FiskTrigger - List of Properties

RML FiskTrigger - Property: FiskTrigger.onSuccess.actionItem.redirectUri (longName: "fubar")

Type string; Optional.

Specifies the URI of the page the browser should load after the success of the authentication operation. Alternate name is "return_to".

Tells the RP where to redirect the current browser tab, once the auth attempt has been successful. It can be another page on the RP website, or elsewhere on the internet. Note that this is a request. The RP does not have to comply with it. In particular, an authentication failure will result in an error page, it will not redirect the user to the redirectUri page.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "https://example.com/some/path/page.html"}]} }'>Login</a>

RML FiskTrigger - Property: FiskTrigger.ulCreationCfg (longName: "fubar")

Type Object; Optional.

Specifies the UserId and related configuration values to request the RP assign to the User Login. This value is primarily used with "requested_ulc User Login Creation Method". This value is primarily used with opId="registerUserLogin", "setUserId" and "login". This value is part of a REQUEST to the RP to assign the specified UserId to the User Login. The RP may or may not comply. This value is primarily used to send a Multi-site UserId to the RP.

The RML FiskProfile userLoginCreationRules value tells the browser what the RP's rules are. The RML FiskTrigger ulCreationCfg asks the RP to change its default behavior.

When used in the browser with opId="login", the value is only used if the user chooses to create a new User Login. If so, then the uid.clientReqUserId value is sent to the RP with opId="registerUserLogin". When used with "registerUserLogin" and "login", the LoginManager.createUserLogin function is called, and the *original* userId is set to the desired value. However, the RP may decline to use the UserId, and respond with a different UserId value. This will change the userId value in the LoginManager. When used with "setUserId", the value is not stored in the LoginManager at all all, unless and until the RP responds with confirmation that the value was assigned.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "ulCreationCfg": {"userIdGenMethods": ["st1", "ct1"], "rawUserId": "qis:A:A:2:M4j-q:XSAErLq"}, "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a> ... <form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm", "ulCreationCfg": { "userIdGenMethods": ["st1","ct1"] }, "triggerPropFieldNameList":["input27"] }'> <input id="input26" type="text" name="user_email" value="john@example.com" placeholder="Enter your email address" /> <input id="input27" type="text" name="ulCreationCfg" value='{ "userIdGenMethods": ["st1","ct1"] }' /> </form>

RML FiskTrigger - Property: FiskTrigger.triggerPropFieldNameList (longName: "fubar")

Type string[]; (Array of string) Only usable by a form.

Specifies an array of form input field "id" strings. The input field elements should contain the trigger property values. Only usable by forms. This is typically only used for testing and debugging implementations.

For each id in the array, the form input HTML element with that id is found, the property name is read from the "name" attribute, and the property value is read from the "value" attribute, then the FiskTrigger property with that name is set.

In the future, setting sub-properties might be implemented. In this case, the name of the field would contain a period, separating the target property from a sub-property. (Example: name="ulCreationCfg.rawUserId")

Example HTML Anchor:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "triggerPropFieldNameList": ["input27","input28"], "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'> ... <input id="input27" type="text" name="profOp" value="remere/login" /> <input id="input28" type="text" name="ulCreationCfg" placeholder="{ Enter JSON. }"/> <input type="submit" value="Submit" /> </form>
Example JavaScript implementation:
// example implementation
/**
 * Extracts a FiskTriggerWrap object from the htmlElement.
 * (1) Extracts a special HTML attribute, converts it into a JSON object. (jsonObject)
 * (2) If htmlElement is a form, then extracts the specified fields into the JSON.
 * (3) Converts the JSON object into a FiskTriggerWrap.trContents object.
 * @param {DOMElement} htmlElement; The HTML element to extract from.
 * @return {FiskTriggerWrap} The extracted FiskTrigger object.
 */
function CCI_extractFiskTriggerFromHtmlElement(htmlElement) {
  const funcNameV = "CCI.extractFiskTriggerFromHtmlElement ";
  if (htmlElement === undefined) {
    throw new Error(funcNameV + "htmlElement is undefined.");
  }
  const attributeName = han_serviceTrigger_dataAttr;
  let attrWrap = CCI_extractJsonAttributeWrapFromHtmlElement(htmlElement, attributeName);
  let jsonObject = attrWrap.jsonObject;
  let nodeName = htmlElement.nodeName;
  if (nodeName == "FORM") {
    let fieldData = CCI_extractFieldDataFromForm(htmlElement);
    // The form data has priority.
    // THe form data should OVERWRITE any existing jsonObject properties with the same name.
    fieldData.fieldList.forEach((field, index) => {
      jsonObject[field.name] = field.value;
    });
  }
  let fiskTriggerWrap = CCI_extractFiskTriggerFromContents(jsonObject, attrWrap.href, htmlElement);
  return fiskTriggerWrap;
}

/**
 * Extract the form field values.
 * For each form field, use the field name as the property name,
 * and perform a JSON.parse() on the field value.
 * @param {Object} formElement; The form to extract fields from.
 * @param {string[]} fieldNameList; The list of field names to extract.
 * @return {ExtractedFormData} The extracted form data.
 */
function CCI_extractFieldDataFromForm(formElement) {
  let resultFieldList = [];
  let extractedData = {};
  extractedData.fieldSet = {};
  extractedData.fieldList = resultFieldList;
  // let formId = formElement.id;
  if (false) {
    /*
    let fieldList = {};
    fieldIdList.forEach(function (fieldId, index, array) {
        let fieldId = item.fieldId;
        // Create a special query selector. That fixes the colon and backslash characters.
        let fieldId_qs = fieldId.replace(/\\/g, "\\\\"); // escape the backslash char.
        fieldId_qs = fieldId_qs.replace(/:/g, "\\:"); // escape the colon char.
        let fieldElement = formElement.querySelector("#" + fieldId_qs);
        if (fieldElement == null) {
          throw new Error("Form is missing field with id=" + fieldId);
          // fieldElement = {};
        }
        // let fieldName = fieldElement.name;
        // let fieldValue = fieldElement.value;
        fieldList.push({name:fieldElement.name, value:fieldElement.value});
    });
    */
  } else {
    let formSnapshot = CCI_createFormSnapshot(formElement);
    let fieldNameList = formSnapshot.fieldList;
    // let formData = new FormData(formElement);
    // let fieldKeys = formData.keys();
    // let fieldNameList = Array.from(fieldKeys);
    fieldNameList.forEach(function (item, index, array) {
      let fieldName = item.n;
      if (fieldName === undefined || fieldName == null) {
        throw new Error("Form snapshot is invalid. Field name is undefined or null. Field index=" + index);
      }
      if (fieldName == "") {
        throw new Error("Form field has missing or empty 'name' attribute. Field index=" + index);
      }
      let snapshotField = CCI_getFieldItemFromSnapshot(fieldName, formSnapshot);
      let fieldType = snapshotField.t;
      // The "checkbox" and the "select-multiple" field types hold an array of values.
      // let fieldValueIsAnArray = formData.isValueAnArray(fieldName);
      // let fieldValue = formData.getValueCombined(fieldName); // Can be a single value or an array!
      // let fieldType = "text"; // formData.getType(fieldName);
      // let fieldType = formElement.querySelector("input[name=" + fieldName + "]").type;
      // let fieldValueIsAnArray = (fieldType == "checkbox" || fieldType == "select-multiple");
      // let fieldValue = fieldValueIsAnArray?formData.getAll(fieldName):formData.get(fieldName);
      let fieldValueIsAnArray = CCI_isSnapshotFieldValueAnArray(snapshotField);
      let fieldValue_s = snapshotField.v; // Can be a single value or an array!
      let fieldValue_j; // The JSON fieldValue. (Converted to a JSON object, array, etc.)
      let fieldValueIsAnArray2 = Array.isArray(fieldValue_s);
      if (fieldValueIsAnArray) {
        fieldValue_j = fieldValue_s; // A JSON Array.
      } else {
        if (fieldValue_s === undefined) {
          // Invalid field value. Skip it, or throw an error?
          throw new Error("Missing field value. Field name=" + fieldName);
          // fieldValue_j = undefined;
        } else if (fieldValue_s == null) {
          fieldValue_j = fieldValue_s; // null
        } else if (fieldValue_s == "") {
          // The user did not provide a value. Skip it?
          // Or, did the user provide the empty string as the value?
          // throw new Error("Invalid field value in form. name=" + fieldName + " value=''");
          fieldValue_j = fieldValue_s;
        } else {
          // Figure out what data type the value is. (Need to know if it's an unquoted string.)
          // JSON.parse() would *almost* work for all fieldValue types, but ...
          // WARNING. If fieldValue_s is an unquoted string, JSON.parse will fail. (i.e. "some text")
          // WARNING. When determining value type, leading spaces are ignored.
          // WARNING: To enforce a string type, quote the value. (If value starts with '{' '[' '0-9' etc.)
          let fieldValueTrim = fieldValue_s.trim();
          let isTypeUnquotedString = false;
          let valueType;
          let ch1 = fieldValueTrim.charAt(0);
          if (ch1 == "-") {
            // valueType = "number";
            if (fieldValueTrim.length > 1)
              ch1 = fieldValueTrim.charAt(1);
          }
          if (fieldValueTrim == "") {valueType = "unquotedString";
          } else if (fieldValueTrim == "null") {valueType="object";
          } else if (fieldValueTrim == "true") {valueType = "boolean";
          } else if (fieldValueTrim == "false") {valueType = "boolean";
          } else if (ch1 == "1" || ch1 == "2" || ch1 == "3" || ch1 == "4" || ch1 == "5") {valueType = "number";
          } else if (ch1 == "6" || ch1 == "7" || ch1 == "8" || ch1 == "9" || ch1 == "0") {valueType = "number";
          } else if (ch1 == "{" /* } */) {valueType = "object";
          } else if (ch1 == "[") {valueType = "array"; // ]
          } else if (ch1 == "\"" || ch1 == "'") {valueType = "quotedString";
          } else { valueType = "unquotedString"; isTypeUnquotedString = true;
          }
          if (isTypeUnquotedString) {
            fieldValue_j = fieldValue_s; // Do not use JSON.parse() on an unquoted string.
          } else {
            try { fieldValue_j = JSON.parse(fieldValue_s); }
            catch(e) {
              throw new Error("Invalid JSON value in form field. name=" + fieldName + " value=" + fieldValue_s + " First char (" + ch1 + ") implied JSON. Try quoting?");
              // fieldValue_j = fieldValue_s;
            }
          }
        }
      }
      let fieldItem = {name:fieldName, t:fieldType, value:fieldValue_j};
      // extractedData.fieldSet[fieldName] = fieldItem;
      resultFieldList.push(fieldItem);
    });
  }
  // extractedData.fieldSet = fieldSet;
  return extractedData;
}

Capis Default Values

The "Capis Default Values" are used when the FiskDigest in the HTML page does not specify a value. The Configuration Settings that have "specification defaults" are the serviceCatalogUri and similar properties.

  1. The default value of the name="serviceguidedata" attribute of the ServiceGuide HTML Element Element. (The value is the defaultValues.socSiteCatalogDocUri of the FiskCatalog.)
  2. The default value of FiskDigest.id is "offer_n". Where "n" is the index of the group in the HTML. (Starts with 1.) Example: "offer_1"
  3. The default value of FiskDigest.sgSpec is "capisSgSpec 0.1"
  4. The default value of RML FiskProfile.id is "protocol_n". (Where "protocol" is the value of the protocolUsed, and "n" is the index of the item in the serviceProfileList array.) (Starts with 1.) (The id of the FiskProfile.)
  5. The default value of RML FiskProfile.sdSpec is "remereSdSpec 0.1",
  6. The default value of RML FiskProfile.protocolUsed is "remere".
  7. The default value of RML FiskProfile.requestFormatList is ["remereRequestFormat 0.1"]
  8. The default value of RML Request.requestFormat is "remereRequestFormat 0.1".
  9. The default name of the HTTP parameter containing the Capis service request is "capis_request".
  10. The default directory name for "remere login authentication" files is "/_capis/remere/".
    Example FiskProfile:
    {
        "endpointBlock": {
          "id": "endpoint_1", "http_method": "POST", // Default http_method is "POST"
          "sUri": "/_capis/remere/requestOp.json", // REQUIRED. Define the endpoint URI.
          "supportedFormats": ["JWS"]
        },
        "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"},
    }
  11. The default directory name for "fallback" files is "/_capis/fallback".
    Example HTML Anchor:
    <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", ... }'>Login</a>

Capis Default Values: capis_request

Type string; Required. (A value must be specified.)

The default name of the HTTP parameter that contains the Capis service request. It is JSON data.

The browser sends the service request to the "Service Endpoint" in whatever manner the RP designated in the FiskProfile. (i.e. In the FiskCatalog.) Usually the method is an HTTP POST. (As a special case, if the ServiceTrigger HTML Element that activated Capis is a form, and Capis is configured to send an HTTP POST, then the browser will send the service request as a special HTTP parameter *in addition to* any regular form data. The default name of the HTTP parameter is capis_request.) See the Service Trigger Activation Protocol.

The RML Request JSON object contains a crProofSection" ("prs") property, and the "opIdReq" to be performed.

Remere Login User Auth Method (uauth_method)

How a user is authenticated. How he signs-in to an RP. A user must prove that they are who they claim to be. Several ways to do this are:

  1. prove access to a shared secret (such as a password)
  2. prove possession of a private key (by creating a key signature)
  3. prove that the user is authorized by a different RP. (i.e. a federated credential)
  4. prove ownership of a Locked Id.
  5. Some combination of the above

Using any of the "certificate_" variations (such as "only_MSUID") means that a type of "UserId certificate" is being used. Be careful to use to correct "csvm" value.

In general, which "authentication method" the browser chooses to use, will also determine what "csvm" value needs to be sent to the RP. "RML Request.sigInfo.csvm" is used for Certificate Signature Verification Protocol.

(The "csvm" values include "OIDCD", "BrowserID" and "RDAC".)

List of User Auth Methods (i.e. Login Methods)

List of user authentication methods. These can be supported by an RP in RML FiskProfile.supported.userAuthScheme. And used by the browser in RML Request.user_auth_method.

Remere Login UA Method - Value: "stored_cred"

The RP should use the stored set of Credential Verification Rule (e.g. passwords, public keys, etc.) to authenticate the user. This option will not work if the RP does not have any stored credentials for the user_login. This is the simplest form of user authentication. This is the default.

The RP must store some Credential Verification Rule (e.g. passwords, public keys, etc.) for each user.

This is the only user_auth_method for Single-site UserId>. (Or can without_MSUID be used too?)

The RP may choose to not support this user_auth_method. It may choose to support only the only_MSUID user_auth_method.

If the RP supports this option, then it (probably) means that the RP supports creating a "Single-site UserId" during login. (i.e. assigned_ulc or similar.)

Remere Login UA Method - Value: "without_MSUID"

The RP should not verify that the user owns the Locked Id. The RP should authenticate the claimed UserId using only the set of CVR that it has stored for the user. This option will not work if the RP does not have any stored credentials for the user_login.

The RP should not verify the ownership of the UserId. If the UserId is a Federated UserId the RP should not require or use the UserId certificate. Even if a UserId Certificate if provided by the browser. If the UserId is a Self-contained type, then the RP will not extract the lock requirements from inside the UserId.

This is a shortcut for browsers and the RP. It eliminates a lot of the work that usually is not necessary. It allows the use of a Locked Id during user authentication, but skips the step of verifying its ownership. This is possible because if the Multi-site UserId is recognized by the RP, (If there exists a user that uses the UserId), then the ownership of the Multi-site UserId was verified previously, during the "user login creation". (The login_creation method was probably "MSUID User Login Creation Method".)

This option is very useful if the RP already stores a set of CVR for the user. As long as the user has credentials, and chose sufficiently secure credentials, then it is not really necessary to require the user to also prove that he owns the UserId during every login. (i.e. to unlock the UserId.)

When using this option, the RML Request signatures that have certPK, should have their sigInfo.vkm value set to "site_stored_vks".

This method can only be used if the RP stores some kind of "Credential Verification Rule" (e.g. a public key, password, etc.) for the user. This method also requires the user to store a Credential Source for the RP in his Login Manager. (A password or private key, etc.)

See "without_MSUID" User Authentication Method Detail.

Remere Login UA Method - Value: "only_MSUID"

The RP should use only the Locked Id to authenticate the user. It should not use any of the credentials stored at the RP.

This option entirely relies on the proof that the user "owns" the UserId. If the UserId is a Federated UserId, then it requires a UserId certificate and entirely relies on the Identity Provider.

See only_MSUID-user-authentication-detail.

Remere Login UA Method - Value: "combo_MSUID"

The RP should use the active credentials stored at the RP AND the Locked Id proof of ownership. This process will prove that the user "owns" the UserId.

User Login Creation at an RP

How to create a new User Login at an RP. See opId="registerUserLogin". (A.K.A. user registration.) Used by RML FiskProfile.userLoginCreationRules.

Creates a new User Login at a website. This registers the current user of the RP. It assigns him a UserId and some type of login secrets. (i.e. Some Credential Verification Rules. A public/private key or "username and password", etc.) Creating a User Login will allow the RP website to be able to identify the same user in the future.

A User Login is not the same thing as an "account". This is because RPs may allow Account Sharing. That is, for multiple users to share the same account. Each user would get their own "user login", they would get their own UserId and password or public key, etc. but they would share a single "service account". Because of this, creating a new user login DOES NOT have to also create a new "service account" at the RP. Some RPs may require an additional step to "create an account". Normally, both the "user login" and the "service account" are created together. However, some times a user may not want to create their own account, they may want to "be added to a friend's existing account" instead. For this reason, there is an only_register option to specifically inform the RP to only "register" the user, and not to create a service account. (An RP may split the account creation process into two steps, so this flag may not be needed.)

Users create a new User Login at an RP in order to... be able to login again later. (1) Have a way to identify themselves to the RP (i.e. with a UserId) and (2) be able to prove that they are who they claim to be. (i.e. authenticate themselves to the RP.)

The user's browser creates a opId="registerUserLogin" RML Request, includes some public key(s) etc. and sends it to the RP. The RP then sends a RML Response back to the browser, with acknowledgment of user registration, or an error. The default behavior is for the RP website to include in the response the new assignedUserId that it has assigned to the user. However, some RP may allow users to choose their own UserId.

If a Locked Id is specified when creating a User Login, there is an extra step. The RP MUST VERIFY that the user can unlock the Locked Id before the RP can use it to create a User Login. An RP MUST NOT register a Locked Id to any of its users, unless the user can unlock the ID. Because that would allow someone who does not own the Multi-site UserId to create a login using the UserId. (This is a security risk)

An RP SHOULD NOT automatically create a user_login for a UserId in response to a opId="login" RML Request. For example, if a user attempts to login with a Locked Id, and the RP verifies the user unlocked the UserId, but the RP does not have a record of a user_login for that UserId, the RP MUST NOT automatically create a user_login for the current user. Instead, the RP must CONFIRM that the user wants to create a new user_login. This will prevent User Login creation due to user error. It is easy for a user to make a mistake, and accidentally select the wrong User Login to log in to an RP with. The user may already have an account at the RP, but with a different UserId. Or the user may want to change his existing account. For example, the user may want to change the account in order to allow the new UserId to access it. Because the RP does not know the user's intentions, automatically creating a new user_login would be a problem.

The RP may allow users to CHOOSE THEIR OWN UserId. The RP may provide the user with a list of suggested Single-site UserId. Or it may do something more creative and perform a dynamic lookup of what suggestion the user types in. Regardless, when the user activates the opId="registerUserLogin", the RML FiskTrigger.ulCreationCfg will contain the chosen UserId.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "registerUserLogin", "sub": "remere/registerUserLogin", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "abcdefg", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k", // The QIF UserId "timestamp_uid": "q3jC9-h" }, // "user_auth_method": "combo_MSUID", "user_login_creation_method": "claim_ms_ulc", "redirectUri": "/some/path/page.html",
// user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ],
"useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "JWT", // "format": "JWT" (not necessary. The default is "JWT".) "val_p": "mmmm" // This is the encoded JWT, see below. } ], }, // "credRegSec" credentialRegSection. Required for "registerUserLogin" and "setActiveCredentials" opIdReq. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "bupk": "qis:A:H:2:mK7HnbndYb746GvnYkrU", // The Backup User Public Key ("backup_upk") "bupk2": "none" } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "alg": "ES256", "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256"} "header": { "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", // "kid": "userIdPK" // "userIdPK" means the public key is in "userId". "vkm": "certificateF" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] } };
Example Certificate: The "UserId certificate" JWT. header and claims: (uic_chain[0])
let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, // REQUIRED "exp": 1503971430, // REQUIRED "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "uid": { "rawForLogin": "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k", // The QIF UserId }, "cert_pubKeyList": [ { "pkf": "JWK", "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." } ] };
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. "csvm": "idr_keystore" // Get the sig verification key from an "identity registrar keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

Account Creation at an RP

Users can create a User Login at an RP by using the opId="registerUserLogin" value. RPs publish their requirements for User Login creation in the RmlFiskProfile.userLoginCreationRules property.

When creating a User Login at an RP, a user must do a few things. In order to be able to login again later. (1) Have a way to identify themselves to the RP (i.e. with a UserId) and (2) be able to prove that they are who they claim to be. (i.e. authenticate themselves to the RP.)

Ways to create a UserId.
  1. The RP assigns a UserId.
  2. The RP provides a list of User Login creation options. (The user chooses from several options.)
  3. The RP makes suggestions. (i.e. interactivity between the user and the RP.)
  4. The user requests a UserId. The RP may approve the request.
    Usually, the RP publishes some list of requirements for the identifier. For example: The UserId must be "unused". There is a limited character set to choose from. There is a maximum length, etc. Limited interactivity (negotiation) with the RP is necessary, to find out if a desired UserId is already in use.
  5. Use a "Locked Id".
    It is a universally unique UserId, that is guaranteed to be "unused". (Because the UserId is RESERVED. The RP must not register it to a user, unless the user can unlock the ID. i.e. Prove he owns it.) Note: Multi-site UserIds may use some characters that are not available to "Single-site UserIds". Or may start with a unique prefix. (i.e. "qis:A:H:", "qis:A:F1:") See QIF UserId format.

Options 1, 2 and 3 are "Single-site UserIds". Because they only have meaning at that single RP.

There are two choices for how to set the Credential Verification Rule. (i.e. CVRs)
  1. Send the CVRs to the RP upon User Login creation. (i.e. password and/or public key, etc.) The RP will store the CVR associated with the UserId. It uses the CVR to authenticate the user on subsequent logins.
  2. Do not send any CVR to the RP on User Login creation. Or, the RP does not store any CVR. For a time, the RP relies exclusively on external forces to authenticate the user. This requires a Locked Id. (It requires more than a "Single-site UserId", because there is no password.) The RP can rely exclusively on the user's MSUID (on the CVR in the MSUID), a federated MSUID. etc.
The RP can also allow users to update the Credential Verification Rule that it stores for them. So both of the above cases are not permanent, and can be modified over time.

There must be some way of telling if an account already exists for the supplied username or MSUID. One way to do this is that the RP can require a "register-MSUID" package (i.e. a User Login creation) (the package contains a public key and username) sent with a form submission. (The form submission can include user name, email address, etc.)

When creating an account, the RP might allow additional Credential Verification Rule (i.e. public keys) to be set. (2nd factor authentication) The public keys can be sent to the RP in an additional RML Request.property. The keys can be in JWK format. When logging into an RP, the second signature can be sent to the RP in this second property. See "req_factor2".

See UserLogin Creation Methods for a list of the possible values.

List of User Login Creation Methods

The User Login Creation Methods. Used in the RmlFiskProfile.userLoginCreationRules.createMethods. Used in the rml.request.user_login_creation_method value. Used with the opId="registerUserLogin".

Previous values are SSUID_acm and MSUID_acm.

Login Creation Method - Value: "assigned_ulc"

The RP assigns a new Single-site UserId without any user intervention. The "assigned_ulc" value is the default. See rml.request.user_login_creation_method See RmlFiskProfile.userLoginCreationRules.createMethods.

When the RP is sent a opId="registerUserLogin". The RP sends back a new assignedUserId in the RML Response.

Login Creation Method - Value: "claim_ms_ulc" Claim a Multi-site UserId

The user claims a Locked Id as his UserId.

The RP may limit the types of Locked Id that it supports. See the user identifier type supported by the RP.

A simple way for a user to create a User Login at an RP. The user provides a Locked Id and proof of ownership of the UserId. This skips the hardest part of traditional User Login creation, having the user find (and remember) an unused Single-site UserId.

See MSUID_acm.

Login Creation Method - Value: "requested_ulc"

The user requests a specific UserId from the RP.

Used with RML FiskTrigger.ulCreationCfg and RML FiskProfile.userLoginCreationRules.

Login Creation Method - Value: "requested2_ulc"

The RP negotiates a UserId with the user.

The user performs some interactive process with the RP to negotiate a new Single-site UserId. This is only used when the user has NOT provided a Locked Id. And there is no UserId certificate.

In this case, there is no UserId Certificate. There is only the UserId Assertion, and the RP has the sig verification key for it. The RP stores some "Credential Verification Rule" (e.g. a public key) for each user. (The UserId Assertion should use a "sigInfo.vkm" value of "site_stored_vks".)

See SSUID_acm.
31 32 33 34 35 36 37 38

FiskProfile Detail

A FiskProfile is a Service Configuration Profile. (Service Order Configuration Settings) Pronounced "Sock Profile". It is a JSON object that contains a set of ConfigSettings that describe a Remote Service. It can include capabilities, requirements, service options, and how the Remote Service is to be called. FiskProfile objects are usually defined together in a FiskCatalog. (In the serviceProfileList property.)

Browsers use the FiskProfile objects to understand what types of services the RP provides/supports. A FiskProfile contains: (1) The id of the service. (2) The protocolUsed. (3) The endpointBlock URI. (i.e. What URI the browser should send the Service Request to.) (4) The other features that the service supports. See the RML FiskProfile. (The Capis FiskProfile objects may use JSON Web FiskProfile Format. JWSD)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "protocolVer": "1.0", "sdSpec": "remereSdSpec 0.1", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "requestFormatList": ["remereRequestFormat 0.1"] }

Capis FiskProfile - List of Properties

List of common FiskProfile properties.

RML FiskProfile Detail

A RML FiskProfile is a specific version of a FiskProfile. It is used for User Authentication.

A RML FiskProfile specifies the capabilities and requirements of an RP's "User Authentication service". It is one type of "Remote Service" and uses a custom "FiskProfile". (Currently the specification is "remereSdSpec 0.1")

A RML FiskProfile must be contained in a FiskDigest. (In the serviceProfileList.)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "protocolVer": "1.0", "sdSpec": "remereSdSpec 0.1", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "requestFormatList": ["remereRequestFormat 0.1"], "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]}, "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "mediation": "silent", /* One possible suggestion. Old style. "acceptableCredentials": { "password": {minEntropy: 64}, "federated": {providers: ["https://accounts.google.com", "https://www.facebook.com", ...]}, "sms": { checkBy: { send: function (number) {/*Ask the server to send an SMS*/}, "vouch": [googleKey, samsungKey, appleKey, ...], }, }, "publicKey": { // <-- Better name for webauthn credentials. "attestationChallenge": crypto.getRandomValues(sixteen_byte_buffer), "cryptoParameters": [{algorithm: "ES256"}], // Do we need a filter for acceptable attestation certificates? } }, */ "userLoginEditRules": { "comment_21": "Each ulAuthProfile details the number and type of credentials required for that configuration.", "userLoginAuthProfileList": [ { "id": "cfg1", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"pubKeyFirst"} ] }, { "id": "cfg2", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"passwordCred2"} ] } ], "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } ] }, "credentialReg": { "ruleTypes": {"pubkey1":"1"}, "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}], "required_ruleTypes": {"pubkey1":"1"} }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] }, "requirement_section": { } }
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", // Lists what Webauthn credential creation options the RP supports. "userLoginCreationOptions": { "publicKey": { // "Note that both the 'rp' and 'user' properties of Webauthn have been moved.", // "The 'rp' property was MOVED up a level. The 'user' property MOVED to 'rml.FiskProfile.user_info'.", // "pubKeyCredParams is an array of type PublicKeyCredentialParameters. The order is important.", // "This Relying Party will accept either an ES256 or RS256 credential, but prefers an ES256 credential.", // "COSE_algId is a COSEAlgorithmIdentifier", // "-7 is the value for ES256. As registered in the IANA COSE Algorithms registry", // "-257 is the value for RS256. As registered by the WebAuthn specification", "pubKeyCredParams": [ // Each item is a pubKeyCred. { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ], "timeout": 60000, "excludeCredentials": [], "extensions": {"loc": true}, "attestation": "none", "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": false, "userVerification": "preferred" } } }, // Lists what Webauthn credential request options the RP supports. "userLoginProofOptions": { publicKey": { // "allowCredentials is an array of type PublicKeyCredentialDescriptor.", // "The id fields in allowCredentials, will be automatically converted into a Uint8Array.", // "id conversion is: Uint8Array.from(window.atob(id), (c) => c.charCodeAt(0))", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don't care", "webauthnExample_foobar": 42 } } }, ... }

Each authentication FiskProfile MUST have a "FiskProfile.protocolUsed" and an "endpoint.sUri".

This protocol does not provide an attestation mechanism which allows RPs to identify the class of device and either accept it or not depending on the particular site's policy. Instead of telling the RP about the device, the RP publishes the class of device it accepts. "supported_device_type".

Note that all "supported" properties are arrays. (Example: FiskProfile.credentialReg.credentialReg.ruleTypes: ["pubkey"])

RML FiskProfile - List of Properties

RML FiskProfile Detail WebAuthn

FiskProfile - Property: FiskProfile.id

Type string; Default value: "protocol_n" Example: "remere_1"

Specifies the id of the service. If the "id" is not specified, then a value is automatically assigned. The default value is of the format "protocol_n". Example: "remere_1" The "protocol" is the value of the "protocolUsed" property in the FiskProfile. The "n" is the index of the FiskProfile in the serviceProfileList array. (Starts with 1.) (Note: If the protocol is not specified, the id will be set to "service_n". However, if the protocol is not specified, then the FiskProfile is invalid.)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id":"endpoint_1", "http_method":"POST", "sUri": "/_capis/remere/requestOp.json" }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"} }

Valid values are anything unique. Example: "remere_1", etc...

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

knownProtocols Detail:

Type string; Required. (A value must be specified.)

Known valid values are: " "remere", "webauthn", "exampleProtocol", etc.

"remere", "webauthn", "exampleProtocol", etc.

FiskProfile - Property: FiskProfile.protocolUsed

Type string; Required. (A value must be specified.)

Specifies the protocols handled by the service. For Remere Login the value must be "remere". For Webauthn, the value must be "webauthn".

Known valid values are listed in "knownProtocols". Examples: "remere", "webauthn", etc.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

FiskProfile - Property: FiskProfile.protocolVer

Type string; Default value: "1"

Specifies the maximum protocol version handled by the service.

FiskProfile - Property: FiskProfile.default_endpoint

Type string; Default value: "endpoint_n"

Specifies the default_endpoint of the service.

FiskProfile - Property: FiskProfile.sdSpec

Type string; Default value: "remereSdSpec 0.1"

Specifies the name and version of the "FiskProfile Specification" that the FiskProfile is written in. If there is no sdSpec specified, then the default sdSpec for the protocolUsed is used.

Different services can use different FiskProfile Specifications. They can define different properties, use different protocols, etc. Even though all the services are offered by the same RP.

Valid values are in the form of "remereSdSpec 0.1". See Capis Default Values.

Specifies the name and version of the specification that the RP used to write the FiskProfile. (A FiskProfile is used inside of a FiskDigest in the HTML page.) Note the difference between a sdSpec and a FiskDigest sgSpec.

All the property names and values the RP uses in a FiskProfile (in the HTML page, etc.) are defined by the "sdSpec" that the RP uses. With a few exceptions. There are a few Configuration Settings in a FiskDigest which are independent of the sdSpec, as they are used before the FiskCatalog is retrieved. They are the serviceCatalogUri and similar properties.

Valid values: "remereSdSpec 0.1".
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json" }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"} }

FiskProfile - Property: FiskProfile.endpointBlock

Type Object; At least one endpoint is required.

An "endpointBlock" object contains settings for the RP endpoint. An endpoint can be either a single item, or one of the items in the "endpointList" array. (If there is both an "endpointBlock" and an "endpointList" property, the "endpointList" is used.)

Defines an authentication service endpoint.

An endpoint has the following properties:

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json", "parameterList": [{"name":"capis_request", "type":"json_object"}], "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"} }

FiskProfile - Property: FiskProfile.endpointList

Type Endpoint[]; (Array of Endpoint) Required. At least one endpoint is required.

Defines the array of service endpoints.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointList": [ { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json", "parameterList": [{"name":"capis_request", "type":"json_object"}], "supportedFormats": ["JWS"] }, { "id": "endpoint_2", "http_method": "GET", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"], "parameterList": [{"name":"capis_request", "type":"json_object"}] } ], "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"} }

Endpoints typically receive HTTP POST requests and return an HTTP response. The response typically contains a single JSON object, a JSON document, that contains either return values (such as a success code) or an error object. That describes an error. For example, see RML Response Detail.

FiskProfile - Property: FiskProfile.endpoint.id

Type string; Default value: "endpoint_n" (Where "n" is an integer.)

Specifies the id of the endpoint. The default is "endpoint_n". Where "n" is the index of the item in the array. (Starts with 1.)

FiskProfile - Property: FiskProfile.endpoint.http_method

Type string; Default value: "POST"

Defines the type of HTTP request to send to the endpoint URI.

Valid "method" values are "POST" and "GET".

FiskProfile - Property: FiskProfile.endpoint.sUri

Type string; Required. (A value must be specified.)

Specifies the URI of the authentication service endpoint.

Must start with either "/" or "https:". The https protocol is always used. If it starts with "/", then "https:" is prepended.

FiskProfile - Property: FiskProfile.endpoint.parameterList

Type Array(ApiParam); Default value: [{"name":"capis_request", "type":"json_object"}]

Specifies the list of parameters in the Service Request data sent to the RP. Each parameter must have a "name" and a "type".

The same parameter names are used with both HTTP GET and POST methods.

Lists the parameters that the endpoint takes, in the order it takes them. All parameters are required by default, unless otherwise noted. (i.e. "isOptional":true) Most APIs require a particular number of parameters, in a particular order. The end parameters may be set as optional.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointList": [ { "id": "endpoint_1", "http_method": "POST", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, { "id": "endpoint_2", "http_method": "GET", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"], "parameterList": [{"name":"capis_request", "type":"json_object"}] } ], "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, ... }
Example GET URI: http://example.com/endpointUri?capis_request=%7B"JWS"%3A"xxx"%7D -- The encoded value for 'capis_request={"JWS":"xxx"}'

The parameterList is used to override the default parameterList that is used by the protocol. For example, the Remere Login protocol uses a default parameterList of [{name:"capis_request", type:"json_object"}].

The most common data type is "json_object". This means that the Protocol Handler will (attempt to) convert the parameter into a JSON object, before sending the request to the RP. The "type" value can be "string", "int", "number" (i.e. a real_number), "boolean", "json_object", or "json_array".

FiskProfile - Property: FiskProfile.endpoint.supportedFormats

Type string; Default value: "JWS"

Specifies the format of the request sent to the RP.

The default value of "JWS" is usually correct.

FiskProfile - Property: FiskProfile.requestFormatList

Type Array(FormatRec); Default value: ["remereRequestFormat 0.1"]

Specifies the list of request formats supported by the RP. In the order of priority. The name and version of the request format desired by the RP.

The default value of ["remereRequestFormat 0.1"] is usually correct. Whatever format the browser chooses to use, that value should be put in the RML Request.requestFormat.

FiskProfile - Property: FiskProfile.fallbackProtocol

Type Object; Default value: {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}

Specifies the fallback behavior if the browser does not support the protocol of the service. In the order of priority.

For example: If the browser does not support the "remere login" protocol, or if there is a problem using that protocol, then the backup behavior is for the browser to redirect the user to a page containing a standard login form. The default uri of the "login" page is "https://$domainName/_capis/fallback/remere/login.html".

The default value of {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"} is usually good enough. Note that the "$domainName" "$protocolId", "$fiskProfileId" and "$opId" strings are replaced with the actual values.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "mediation": "required", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

FiskProfile - Property: FiskProfile.ras_endpoint

Type string;

Specifies the id of the endpoint to use. The endpoint must exist in the serviceProfileList in the FiskCatalog.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "mediation": "required", "ras_endpoint": "endpoint_2" "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

RML FiskProfile - Property: FiskProfile.user_info

Type Object;

Specifies information about the user, to the user's browser, after User Login creation.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "mediation": "required", "user_info": {"username":"qis:A:A:2:M4j-q:XSAErLq"} "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

This property is used to inform the user's browser of the information the RP has, that might be of help to the user. For instance, that the user supplied to the RP. The browser can add this information to its internal data stores to augment the credentials, to help the user in the future.

Example FiskCatalog.rpInfo: Example: Inside the "user_info": {} data object:
user_info: {
  "id": "MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=",
  "name": "alex.p.mueller@example.com",
  "userDisplayName": "Alex P. Müller",
  "small_icon": "https://pics.example.com/images/00/p/aBjjjpqPb.png"
}

RML FiskProfile - Property: FiskProfile.appIdSpec

Type string; Default value: (the RP domain name)

Specifies the Auth Application Id (authAppId) to use. (When logging in, and when creating a new User Login.) If the authAppId is not specified, the default is to use the "current domain" as the authAppId. (i.e. The capId as the authAppId.) A authAppId is the id under which a Capis service (such as Remere Login) stores data. The RemereLogin API and the Login Manager stores the User Logins for an RP, or organization under the authAppId.

A authAppId is similar to a URI, however it does not have a scheme. (A scheme of "https" is assumed.) Example: "login.example.com". It may or may not have a path. (A path contains a slash and a file name after the slash.) Example: "example.com/remereApp/2022010100" The path may also contain additional parts, such as a GUID as a directory or file name. (Or as a field in the query string?)

The default is to use the capId as the authAppId. And that capId will be the only "read-only authorized capId" and "ulCreation authorized capId". (i.e. An capId that is allowed to access the User Logins that are associated with the authAppId.) However, a larger organization may set up multiple RPs to use the same authAppId. (Known as SSO. Single Sign On.) Access to a authAppId is validated by capId, so that only an "authorized" RP can access User Logins that use the authAppId.

What does appIdSpec do?

The appIdSpec is used in two ways.

An RP can set the appIdSpec value to whatever string value it wants. However, this will cause it to not work correctly. Because the browser enforces security restrictions. The capId of the current page is passed into these functions as a security parameter.

When finding existing User Login, the browser will only add those User Logins that MATCH the authAppId (and the altRapSourceList) to the result list.

When finding existing User Login, the "collectUserLogins" function searches on the authAppIdBase (and the altRapSourceList). The browser will pass in the capId of the current page. (The result list of User Login can be shown to the user.) The returned list of User Login will ONLY include those User Login that regard the capId argument as "authorized". The Login Manager will MATCH the capId with those authorized by the authAppId. (i.e. The Login Manager checks that the capId is authorized to use the authAppId.)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]} }, ... }
Examples of authAppId values:
  • "example.com"
  • "login.example.com"
  • "example.com/remereApp/2022010100"
  • "login.example.com/remereApp/2022010100"

How a User Login uses authAppId

Each User Login is created for, and is associated with, a single authAppId. (In a Login Manager, the authAppId is the "database key" that a User Login is tied to.) In a Login Manager, a set of User Login are found by searching on an authAppId. (See the LoginManager.collectUserLogins function.)

By default, a User Login is only authorized to be used by a single capId. (The capId that is in the base of the authAppId.) A User Login may allow additional capId to use it. (A User Login may define a list of authorized capId. See RmlFiskProfile.userLoginCreationRules.allowedRpIdRules. Possible change. The list of authorized capId may be defined by a *authAppId*, not by a User Login.) Because of this, a single capId may be authorized to use a wide set of User Logins. (Every User Login that lists that capId as authorized.) At a minimum, an RP is authorized to use (at least) all the User Login that are associated with an authAppId that contains its capId. (i.e. All the authAppId that contain the capId of the RP.)

An RP gets to decide which authAppId to use, or multiple authAppId that it wants to use. (Subject to it being authorized to use that authAppId.) See altRapSourceList.

An capId is used to address a "proof of identity"

A "proof-of-identity" MUST be addressed to a capId, or it is not valid. The "audience" field in a RML Request must contain the capId of the RP. If an RP receives a proof of identity with an "aud" value that does not match itself, then the proof is invalid, and the RP MUST NOT accept it. (i.e. an RP may only use a proof that is addressed correctly.) Same with the "authAppIdUsed" field. It must contain a known authAppId. This is another way that prevents a "proof of identity" from being reused. (The other is the challengeKey.) A generated proof cannot be used to login to any RP other than the one for which it was created. Note that using a non-standard "serviceCatalogUri" may require that the "audience" in the "proof of identity" be changed too. See the FiskCatalog FiskProfile.appIdSpec property.

What does a authAppId do?

A authAppId is used in two ways. (1) To create a new User Login. (2) To find existing User Login. (to login, etc.). (i.e. rml.opId "login".)

The RP gets to choose what authAppId to use during both scenarios. During user login creation and use. It can choose either a authAppId that matches the RP domain name, or one that is a parent domain of it.

For example, if a user login was created using the authAppId "login.example.com", and then the user tried to login at "example.com", the user login for "login.example.com" would NOT be visible. However, if the website at "login.example.com" forced the use of the authAppId "example.com" (not "login.example.com"), for "create" (so that it was used for both "create" and "use" scenarios), then the user would be able to use the same user login at both URLs.

This has both good points and bad points. A good point is that a domain (i.e. "example.com") cannot read the User Logins for any sub-domain. Another good point is that a sub-domain can force the *creation* of a user login using a parent domain. A bad point is that a sub-domain can *read* (i.e. access) all the User Logins for all parent domains. Another bad point, if the website initially used a sub-domain, and then wants to change to use a parent domain, that is impossible.

Webauthn --- Note: capId represents the caller’s RP ID. The RP ID defaults to being the caller’s origin's effective domain unless the caller has explicitly set opArgs.capId or FiskProfile.capId when calling get().

The default value

The default value for a authAppId is simply the capId of the current page. It is the RP's domain name, without any scheme, port number or path component. (A authAppId may include a path component, but the default value has none.) Example: If the current page is "http://login.example.com/some/place/like/home.html", then the default value of authAppId is "login.example.com".

RESTRICTIONS

When creating a User Login, the appIdSpec value must be based on the web site's origin. (The authAppId must start with the capId.) All the RP can do is make the appIdSpec less specific. For example, if a web site is served on "help.login.example.com", the RP can set its appIdSpec to be "login.example.com" or "example.com". This makes the appIdSpec LESS specific than the default. This will cause, during User Login creation, the new User Login be tied to the LESS specific domain. (That might mean that the User Login can be accessed by capId with different sub-domains, like "site3.example.com".)

Alternate. The appIdSpec may denote a JSON file. If it does, then in order to create a User Login, the file is retrieved (via HTTPS). (In this case, the URI is unrestricted and may be at any domain name. i.e. The appIdSpec does not have to start with the capId of the current page.) The JSON file may contain a list of ulCreation authorized capId. i.e. A list of capId that are allowed to CREATE new User Logins using that authAppId. (i.e. a "capIdAccessList") If the capId of the current page is authorized by the list, then it is allowed to create a new User Login. The file may also contain a list of capId that are allowed to FIND and use those User Logins created with the appIdSpec. (i.e. a RmlFiskProfile.userLoginCreationRules.capAccessList) In this case, that list is made the authorized capId list. Similar to RmlFiskProfile.userLoginCreationRules.allowedRpIdRules.

When creating a new User Login. The RP may specify a appIdSpec value that is different from the default. It will work as long as the new authAppId value starts with a registrable domain suffix of, or is equal to, the caller’s origin's effective domain. (The authAppId value before the path component is restricted in this manner.)

When finding existing User Login. If appIdSpec is not present, then set appIdSpec to effectiveDomain. (i.e. the capId of the RP.) Otherwise: If the domain name in authAppId (before the slash) is not a registrable domain suffix of and is not equal to effectiveDomain, return a DOMException whose name is "SecurityError", and terminate this algorithm. Set authAppId to opArgs.authAppId. Note: the authAppId before the slash represents the caller’s RP ID. The authAppId specifies an attribute of the User Logins. The authAppId defaults to being the caller’s origin's effective domain unless the caller has explicitly set authAppId when calling remereLogin_api.performServiceOp -- and/or LoginManager.collectUserLogins().

Example HTML ServiceGuide:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/>
See the example_mainFiskCatalog.json document.

RML FiskProfile - Property: FiskProfile.FIDO_appId (longName: "fubar")

Type string;

No longer in use. This refers to an old specification used by FIDO.

Specifies one or more facets of the same application. A facet is either a domain name, sub-domain name, or an Android or iOS app key.

This specification defines a "Key Storage" system. This storage system is not part of a web browser, but is remotely accessible from a web browser, and from other applications. The system can be running on a device such as a phone and the applications may be phone applications. (Android or iOS applications.) The "Key Storage" system may be a physical device, (such as a USB stick) or it may be remote (i.e. running on an internet server). The "Key Storage" system assigns a "KeyHandle" to every key that it generates. A web browser, or other application, accesses the keys in the "Key Storage" by providing a keyHandle, and an AppID.

The "Key Storage" stores keys based on the RP. However, if that was the case, then all applications that wanted to access

If applications accessed keys with only a keyHandle, then there could be a malicious app, installed on the device (on the phone or desktop computer) that can bypass the security. It can try every possible keyHandle, in order to steal every key. Because of this, having only a keyHandle is too little security. Instead, an application must PROVE that it is allowed to access the "Key Storage" system for the desired keyHandle. If applications accessed keys by providing both a keyHandle and an RP identification string.

An appId value allows each of the specified facets to use the same authentication. A user can use a web browser, or an app on their mobile phone, and use the same credential to authenticate themselves to the application. The same username and public key, etc. Without an appId, each different application would have to have its own method of authentication. (A different username and password for each web browser, and for each mobile phone app.)

Most of the time, an appId is not needed.

The default is for the appId to be missing from the FiskCatalog entirely. In this case, the browser uses a "default value" for the appId. This consists of the

Examples:
  • "https://RP_example.com"
  • "/app-id.json"
  • "https://example.com/app-id.json"

The specification must not allow one RP to access the credentials of another RP. What set of Web origins and native applications (facets) make up a single logical application and how can they be reliably identified? How can we avoid making the user register a new key for each web browser or application on their device that accesses services controlled by the same target entity? How can access to registered keys be shared without violating the security guarantees around application isolation and protection from malicious code that users expect on their devices? How can a user roam credentials between multiple devices, each with a user-friendly Trusted Computing Base for FIDO?

Default is the page origin. Example: "https://RP_example.com:2030"

The appId, if it exists, must be a JSON object that lists the facets of the application.

The appId document must be retrieved using an anonymous fetch. That is, the HTTP GET used to retrieve the document MUST not have any cookies, authentication, Origin or Referrer headers, and must not use any TLS certificates or other credentials. The response delivering the document MUST have a MIME Content-Type of "application/json".

The appId only needs to be specified if the application can be reached through multiple urls and/or mobile. If connecting to the app on a different url, but it has the same app-id, it instructs the browser (or app) to use the same proof (use the same private key on both urls). Without the app-id, accessing the service via different means would use different private keys. See App ID and FIDO Alliance Specification .

Example FiskCatalog:
{
  "trustedFacets": [{
    "version": { "major": 1, "minor" : 0 },
    "ids": [
        "https://login.example.com",
        "https://secure.example.com",
        "android:apk-key-hash:585215fd5153209a7e246f53286035838a0be227"
    ]
  }]
}

RML FiskProfile - Property: FiskProfile.loginConfig

Type Object; Required. (A value must be specified.)

Specifies the extended User Login information.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]} }, ... }

RML FiskProfile - Property: FiskProfile.loginConfig.altRapSourceList

The list of "Foreign User Login Service Apps" (foreign authAppId) supported by the RP. That is, the list of Foreign RP from which the RP is willing and able to use a Login Item to authenticate the user.

This option allows the RP to receive and process User Login information that belongs to the listed Foreign RP. That is, the RP can use User Logins that belong to the Foreign RP website. (Those User Logins that are normally used to login the user to the foreign website.)

This option is for websites that want to use the User Login created by a different website. (i.e. To share login credentials.) To allow the user to login to the RP website with the username and credentials for a different website. For example, a set of websites may only have one username and set of credentials for a user. (All are shared for the websites in the group). This is also useful if a website changes its domain name. The new website can continue to use the same logins as the old website.

Contains two lists. (1) "altRapSourceList" is other RP that are alternate sources for credentials. (2) RmlFiskProfile.userLoginCreationRules.allowedRpIdRules contains Foreign RP that are allowed to use this RP's credentials.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]} }, ... }

RML FiskProfile - Property: FiskProfile.loginConfig.capAccessList (longName: "Permit Sharing With RP")

Type Object;

Contains the Foreign RP access rules. The list of cap groups that are allowed to access the appIdSpec. (i.e. A UserLogin that uses the authAppId specified in the appIdSpec.)

The list of Foreign RP that this RP allows its User Logins to be shared with. This authorizes the Foreign RP to use Logins from this RP as a "Foreign Login Source".

The Foreign RP must also list this RP as one of the "supported Foreign Login Source". So that it can ask its users to login using logins from this RP. See altRapSourceList.

The "capAccessList" option is for websites that want to share this RP's login credentials with other websites. This will allow a user to login to OTHER websites with the username and credentials for THIS RP.

Contains a list. (2) Other rp that are allowed to use this rp's credentials.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

RML FiskProfile - Property: FiskProfile.mediation

Type string;

Specifies if the RP requires user presence and approval.

If the service requires user intervention.

"mediation" value options are: "silent", "optional", "required"

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "mediation": "required", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

RML FiskProfile - Property: FiskProfile.displayInfo (longName: "fubar")

Type Object;

Specifies custom names and icons for the site. Can be displayed to the user in the identity login box.

Example FiskProfile:
{ "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "displayInfo": { "loginIcon": "/images/logo.png" } }

RML FiskProfile - Property: FiskProfile.userLoginCreationRules

Type Object; Default value: {"createMethods":["assigned_ulc"]}

Specifies the "User Login creation" options that the RP supports. The values must be from the UserLogin Creation Methods. (Used with the opId="registerUserLogin".) Used in the RML Request.user_login_creation_method value.

Specifies the Single-site UserId to use during User Login creation. (i.e. When registering the user.) The value must be one of the set of UserId created by the RP, and given to the browser to choose from.

This value is used by the requested_ulc User Login Creation Method.

TODO, WARNING: THE userIdGenMethods PROPERTY MAY BE REMOVED. This property really isn't needed.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "mediation": "required", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "userIdGenMethods": ["st1","ct1"], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

The RmlFiskProfile.userLoginCreationRules value is an object. The object can have the "userIdGenMethods" property. The "st1" value means "server timestamp format #1". The "ct1" value means "client timestamp values format #1"

The RML FiskProfile userLoginCreationRules value tells the browser what the RP's rules are. The RML FiskTrigger ulCreationCfg asks the RP to change its default behavior.

This property is used to specify a Single-site UserId. (As opposed to a Locked Id.) Therefore, it is only used when the RP supports both User Login creation and account login using Single-site UserIds. (Which is the default.) Specifically, the RP must support Single-site UserIds (i.e. the "single_site_userId" RmlFiskProfile.userLoginCreationRules.userIdTypes), and it must be capable of storing Credential Verification Rules (i.e. the "stored_cred" supported.userAuthScheme).

With this configuration option, the RP can provide help to the user when choosing a Single-site UserId, or validate the UserId, before the browser sends the "authentication request" to the RP.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100" "userLoginCreationRules": { "userIdTypes": ["single_site_userId"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "supported": { } }

RML FiskProfile - Property: FiskProfile.userLoginCreationRules.userIdTypes

Type Array(ApiType); Default value: ["single_site_userId"]

Specifies the list of "user identifier types" the RP supports. For use in the UserId. Default is ["single_site_userId"].

An "identifier type" can be either one of the defined values (i.e. "single_site_userId"), or it can be a QIF type specifier. (i.e. contain the text "qis:", a namespace and type.) For example, either "single_site_userId" or "qis:A:H:9:ri", etc. An RP can choose to trust only QIF identifiers with a particular namespace. Or it can only trust those in a namespace that also have a certain subtype (such as "qis:A:H:9:ri"), etc.

The value is also used in the UserLogin Creation Methods. When the "claim_ms_ulc" RmlFiskProfile.userLoginCreationRules.createMethods is used. It limits what type of identifier can be used to identify a user at the RP.

Values include "single_site_userId", "qis:A:H:9:ri", "federated", etc.

(Used by the "RML Request.uic.format" property.)

Example: ["single_site_userId", "qis:A:H:9:ri", ...]

single_site_userId value:

The "single_site_userId" value means that the RP MUST be capable of storing some sort of Credential Verification Rule for the users. (a password, or public key, etc.) When the RP receives the "proof of identity", it will validate the proof using the Credential Verification Rule stored for the claimed UserId.

RML FiskProfile - Property: FiskProfile.userLoginCreationRules.createMethods

Type Array(UlCreation); Default value: ["assigned_ulc"]

Specifies a list of "User Login creation" methods the RP supports. (Used with the opId="registerUserLogin".) Used in the RML Request.user_login_creation_method value.

See UserLogin Creation Methods for a list of the possible values.

Valid values are "assigned_ulc", "claim_ms_ulc", "requested_ulc" etc.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100" "userLoginCreationRules": { "userIdTypes": ["single_site_userId"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "supported": { } }

RML FiskProfile - Property: FiskProfile.userLoginCreationRules.allowedRpIdRules (longName: "Permit Sharing With RP")

Type Object;

Contains the Foreign RP sharing rules.

The "rpMatch" is the list of Foreign RP that this RP allows its User Logins to be shared with. This authorizes the Foreign RP to use Logins from this RP as a "Foreign Login Source".

The Foreign RP must also list this RP as one of the "supported Foreign Login Source". So that it can ask its users to login using logins from this RP. See altRapSourceList.

The "allowedRpIdRules" option is for websites that want to share this RP's login credentials with other websites. This will allow a user to login to OTHER websites with the username and credentials for THIS RP.

Contains a list. (2) Other rp that are allowed to use this rp's credentials.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]}, "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

RML FiskProfile - Property: FiskProfile.userLoginCreationRules.capAccessList (longName: "Permit Sharing With RP")

Type Object;

Contains the Foreign RP sharing rules.

The list of Foreign RP that this RP allows its User Logins to be shared with. This authorizes the Foreign RP to use Logins from this RP as a "Foreign Login Source".

The Foreign RP must also list this RP as one of the "supported Foreign Login Source". So that it can ask its users to login using logins from this RP. See altRapSourceList.

The "capAccessList" option is for websites that want to share this RP's login credentials with other websites. This will allow a user to login to OTHER websites with the username and credentials for THIS RP.

Contains a list. (2) Other rp that are allowed to use this rp's credentials.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "appIdSpec": "example.com/remereApp/2022010100", "loginConfig": { "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, ... }

RML FiskProfile - Property: FiskProfile.userLoginCreationRules.capIdAccessList (longName: "fubar")

Type string;

The list of capId that are allowed to create a new User Login. (Using the appIdSpec as the authAppId.) These are ulCreation authorized capId. (for User Login creation)

If the userLoginCreationRules.capIdAccessList is not specified (or is empty) then the default rules of appIdSpec apply. (Only the capId that the appIdSpec starts with is authorized. Or, if the appIdSpec is a JSON file, then it can contain the userLoginCreationRules.capIdAccessList.)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required", "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "capIdAccessList: ["login.example.com", "example2.com"], "user_info": {"username":"qis:A:A:2:M4j-q:XSAErLq"} }

RML FiskProfile - Property: FiskProfile.supported

Type Object; Required. (A value must be specified.)

The section containing all the protocols, etc. that the RP supports.

Contains the protocols, etc. that are supported.

RML FiskProfile - Property: FiskProfile.supported.userAuthScheme

Type Array(); Default value: ["only_MSUID"]

Specifies a list of "Remere Login UA methods" that the RP supports. See the Login Method List. For example, "stored_cred".

The values in this property are used by the "user_auth_method" property.

This property contains a list of which authentication methods can be used to login to the RP.

RML FiskProfile - Property: FiskProfile.supported.uic

Type Object;

Specifies a UserId certificate information object.

RML FiskProfile - Property: FiskProfile.supported.uic.format (longName: "supported.uic.format")

Type Array(); Default value: ["JWS"]

TODO. MAY_BE_REMOVED. Identity certificates are only used when a person uses a "Federated UserId". (i.e. A type of Locked Id that uses an "identity provider".)

Specifies a list of the "UserId certificate formats" the RP supports. RPs must support at least "JWS".

(Used by the RML Request.uic.format property.)

Values include "JWS-CS" ("JWS-Compact Serialization"), "JWE-CS" (JWE-Compact Serialization), "JWS" (Long form JWS-JSON), "chain_42".

If "chain_42" is used, then the "uic.ucv" is not used, and the "user_id_certificate_chain" is used instead.

RML FiskProfile - Property: FiskProfile.supported.uic.csvm (longName: "supported.uid_cert.sig_ver_method")

Type Array(); Default value: ["basic1"]

TODO. MAY_BE_REMOVED. Identity certificates are only used when a person uses a "Federated UserId". (i.e. A type of Locked Id that uses an "identity provider".)

Specifies a list of "UserId certificate signature verification methods" the RP supports. RPs must support at least "basic1".

(Used by the RML Request.uic.csvm property.)

The RP uses the "supported.uic.csvm" value in an attempt to verify the UserId certificate. To do this, typically the RP must get a list of signature verification keys from the issuer of the certificate. This is to verify the UserId certificate actually was signed by the issuer. Thus, in a lot of cases, the "supported.uic.csvm" value is used for Certificate Signature Verification Protocol.

Valid values are "basic1", "basic2". (These include "OIDCD" and "BrowserID".)

TODO MAY_BE_REMOVED (X.509 certificate chain and thumbprints, JSON web key and url, etc.) Prefix an entry with "-" (minus sign, dash) to drop support for it. Example: "supported.uic.csvm": ["basic2","-basic1"]

RML FiskProfile - Property: FiskProfile.supported.credentialProof.formats (longName: "supported.credentialProof.formats")

Type Array(); Default value: ["TBD-CPF"]

Specifies a list of the Credential Verification Rule "formats" the RP supports. RPs must support at least "TBD-CPF".

(Used by the RML Request.credentialProof.fmt_p property.)

It is expected that this attribute is rarely used, as the default will suffice. However, it is here in case an RP wants to use something else.

Known values are "TBD-CPF" and "fmt_cert_42".

RML FiskProfile - Property: FiskProfile.credentialReg.ruleTypes

Lists the types of Credential Verification Rule that the RP can store for users. (The RML Request.credentialRegistration.type (typ_r) property should be one of these values.)

Valid values are "password", "pubkey", "federated".

(CVR types define types of CVR. User Identifier Types define types of identifiers.)

RML FiskProfile - Property: FiskProfile.credentialReg

Type Object; Default value: {"publicKeyTypes":[{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}]}

Lists the "Credential Verification Rule" types and number that the RP can store for users.

Valid property names are "credentialReg.passwordTypes", "credentialReg.publicKeyTypes", "credentialReg.federatedTypes", "HOTP", "TOTP".

"password" uses the "credentialReg.passwordTypes. HOTP protocol.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "ruleTypes": {"pubkey":"1"}, "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}], "required_ruleTypes": {"pubkey1":"1"} // The RP requires a "public key" authentication. }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] }, "requirement_section": {} }

RML FiskProfile - Property: FiskProfile.credentialReg.passwordTypes

Type Object;

Lists the "Credential Verification Rule" "password" type and number that the RP can store for users.

RML FiskProfile - Property: FiskProfile.credentialReg.publicKeyTypes

Type Object; Default value: {"size":"1"}

Lists the "Credential Verification Rule" "pubkey" type and number that the RP can store for users.

Valid properties are "size". An RP can declare it does NOT support storing any public keys for the user by either setting "size": "0" or setting "pubkey":{}.

RML FiskProfile - Property: FiskProfile.credentialReg.publicKeyTypes.JWA_alg

Type Object; Default value: "ES256"

The JWK algorithm of the public key Credential Verification Rule being sent to the RP in the CVR section.

Valid values are "ES256", "RS256", "RS384", "RS512". See RML Request.JWS.sig_alg and coseAlgId.

RML FiskProfile - Property: FiskProfile.credentialReg.federatedTypes

Type Object;

Lists the "Credential Verification Rule" "federated" type and number that the RP can store for users.

See federated credential.

RML FiskProfile - Property: FiskProfile.credentialReg.required_ruleTypes

Type Object;

The type and number of "Credential Verification Rule" that the RP REQUIRES.

Valid property names are whatever types are defined in "credentialReg". Examples are: "pubkey1", "password1", "federated1".

Tells the browser what the RP requires that the "proof of identity" contain. A common "required" Credential Verification Rule (CVR) is a public key. A "supported public-key" CVR means that the RP must be able to store a "public key" for each of its users. (Should a user choose to sign-in with a public key.) Being a "required" CVR means that the RP requires the user to send a digital signature signed by the private key. (Provides additional security.)

RML FiskProfile - Property: FiskProfile.credentialReg.required_ruleTypes.pubkey1

Type Object; May be Required?.

RML FiskProfile - Property: FiskProfile.supported.factor2

Type Array();

Specifies the types of 2nd factor authentication that are supported. If the attribute is omitted, no 2nd factor is supported. The most common value is "JWT". Used to support a 2nd authentication form. Tells the browser to submit a second JWT, signed by the user's private key for the RP.

RML FiskProfile - Property: FiskProfile.requirements

Type Object;

The section containing all the protocols, etc. that the RP requires.

Contains all the requirements of the Service. The protocols, etc.

RML FiskProfile - Property: FiskProfile.requirement_section.factor2

Type string;

Specifies the type of 2nd factor authentication required. If the attribute is omitted, no 2nd factor is required. The most common value is "JWT". Used to require a 2nd authentication form. Tells the browser to submit a second JWT, signed by the user's private key for the RP.

RML FiskProfile - Property: FiskProfile.userLoginEditRules

Type Object; Required. (A value must be specified.)

Specifies the RP requirements for User Logins. Including creating, assigning and activating credentials. i.e. This lists the RP requirements for creating and modifying a User Login.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", ... "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"capisdemo.example2.com", "all-subdomains":"yes"}]} }, "userLoginEditRules": { "comment_21": "Each ulAuthProfile details the number and type of credentials required for that configuration.", "userLoginAuthProfileList": [ { "id": "cfg1", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"pubKeyFirst"} ] }, { "id": "cfg2", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"passwordCred2"} ] } ], "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } ] } }

RML FiskProfile - Property: FiskProfile.userLoginAuthProfileList

Type Array(ulAuthProfile); Required. (A value must be specified.)

An array of ulAuthProfile objects. Each ulAuthProfile object details the requirements for authenticating a user.

The credentialInfoList contains credentialInfo items. These items can have a "type" that is one of "public-key", "password", "federated", or "module". The "module" value means that the credentialInfo definition exists in a different place, in the credentialInfoModules.

RML FiskProfile - Property: FiskProfile.ulAuthProfile

Type Object;

A Login Credential Profile object. Each ulAuthProfile object details the requirements for authenticating a user. Each ulAuthProfile contains a set of required credentials. If a user chooses the ulAuthProfile, then they have to create and use that specified set of credentials in order to login.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", ... "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"capisdemo.example2.com", "all-subdomains":"yes"}]} }, "userLoginEditRules": { "comment_21": "Each ulAuthProfile details the number and type of credentials required for that configuration.", "userLoginAuthProfileList": [ { "id": "cfg1", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"pubKeyFirst"} ] }, { "id": "cfg2", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"passwordCred2"} ] } ], "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } ] } }

RML FiskProfile - Property: FiskProfile.credentialInfoModules

Type Object; Required. (A value must be specified.)

An array of credentialInfoModule objects. Each credentialInfoModule object details one of the credential requirements, for authenticating a user.

The credentialModuleInfoList contains credentialModuleInfo items. These items can have a "type" that is one of "public-key", "password", "federated".

Example FiskProfile.userLoginAuthProfileList:
"userLoginAuthProfileList": {
}
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", ... "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"capisdemo.example2.com", "all-subdomains":"yes"}]} }, "userLoginEditRules": { "comment_21": "Each ulAuthProfile details the number and type of credentials required for that configuration.", "userLoginAuthProfileList": [ { "id": "cfg1", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"pubKeyFirst"} ] }, { "id": "cfg2", "allow": "always", "credentialInfoList": [ {"type":"module", "mid":"passwordCred2"} ] } ], "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } ] } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions

Type Object; Default value: {(see details)}

Lists the ConfigSettings for creating a credential. (i.e. a Credential Verification Rule.)

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginCreationOptions": { "publicKey": { "comment_0a": "Note that both the 'rp' and 'user' properties of Webauthn have been moved.", "comment_0b": "The 'rp' property was MOVED to FiskDigest.'rpInfo'. The 'user' property MOVED to the 'serviceguidedata' HTML attribute.", "comment_1": "rp is of type PublicKeyCredentialRpEntity (Relying Party)", "comment_2": "user is of type PublicKeyCredentialUserEntity", "comment_2b": "The id field in user, will be automatically converted into a Uint8Array.", "comment_2c": "id conversion is: Uint8Array.from(window.atob(id), (c) => c.charCodeAt(0))", "comment_3": "pubKeyCredParams is an array of type PublicKeyCredentialParameters. The order is important.", "comment_3b": "This Relying Party will accept either an ES256 or RS256 credential, but prefers an ES256 credential.", "comment_3bb": "COSE_algId is a COSEAlgorithmIdentifier", "comment_3c": "-7 is the value for ES256. As registered in the IANA COSE Algorithms registry", "comment_3d": "-257 is the value for RS256. As registered by the WebAuthn specification", "comment_4": "timeout is of type unsigned long. 60000 milliseconds = 1 minute", "comment_5": "excludeCredentials is an array of type PublicKeyCredentialDescriptor", "comment_5b": "In this example, there is No exclude list of PKCredDescriptors", "comment_6": "extensions is of type UserAuthenticationExtensionsClientInputs", "comment_6b": "An extension of {loc:true} means Include location information in attestation.", "comment_7": "attestation is of type AttestationConveyancePreference. values are none, direct, indirect", "comment_8": "authenticatorSelection is of type UserAuthenticatorSelectionCriteria", "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rp_MOVED": { "comment_1": "rp property was moved to FiskCatalog.rpInfo.", "name": "ACME Corporation", "icon": "http://example.com/some/path/icon.png", "id": "https://login.example.com" }, "user_MOVED": { "comment_1": "user property was moved. renamed to userCreation." }, "userCreation": { "id_comment": "id is of type BufferSource. id will be converted. Uint8Array.from(window.atob(id), (c) => c.charCodeAt(0))", "id": "MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII=", "name": "alex.p.mueller@example.com", "userDisplayName": "Alex P. Müller", "icon": "https://pics.example.com/images/00/p/aBjjjpqPb.png" }, "pubKeyCredParams": [ // Each item is a pubKeyCred. { "type": "public-key", "coseAlgId": -7 }, { "type": "public-key", "coseAlgId": -257 } ], "timeout": 60000, "excludeCredentials": [], "extensions": {"loc": true}, "attestation": "none", "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": false, "userVerification": "preferred" } } } "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.publicKey

Type Object;

Lists the ConfigSettings for creating a Public Key credential.

(i.e. a Credential Verification Rule.)
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginCreationOptions": { "publicKey": { "pubKeyCredParams": [ // Each item is a pubKeyCred. { "type": "public-key", "coseAlgId": -7 }, { "type": "public-key", "coseAlgId": -257 } ], "timeout": 60000, "excludeCredentials": [], "extensions": {"loc": true}, "attestation": "none", "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": false, "userVerification": "preferred" } } } "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.publicKeyCred

Type Object;

Lists the ConfigSettings for creating a Public Key credential.

(i.e. a Credential Verification Rule.)
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginCreationOptions": { "publicKey": { "pubKeyCredParams": [ // Each item is a pubKeyCred. { "type": "public-key", "coseAlgId": -7 }, { "type": "public-key", "coseAlgId": -257 } ], "timeout": 60000, "excludeCredentials": [], "extensions": {"loc": true}, "attestation": "none", "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": false, "userVerification": "preferred" } } } "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.pubKeyCred.JWA_alg

Type string; Default value: "ES256"

Lists the ConfigSettings for creating a Public Key credential.

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.pubKeyCred.cci

Type int. Default value: none

Lists the ConfigSettings for creating a Public Key credential.

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.pubKeyCred.type

Type string; Default value: none

Lists the ConfigSettings for creating a Public Key credential.

WebAuthn FiskProfile - Property: FiskProfile.userLoginCreationOptions.pubKeyCred.coseAlgId

Type int. Default value: -7 (i.e. JWA "ES256".)

Lists the ConfigSettings for creating a Public Key credential.

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginCreationOptions": { "publicKey": { "pubKeyCredParams": [ // Each item is a pubKeyCred. { "type": "public-key", "coseAlgId": -7 }, { "type": "public-key", "coseAlgId": -257 } ], "timeout": 60000, "excludeCredentials": [], "extensions": {"loc": true}, "attestation": "none", "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": false, "userVerification": "preferred" } } } "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginProofOptions

Type Object;

Lists the ConfigSettings for creating a credential assertion.

"comment_1": "capId is of type USVString", "comment_2": "timeout is of type unsigned long. 60000 milliseconds = 1 minute", "comment_3": "allowCredentials is an array of type PublicKeyCredentialDescriptor.", "comment_3b": "The id fields in allowCredentials, will be automatically converted into a Uint8Array.", "comment_3c": "id conversion is: Uint8Array.from(window.atob(id), (c) => c.charCodeAt(0))", "comment_3d": "allowCredentials item, transports values are usb, nfc, ble", "comment_3e": "allowCredentials item can omit the id field? Example 8 in webauthn", "comment_5": "userVerification is of type UserVerificationRequirement", "comment_6": "extensions is of type UserAuthenticationExtensionsClientInputs",

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

WebAuthn FiskProfile - Property: FiskProfile.userLoginProofOptions.publicKey

Type Object;

Lists the ConfigSettings for creating a credential assertion.

"comment_1": "capId is of type USVString", "comment_2": "timeout is of type unsigned long. 60000 milliseconds = 1 minute", "comment_3": "allowCredentials is an array of type PublicKeyCredentialDescriptor.", "comment_3b": "The id fields in allowCredentials, will be automatically converted into a Uint8Array.", "comment_3c": "id conversion is: Uint8Array.from(window.atob(id), (c) => c.charCodeAt(0))", "comment_3d": "allowCredentials item, transports values are usb, nfc, ble", "comment_3e": "allowCredentials item can omit the id field? Example 8 in webauthn", "comment_5": "userVerification is of type UserVerificationRequirement", "comment_6": "extensions is of type UserAuthenticationExtensionsClientInputs",

Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "webauthn", ... "userLoginProofOptions": { "publicKey": { "challenge_MOVED": "challenge property was moved to FiskDigest.challengeKey.", "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rpId_MOVED": "rpId property was moved to FiskProfile.appIdSpec. Or to FiskCatalog.rpInfo.", "rpId_old": "login.example.com", "timeout": 60000, "allowCredentials": [ { "type":"public-key" }, { "type":"public-key", "id":"!!!!!!!hi there!!!!!!!\n" }, { "type":"public-key", "id":"roses are red, violets are blue\n" }, { "type":"public-key", "transports": [] } ], "userVerification": "preferred", "extensions": { "txAuthSimple": "Wave your hands in the air like you just don’t care", "webauthnExample_foobar": 42 } } } }

RML Request Detail

The Remere Login Service Request (RML Request) is a communication from the browser to the Service Endpoint. It asks the RP to perform a user authentication service, such as "login", and it contains all the data needed for that purpose. It is a type of Service Request. It consists of a single JSON object. The JSON contains a JSON Web Signature. The RP will send a RML Response in response to the request.

In general, the RML Request is used to transmit a "proof of identity", along with any other necessary data, to the RP. Using a JWS allows all the data to be cryptographically signed, with multiple signatures. The signatures can be verified by the RP. The payload of the JWS is used to store all the custom information. The payload values include a requested user authentication command (i.e. the opIdReq). The opIdReq value should contain a opId that is one of the RML Operation List. (i.e. "login", "registerUserLogin", "setActiveCredentials" etc.)

The browser sends the RML Request to the "Service Endpoint" in whatever manner the RP designated in its RML FiskProfile. (i.e. In the a FiskDigest, such as in the FiskCatalog.) Usually the method is an HTTP POST. (As a special case, if the ServiceTrigger HTML Element that activated Capis is a form, and Capis is configured to send an HTTP POST, then the browser will send the service request as a special HTTP parameter *in addition to* any regular form data. The default name of the HTTP parameter is capis_request.)

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", "sub": "remere/setActiveCredentials", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "encReqValue": "xxxxxxxx", "rawForLogin": "qis:A:A:2:M4j-q:XSAErLq", // The QIF UserId "encForLogin": "q3jC9-h", "usk": "q3jC9-h" }, "user_auth_method": "stored_cred", "redirectUri": "/some/path/page.html", "useCount": 42,
/* The user_id_certificate_chain is absent when using the "stored_cred" uauth_method. // user_id_certificate_chain "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], */
// crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "aar": { // authenticatorAssertionResponse (optional) "adf": "WebAuthN 1.0", // authenticatorData_format "cda": "xxx", // clientDataJSON. Serialized JSON. "ada": "xxx", // authenticatorData. base64url encoded. "uha": "xxx" // userHandle. base64url encoded. }, "cpl": [ // credentialProofList { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, // "credRegSec" credentialRegSection. Required for opId="registerUserLogin" and "setActiveCredentials". "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" }, "aof": "WebAuthN 1.0", // attestation_object_format "aod": "xxx" // attestation_object. base64url encoded. }, // If the RP allows 2 public keys as Credential Verification Rule. { "cid_r": "cd11", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -257, "val_r": { "kty": "RSA", "kid": "cd11", // "kid" is REQUIRED. Same as the "cid_r". "alg222": "RS256", "n" : "4b9e34...", "e" : "93bc32..." }, "aof": "WebAuthN 1.0", // attestation_object_format "aod": "xxx" // attestation_object. base64url encoded. } ] } };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256", "sct":192} "header": { "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d" // "kid": "userIdPK" // "userIdPK" means the public key is in "uid". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" } ] } };

RML Request - List of Properties

Special JSON properties used in the "service request" JSON object

The following properties can be used in the "service request" JSON object. The following properties can be used in the "payload" JSON object.

RML Request - Property: SoclRequest.JWS (longName: "JWS")

Type Object; Required. (A value must be specified.)

Contains a JSON Web Signature value. It is used as a UserId Assertion.

The JWS property contains a "JSON Web Signature" type of request. This is only one of the possible types of requests. Other types can be created in the future. JWE, etc. This allows the spec to be extended in the future. (For extensibility.)

Example RML Request:
let RML Request = {
  "JWS": {
    "payload": BASE64URL(UTF8(payload_unencoded)),
    "signatures": [
      { "protected": "eyJhbGciOiJFUzI1NiJ9",
        // protected_header = {"alg":"ES256", "sct":192}
        "header": {
          "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d"
        // "kid": "userIdPK" // "userIdPK" means the public key is in "uid".
        },
        "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga"
      }
    ]
  }
}

RML Request - Property: SoclRequest.JWS.payload (longName: "payload")

Type Object; Required. (A value must be specified.)

Contains the data to be signed.

RML Request - Property: SoclRequest.JWS.signatureInfo (longName: "signatureInfo")

Type Object; Required. (A value must be specified.)

A single item in the signatures array. It represents a Credential Signature. Contains the signature. The signatures property contains an array of these items.

Example RML Request:
let RML Request = {
  "JWS": {
    "payload": BASE64URL(UTF8(payload_unencoded)),
    "signatures": [
      { "protected": "eyJhbGciOiJFUzI1NiJ9",
        // protected_header = {"alg":"ES256", "sct":192}
        "header": {
          "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d"
        // "kid": "userIdPK" // "userIdPK" means the public key is in "uid".
        },
        "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga"
      }
    ]
  }
}

RML Request - Property: signatureItem.header (longName: "Signature unprotected header")

Type Object; Required. (A value must be specified.)

Contains the "unprotected" signature header. This commonly includes the "key id" ("kid") against which the signature should be verified.

Contains the kid property.

RML Request - Property: signatureItem.protected (longName: "Signature protected header")

Type Object; Required. (A value must be specified.)

Contains the "protected" signature header.

RML Request - Property: signatureItem.protected.alg (longName: "Signature Public Key Algorithm")

Type string; Required. (A value must be specified.)

Contains the public key algorithm used to create the signature.

Suggested values: "RS256", "RS384", "RS512". See coseAlgId and RML FiskProfile.JWA_alg.

RML Request - Property: signatureItem.protected.sct (longName: "Signature Key Sign Count")

Type int. May be required?

Contains the sign count. (Anti-forgery protection. Detect a cloned User Authenticator.)

RML Request - Property: signatureItem.header.kid (longName: "Signature Key ID")

Type string; Required when ?.

Contains the Key ID. Uniquely identifies the key for the User Login.

Key Id value: userIdPK[:n]

The string "userIdPK[:n]" can be used as the "kid" value in signature headers in the RML Request. (Where 'n' is an integer from 1 to 100.) If it is used as the "kid" value, it notifies the RP that the signature was not created by a regular credential. Instead, the signature must be verified using the sig verification key of the public key encoded inside the "UserId". (i.e. In the "payload"."uid" value.)

Key Id value: certPK:n

The string "certPK:n" can be used as the "kid" value in signature headers in the RML Request. (Where 'n' is an integer from 1 to 100.) If it is used as the "kid" value, it notifies the RP that the signature was not created by a regular credential. Instead, the signature must be verified using the sig verification key of the public key with the specified number from the UserId certificate.

RML Request - Property: signatureItem.header.vkm (longName: "sigInfo.header.sigVKMethod")

Type string; Required if the credential sig verification key is in the UserId Certificate. Default value: "site_stored_vks"

Used for Sig VK Method. See Set User Credential Verification Rules.

Values are site_stored_vks, userIdF and certificateF. Located in the unprotected signature header. (NOT THE SAME AS vkmc_value_list)

Ver Key Method value: site_stored_vks

The string "site_stored_vks" can be used

Ver Key Method value: userIdF

The string "userIdF" can be used as the "vkm" value in signature headers in the RML Request. If it is used as the "vkm" value, it notifies the RP that the signature was not created by a regular credential. Instead, the signature must be verified using the sig verification key of the public key encoded inside the "UserId". (i.e. In the "payload"."uid" value.)

Ver Key Method value: certificateF

The string "certificateF" can be used as the "vkm" value in signature headers in the RML Request. If it is used as the "vkm" value, it notifies the RP that the signature was not created by a regular credential. Instead, the signature must be verified using the sig verification key of the public key with the specified number from the UserId certificate.

RML Request - Property: signatureItem.header.csvm (longName: "sigInfo.header.certificateSigVKMethod")

Type string; Required if the JWT is a UserId Certificate.

Used for Certificate Signature Verification Protocol. See Set User Credential Verification Rules.

Located in the unprotected signature header.

RML Request - Property: signatureItem.signature (longName: "Signature")

Type string; Required. (A value must be specified.)

Contains the actual signature value.

RML Request - Property: SoclRequest.signatures (longName: "signatures[]")

Type Array(); Required. (A value must be specified.)

An array of signatureInfo objects. An array of signatures inside a JWS (JSON Web Signature). These signatures are created by signing the payload with a set of private keys. The signatures are given to the RP in order to authenticate the JWS to the RP. When a RML Request is composed of a JWS, the signatures will thereby authenticate the service request. A JWS supports multiple signatures. Therefore, a JWS service request is capable of Multi-Credential Authentication and Multi-Factor Authentication. (Each signature is proof of a different Credential Verification Rule. Each CVR may be a different factor.)

The Capis specification requires that each signature, except for one (for the "UserId"), MUST match a Credential Verification Rule that the RP stores for the UserId. Each signature MUST have a "kid" value (in the header section) that is the "Credential Verification Rule id" stored by the RP. (There may be less signatures than the number of CVR at the RP. The RP may have some CVR be optional, etc.)

There can be only one signature that does not directly represent a CVR stored at the RP. This is the signature from the UserId private key. The UserId private key signature will be always marked with a special "kid" value. ("kid": "userIdPK") The UserId private key signature is optional. (The RP may or may not require that user authentication requests be signed by it.) (This is for the case when the UserId contains or represents a public key. The UserId private key is the private key corresponding to the public one inside, or represented by, the UserId.)

Example RML Request:
let RML Request = {
  "payload":
   "eyJpc3MiOiJq2bUiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF
    tcGxlLmNvbS9pc19yj290Ijp0cnVlfQ",
  "signatures": [
    { "protected": "eyJhbGciOiJSUzI1NiJ9",
        // protected_header = {"alg":"RS256", "sct":192}
      "header": {"kid":"cd1"},
      "signature":
       "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ
      mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb
      KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl
      t1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES
      c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX
      LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"
    },
    { "protected": "eyJhbGciOiJFUzI1NiJ9",
        // protected_header = {"alg":"ES256", "sct":13}
      "header": {"kid":"cd2", "kid2":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
      "signature":
       "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS
      lSApmWQxfKTUJqPP3-Kg6NU1Q"
    }
  ]
}

RML Request - Property: payloadData.requestFormat (longName: "requestFormat")

Type string; Default value: "remereRequestFormat 0.1"

The specification name and version that the RML Request is written in. (That the browser chose to use.)

The value should be one of those listed in the FiskProfile Detail.requestFormatList. If the request does not specify a requestFormat, then the RP should use the default requestFormat for the protocolUsed.

RML Request - Property: payloadData.protocolId (longName: "protocolId")

Type string; Required. (A value must be specified.)

The protocol of the operation to be performed by the RP. It is usually copied from the FiskTrigger.protocolId property. (In the "servicetriggerdata" attribute.)

RML Request - Property: payloadData.opIdReq (longName: "opIdReq")

Type string; Required. (A value must be specified.)

The requested operation to be performed by the RP. It is usually copied from the FiskTrigger.protocolOp property. (In the "servicetriggerdata" attribute.) It should contain a opId that is one of the RML Operation List. (i.e. One of "login", "logout", "proveUserPresence", "registerUserLogin", "grantAccess", etc.)

RML Request - Property: payloadData.authAppIdUsed (longName: "authAppIdUsed")

Type string; Required. (A value must be specified.)

Specifies the Auth Application Id (authAppId) that the selected User Login is registered to. This is usually what website the User Login was registered to.

The authAppIdUsed is always always equal to the FiskProfile.appIdSpec. The exception is when the user selects a foreignRapId User Login to use (i.e. to login with).

RML Request - Property: payloadData.uam (longName: "user_auth_method")

Type string; Default value: "stored_cred"

Specifies how the request wants the RP to authenticate the user. (If it should require that "ownership" of the MSUID be verified. i.e. With a UserId certificate.)

See Login Method List for the list of valid values. Example: "stored_cred".

Used by the "supported.userAuthScheme" service property.)

RML Request - Property: payloadData.iat (longName: "issuedAt")

Type string; Required. (A value must be specified.)

Indicates the timestamp when the request was generated.

RML Request - Property: payloadData.exp (longName: "expiresAt")

Type string; Required. (A value must be specified.)

Indicates the timestamp when the request expires.

RML Request - Property: payloadData.sub (longName: "subject")

Type string; Required. (A value must be specified.)

Indicates the subject of the request.

RML Request - Property: payloadData.aud (longName: "audience")

Type string; Required if the JWT is not a UserId certificate.

Indicates the intended audience of the request.

RML Request - Property: payloadData.challengeKey (longName: "challengeKey")

Type string; Required. (A value must be specified.)

A "cryptographic nonce" value. base64url encoded. It is a value generated by, and passed back to, the RP. In order to prevent cross site scripting attacks. (CSS attacks) Copied from the config "challengeKey" option.

RML Request - Property: payloadData.cht (longName: "challengeTimestamp")

Type string; Required. (A value must be specified.)

The timestamp when the challengeKey was issued. It is a value generated by, and passed back to, the RP. In order to prevent cross site scripting attacks. (CSS attacks)

RML Request - Property: payloadData.redirectUri (longName: "redirectUri")

Type string;

The URI the caller wants to be redirected to.

RML Request - Property: payloadData.uid (longName: "userId.mmm")

Type Object; Required. (A value must be specified.)

The claimed UserId.

This is not the actual UserId, but a temporary UserId, used only in the current session. The actual UserId is kept secret from the browser. It is encrypted and sent to the RP in an encrypted form in the "login" request. (etc.)

When the opIdReq is "registerUserLogin", the browser should set the uid.timestamp_uid property. The RP will create a new assignedUserId and send it to the browser in an encrypted form. The browser should send it to the Login Manager, using LoginManager.updateUserLoginWithServiceResult() in svUlUpdate.updatedUserId. The Login Manager will decrypt the value and store it, associated with the login.

RML Request - Property: payloadData.uid.rawForLogin (longName: "userId.rawForLogin")

Type string; Required. (A value must be specified.)

The raw UserId.

RML Request - Property: payloadData.uid.encForLogin (longName: "userId.encryption_value_1")

Type string; Required when opId="registerUserLogin".

The encrypted UserId. Using encryption method 1.

The encrypted UserId and the sessionUserId are extra features to protect User Privacy. They both hide the real UserId from the browser. So that the user can use any browser and the UserId for all RP websites will remain secret from the browser.

The encrypted UserId is used when the browser is not logged into the RP website. In this case, the browser must send to the RP a secret, encrypted UserId. The UserId can be encrypted using the HTTP session encryption key or with the RP website HTTPS key.

If the browser is already logged into the RP website, then the browser does NOT send the encrypted UserId to the RP. In this case, it must send the sessionUserId instead. The sessionUserId is a temporary value the RP creates to "stand-in" for the real UserId.

RML Request - Property: payloadData.uid.usk (longName: "userId.sessionKey")

Type string; Required except when opId="registerUserLogin".

The UserId session key. The value from sessionUserId.

The encrypted UserId and the "UserId session key" are extra features to protect User Privacy. They both hide the real UserId from the browser. So that the user can use any browser and the UserId for all RP websites will remain secret from the browser.

The encrypted UserId is used when the browser is not logged into the RP website. In this case, the browser must send to the RP a secret, encrypted UserId. The UserId can be encrypted using the HTTP session encryption key or with the RP website HTTPS key.

If the browser is already logged into the RP website, then the browser does NOT send the encrypted UserId to the RP. In this case, it must send the sessionUserId instead. The sessionUserId is a temporary value the RP creates to "stand-in" for the real UserId.

RML Request - Property: payloadData.uid.encReqValue (longName: "userId.encReqValue")

Type string; Required. (A value must be specified.)

The new UserId value for the RP to set. Encoded.

RML Request - Property: payloadData.uid.timestamp_uid (longName: "userId.timestamp_uid")

Type string; Required when opId="registerUserLogin".

A raw UserId made from a timestamp. It is used with opId="registerUserLogin" as a "service request id". The RP copies it into the response so that the browser will know what request is being responded to.

RML Request - Property: payloadData.iss (longName: "issuer")

Type string; Required if the JWT is a UserId certificate.

Indicates the Backer of the UserId.

RML Request - Property: payloadData.pr.uid (longName: "pr.userId.mmm")

Type Object; Required. (A value must be specified.)

The claimed UserId.

RML Request - Property: payloadData.pr.uid.raw2 (longName: "pr.userId.raw2")

Type string; Required. (A value must be specified.)

The claimed UserId.

RML Request - Property: payloadData.uid.uit (longName: "userId.type")

Type string;

The type of the claimed UserId. Only necessary if the UserId is not written using "QIF" format.

Valid values are a QIF type specifier. (i.e. the text "qis:", namespace_id, idClass and subClass.) Examples: "qis:A:H:9", "qis:A:A:2".

(Used by the "RmlFiskProfile.userLoginCreationRules.userIdTypes" service property.)

RML Request - Property: payloadData.uid.clientReqUserId (longName: "uid.clientReqUserId")

Type string;

The requested username to use when creating an account.

This property is only used with one of the "Negotiated UserId" User Login Creation Method. Such as "requested2_ulc".

This property name stands for "requested username". This property value is set from the RML FiskTrigger.ulCreationCfg property. This property is what the browser (or user) asks for. This is different from the RML FiskProfile.userLoginCreationRules, which contains the rules, and the default userId value that the RP provides.

See RmlFiskProfile.userLoginCreationRules.

RML Request - Property: payloadData.uid_cert_chain ("icc") (longName: "UserId Certificate Chain")

Type Object; Required if the browser tries to authenticate with a Locked Id that uses a UserId certificate.

(If "user_auth_method" is "only_MSUID", "combo_MSUID", or "certificate_2").

A JSON object containing an array of certificates. Each certificate is a JSON object.

(Note: If the UserId certificate is an X.509 certificate chain, then it can be stored in the UserId Assertion JWT header (in the RML Request) as the "x5c" value. There does not have to be a separate "uid_cert_chain" property in this case.)

The UserId proof includes the "uic.ucv", "uic.format", and "uic.csvm" fields. These are part of the user_id_certificate_chain.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId": "remere", "opIdReq": "login", "sub": "remere/login", ... // user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], ... };

RML Request - Property: payloadData.uic.ucv (longName: "uid_cert.value -- UserId Certificate")

Type Object; Required. (A value must be specified.)

Contains an "UserId certificate". (Can be a JWT.) It is required, unless the "UserId certificate" is included in the UserId Assertion. For example, it could be an X.509 certificate and can be included in the UserId Assertion JWT as the "x5c" header property.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId": "remere", "opIdReq": "login", "sub": "remere/login", ... // user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], ... };

RML Request - Property: payloadData.uic.format (longName: "uid_cert.format")

Type string; Default value: "JWS"

Specifies the format of "UserId certificate" in the "uic.ucv" property.

RPs must support at least the format "JWS" in the "supported.uic.format" service property.

Values include "JWS-CS" ("JWS-Compact Serialization"), "JWE-CS" (JWE-Compact Serialization), "JWS" (Long form JWS-JSON), "chain_42".

If "chain_42" is used, then the "uic.ucv" is not used, and the "user_id_certificate_chain" is used instead.

RML Request - Property: payloadData.uic.csvm (longName: "uid_cert.sig_ver_method")

Type string; Default value: "OIDCD"

Contains the "UserId certificate signature verification method". This value gives the RP information about how to verify the certificate. It is usually used to tell the RP how to get the sig verification key for the certificate. (i.e. it is usually used as input to Certificate Signature Verification Protocol.) However, the RP must first know the type of the certificate, and have the ability to read it.

(Used by the "supported.uic.csvm" service property.)

Valid values are:

The browser should choose to use an IDP and an "UserId certificate" that the RP can understand.

RML Request - Property: payloadData.useCount (longName: "useCount")

Type int. Required. (A value must be specified.)

Holds the use count. Holds the number of times the User Login has been used. Should always increase. (Anti-forgery protection. Detect a cloned User Authenticator.)

The User Login use counter. Holds the number of times the User Login has been used. Should always increase. Every use of the User Login to login should increment the counter. For extra security, the RP can record the count. If any subsequent login by the User Login uses an equal or lower value, then the User Login was compromised. (Attempts to detect if the User Authenticator is duplicated. Or the security device was copied, etc.)

RML Request - Property: payloadData.gua (longName: "grant_user_access")

Type Object; Required if opId="grantAccess".

The collection of information required in order to add, remove and modify a user's access to the account. Used by the Grant Access Service Action.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "grantAccess", "sub": "remere/grantAccess", ... // The grant_user_access section. "gua": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty":"ES", "kid":"cd10", ... } } ] } };

RML Request - Property: payloadData.prs (longName: "credentialProofSection")

The details of the user identity for an RP. Includes "id", credentialProofList ("cpl").

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "login", // or "proveUserPresence" "sub": "remere/login", ... "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "encReqValue": "xxxxxxxx", "rawForLogin": "qis:A:A:2:M4j-q:XSAErLq", // The QIF UserId "encForLogin": "q3jC9-h", "usk": "q3jC9-h" }, "user_auth_method": "stored_cred", "redirectUri": "/some/path/page.html", "useCount": 42, ... // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "aar": { // authenticatorAssertionResponse (optional) "adf": "WebAuthN 1.0", // authenticatorData_format "cda": "xxx", // clientDataJSON. Serialized JSON. "ada": "xxx", // authenticatorData. base64url encoded. "uha": "xxx" // userHandle. base64url encoded. }, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, ... };

RML Request - Property: payloadData.prs.uapCounter (longName: "uapCounter")

Type int.

The number of times the LoginManager has been used to create a user authentication proof. (i.e. a proof of identity)

RML Request - Property: payloadData.prs.cpl (longName: "credentialProofList")

Type Array();

An array of credentialProof objects. This normally contains only proofs for credentials that are not public keys. The signatures property holds the proof for those credentials that are public keys. (If the RML Request is not represented by a JWS, then the "credentialProofList" may hold public key credentials as well.) Each "credential proof" object can be of a different type.

The credential proofs are given to the RP in order to authenticate the user. Having the RML Request contain multiple credential proof objects allows the RP to use Multi-Credential Authentication and Multi-factor authentication.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "login", // or "proveUserPresence" "sub": "remere/login", ... "useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "aar": { // authenticatorAssertionResponse (optional) "adf": "WebAuthN 1.0", // authenticatorData_format "cda": "xxx", // clientDataJSON. Serialized JSON. "ada": "xxx", // authenticatorData. base64url encoded. "uha": "xxx" // userHandle. base64url encoded. }, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, ... };

RML Request - Property: payloadData.prs.credentialProof (longName: "credentialProof")

Type Object;

A single item in the credentialProofList array. Represents a Credential Proof for a credential that is not a publicKey.

Each credential proof object has an "id", a "type", a "format", and a "value". The names of these properties are "cid_p", "typ_p", "fmt_p" and "val_p". The value is a string or a JSON object of a type specified by the format value. Each credential MUST normally have a "cid_p" value, and the value MUST MATCH one of the "Credential Verification Rule id" values stored by the RP for the UserId. (There is only one exception. The "UserId" public key.)

There is one special case. One credential proof object is allowed to NOT refer to a CVR stored by the RP. In this case, the "cid_p" must be set to the special value "userIdPK". ("cid_p": "userIdPK") This marks the credential proof object as being the signature of the UserId private key. (This requires that the UserId be an encoded public key. See UserId.) Note that the UserId credential proof object is optional. An RP may or may not require it.

The RP stores "Credential Verification Rule" values for user authentication. The CVR values are given to the RP during (1) "registerUserLogin" and (2) "Set Active Credentials" operations. The RP may not require all CVR. Some stored CVR may be optional, or the RP may require only a subset. The exact configuration is up to the RP. Because some CVR may be optional, the RML Request may contain less credential proofs than the number of CVR at the RP. (The total number of credential proofs, in the cpl_list and the "signatures" array, may be less than the number of CVR stored by the RP.)

The credentialProof object also allows experiences that combine multiple authentication mechanisms such as fingerprint + PIN.

RML Request - Property: payloadData.prs.credentialProof.cid_p (longName: "credentialProof.id")

Type string; Required. (A value must be specified.)

Specifies the id of a credentialProof in the "credentialProofList". (Used with rml.request.credRegSec.crList request property.)

The credentialProof."cid_p" value MUST MATCH the id (the credentialReg."cid_r") of one of the "Credential Verification Rule id" values stored by the RP for the UserId. (The "Credential Verification Rule" values were given to the RP during a previous "Set Active Credentials" operation.) (i.e. if opId="registerUserLogin" or "setActiveCredentials".) The RP stores the "cid_r", along with the CVR values. (The password or public key, etc.)

RML Request - Property: payloadData.prs.credentialProof.typ_p (longName: "credentialProof.type")

Type string; May be Required?.

Specifies the type of a credentialProof in the "credentialProofList". (Used with credentialReg.ruleTypes service property.)

Valid values are "password", "pubkey", "federated".

RML Request - Property: payloadData.prs.credentialProof.val_p (longName: "credentialProof.value")

Type string; Contains base64url encoded binary data. Required. (A value must be specified.)

Specifies the value of a credentialProof in the credentialProofList array. Usually this is a JWT value. The format of the item is specified by the neighboring credentialProof.format parameter. The default credentialProof.format is "TBD-CPF".

If the CVR is a public key, then the credentialProof.value is usually a JWT, with a signature created by the private key.

RML Request - Property: payloadData.prs.credentialProof.fmt_p (longName: "credentialProof.format")

Type string; Default value: "TBD-CPF"

Specifies the format of a credentialProof.value in the credentialProofList array.

Values include "JWS-CS", "U2F", "sig". ("JWS-Compact Serialization"). (U2F is the standard of Universal 2nd Factor.)

If the value is "TBD-CPF", then the credentialProof."value" is a "TBD-CPF". If the value is "fmt_cert_42", then the credentialProof."value" is not used, and the "user_id_certificate_chain" is used instead. (That includes the "uic.ucv", "uic.format", and "uic.csvm" fields.)

The value the browser uses for credentialProof."format." should be one of those supported by the RP. (One of those in the "supported.credentialProof.formats" array.)

RML Request - Property: payloadData.req_factor2 (longName: "req_factor2")

Type string;

Holds the 2nd factor authentication. (During User Login creation, the list of public keys. During login, the 2nd JWT.) When creating an account, the RP might allow additional public keys to be set. (2nd factor authentication) The public keys can be sent to the RP in an additional HTTP parameter. The keys can be in JWK format. When logging into an RP, the second signature can be sent to the RP in this second parameter. (As a JWT.)

RML Request - Property: payloadData.cert.pubKeyList (longName: "cert.pubKeyList")

Type Array(); Required if JWT is a UserId certificate.

Specifies the array of public keys in the UserId certificate.

RML Request - Property: payloadData.cert.pubKey.pkf (longName: "cert.pubKey.format")

Type string; Required if JWT is a UserId certificate.

Specifies the format of a public key in the UserId certificate.

RML Request - Property: payloadData.prs.aar (longName: "authenticatorAssertionResponse")

A JSON object that specifies the exact bytes that the User Authenticator returned.

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", // or any opId "sub": "remere/setActiveCredentials", ... "crProofSection": { ... "uapCounter": 42, "aar": { // authenticatorAssertionResponse (optional) "adf": "WebAuthN 1.0", // authenticatorData_format "cda": "xxx", // clientDataJSON. Serialized JSON. "ada": "xxx", // authenticatorData. base64url encoded. "uha": "xxx" // userHandle. base64url encoded. }, "cpl": [ // credentialProofList { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } // End of credential entry. ] } // End of crProofSection. };

RML Request - Property: payloadData.prs.adf (longName: "authenticatorData_format")

Type string; Default value: "WebAuthN 1.0"

A string that specifies how the "authenticatorData" is encoded.

RML Request - Property: payloadData.prs.cda (longName: "clientDataJSON")

Type string; Contains base64url encoded binary data. Requirement unknown.

Holds the result of JSON stringifying and UTF-8 encoding to bytes a CollectedClientData dictionary.

Example JSON:
{"type":"webauthn.create", "challenge":"MTIzNDU2Nzg", "origin":"RP_example.com", "tokenBinding":{"id":"m38hcBq"}}
Don't have any real idea what this should be. Look at webAuthn for ideas.
dictionary CollectedClientData {
    required DOMString           type;
    required DOMString           challenge;
    required DOMString           origin;
    TokenBinding                 tokenBinding;
};

RML Request - Property: payloadData.prs.ada (longName: "authenticatorData")

Type string; Contains base64url encoded binary data. Required if opId="registerUserLogin" or "setActiveCredentials".

Contains the data from the User Authenticator. Holds base64url encoded binary data.

Don't have any real idea what this should be. Look at webAuthn for ideas.
Example JSON:
dictionary ParsedAuthenticatorData {
    ArrayBuffer (32 bytes) rpIdHash; // A hash of the capId string.
    ArrayBuffer (1 bytes) flags;
    // MOVED signCount; (Number) Moved to useCount.
    // MOVED attestedCredentialData; (Object. optional) Moved to attestation_object.
    extensions; (Object. optional)
};

RML Request - Property: payloadData.prs.uha (longName: "loginItem_userHandle")

Type string; Contains base64url encoded binary data. Requirement unknown.

Contains an opaque user handle. Holds base64url encoded binary data.

RML Request - Property: payloadData.credRegSec (longName: "credRegSec")

The Credential Registration Section. The collection of User Authentication Credentials to be stored by the RP. Used when the opId is either "registerUserLogin" or "setActiveCredentials".

It contains the list of Credential Verification Rule to be stored by the RP. (i.e. the "crList") (Each entry in the "removeList" contains two items, "cid_r" and "typ_r".) Each entry in the "crList" contains at least 4 items, "cid_r", "typ_r", "fmt_r" and "val_r". The data may be passwords or public keys, etc.

Valid properties are "cid_r" = "credential id", "typ_r" = "type", "fmt_r" = "format", "val_r" = "value"

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", // or "registerUserLogin" "sub": "remere/setActiveCredentials", ... // The "credRegSec" credentialRegSection "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty":"ES", "kid":"cd10", ... }, "aof": "WebAuthN 1.0", // attestation_object_format "aod": "xxx" // attestation_object. base64url encoded. } ] } };

RML Request - Property: payloadData.user_login_creation_method (longName: "user_login_creation_method")

Type Array(); Required if opId="registerUserLogin". Default value: "assigned_ulc"

It is a member of the credRegSec section.

The value is one of those from UserLogin Creation Methods. Such as claim_ms_ulc, assigned_ulc. The value has to be one of those supported by the RP. See RmlFiskProfile.userLoginCreationRules.createMethods.

RML Request - Property: payloadData.credRegSec.bkcr (longName: "backup_credentials")

It is a member of the credRegSec section.

Provides a backup way to verify the user's identity. It exists in case the primary public key (in the UserId) is compromised. The public key is also used when verifying an "identity change request". When the user tries to change their identity. (The backup is also verified in case the user's primary public key has been compromised.) This backup key is given to the RP in the RML Request (that uses the opId="registerUserLogin") when creating an account at the RP. The RP should permanently keep the key. It provides an additional way to verify the user. It is only used to verify the user for a few extreme cases.

When the user wants to set the UserId, he must provide an "identity change request" key. The RP must ensure that the change request is performed by the user. The request must be signed with a signature from the private key linked to the "backup_credentials".

RML Request - Property: payloadData.credRegSec.crList (longName: "credRegSec.credentialRegList")

Type Array(); Required. (A value must be specified.)

An array of credentialRegistration objects. Each item represents a Credential Verification Rule to be stored by the RP. The list is expandable, to hold any number of credentials. See RML Request: Set Credential Verification Rules Detail.

It is a member of the credRegSec section.

RML Request - Property: payloadData.credRegSec.credReg (longName: "credentialRegistration")

Type Object;

A single item in the crList array. (In the "credRegSec" section.) It represents a Credential Verification Rule export. (It contains the exported CVR.)

Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "setActiveCredentials", // or "registerUserLogin" "sub": "remere/setActiveCredentials", ... // The "credRegSec" credentialRegSection "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty":"ES", "kid":"cd10", ... }, "aof": "WebAuthN 1.0", // attestation_object_format "aod": "xxx" // attestation_object. base64url encoded. } ] } };

RML Request - Property: payloadData.credRegSec.credReg.cid_r (longName: "credentialRegistration.id")

Type string; Required. (A value must be specified.)

Specifies the id of a credentialReg in a crList. The identifier of a Credential Verification Rule being sent to the RP in the "credRegSec" section.

During login, each of the credentialProof."cid_p" values sent to the RP (by the user's Login Manager) MUST MATCH the id of one of the "Credential Verification Rules" stored by the RP for the user. (The credentialProof."cid_p" and signature."kid" must match a credentialReg."cid_r".)

During one of the "Set Active Credentials" commands, the user's "Login Manager" sends a list of credentialReg items (i.e. the "crList") to the RP. It can set the credentialReg."cid_r" values to whatever it likes, with the following restrictions. Each value must be unique within the scope of the crList. Limited to alpha-numeric, dash and underscore characters. (allows base64url encoding). Maximum of 16 characters. The first character must be "c".

This value is NOT supplied by the RP. It is supplied by the browser. It is used during one of the "Set Active Credentials" commands. (i.e. if opId="registerUserLogin" or "setActiveCredentials".) The RP stores the "cid_r" value, along with the actual CVR value. (The password or public key, etc.)

RML Request - Property: payloadData.credRegSec.credReg.typ_r (longName: "credentialRegistration.type")

Type string; May be Required?.

The type of a Credential Verification Rule being sent to the RP in the "credRegSec" section. (Used with credentialReg.ruleTypes service property.)

Valid values are "password", "pubkey", "federated".

(Note: User Identifier Types define types of identifiers. Like a Credential Verification type is a type of CVR.)

RML Request - Property: payloadData.credRegSec.credReg.fmt_r (longName: "credentialRegistration.format")

Type string; Default value: "JWK"

The format of a Credential Verification Rule being sent to the RP in the "credRegSec" section. (Used with credentialReg.ruleTypes service property.)

"pubkey" values are usually of format "JWK". "password" values are usually a sting. "federated" values are ??

RML Request - Property: payloadData.credRegSec.credReg.coseAlgId (longName: "credentialRegistration.coseAlgId")

Type int. Default value: -7 (i.e. JWA "ES256".)

The JWK algorithm of the public key Credential Verification Rule being sent to the RP in the CVR section.

Valid "JWA_alg" values are "ES256", "RS256", "RS384", "RS512". See RML Request.JWS.sig_alg and RML FiskProfile.JWA_alg.

RML Request - Property: payloadData.credRegSec.credReg.val_r (longName: "credentialRegistration.value")

Type Object; May contain base64url encoded binary data. Required. (A value must be specified.)

The value of a Credential Verification Rule being sent to the RP in the "crList" section of the "credRegSec". (Used with credentialReg.ruleTypes service property.)

"pubkey" values are usually of format "JWK". "password" values are usually a sting. "federated" values are ??

RML Request - Property: payloadData.credRegSec.credReg.kid (longName: "credentialRegistration.keyId")

Type string; Required. (A value must be specified.)

The id of the public-key Credential Verification Rule. // The Key ID.

Must be the same as the credentialRegistration.id ("cid_r").

RML Request - Property: payloadData.credRegSec.credReg.aod (longName: "credentialRegistration.attestation_object")

Type string; Contains base64url encoded binary data. Required if opId="registerUserLogin" or "setActiveCredentials".

Contains the attestation object from the LoginManager. Holds base64url encoded binary data.

RML Request - Property: payloadData.credRegSec.credReg.aof (longName: "credentialRegistration.attestation_object_format")

Type string; Default value: "WebAuthN 1.0"

Holds the attestation object format from the LoginManager.

RML Response Detail

The Remere Login Service Response (RML Response) is a JSON object created by the RP, and sent to the browser, in response to a RML Request that was sent to the Service Endpoint. It consists of a single JSON object. All RML Response objects are received and processed by the browser RML implementation first, before any scripts on the HTML page. This is so that the browser will always know the current Remere Login Status at the RP.

The response JSON object can contain a success code or error, etc. If the opId sent to the RP was "registerUserLogin" then the response from the RP will contain the new assignedUserId. (Or an error.)

The "error" property contains the error code, message, etc. A json response must only have EITHER a "result" OR an "error" property. A single response cannot have both.

Example RML Response JSON object:
{
  "protocolId": "remereResponse 1.0",
  "id": 1234567890,
  "request": {
    "opIdReq": "login",
    "profileId": "remere_1"
  },
  "result": {
    "assignedUserId": "qis:A:A:1:Gib9_cYs",
    "sessionUserId": "q3jC9-h",
    "alt_user_id": "qis:A:A:1:Gib9_cYs"
  },
  "comment_22": "The 'result' and 'error' properties cannot both exist in a single response.",
  "error": {
    "code": 1492,
    "message": "Parse error",
    "message_2": "Method not found",
    "message_3": "Parse error",
    "message_4": "Invalid request",
    "data": { "some property":"some data" }
  }
}

RML Response - List of Properties

Special JSON properties used in the "service response" JSON object

The following properties can be used in the "service response" JSON object.

RML Response - Property: SocResponse.id (longName: "id")

Type int. Required. (A value must be specified.)

The id number of the request. The response must have the same id number as the corresponding request. The sender may use these numbers to identify the request and response.

RML Response - Property: SocResponse.protocolId (longName: "protocolId")

Type string. Required. (A value must be specified.)

The protocol that the response is written in. The response must have a supported protocol.

RML Response - Property: SocResponse.result (longName: "result")

Type Object; Required if an error did not occur.

Contains the result value from the RML Request. A response may only have ONE of "result" or "error". It may not have both.

Example RML Response JSON object:
{
  "protocolId": "remereResponse 1.0",
  "id": 1234567890,
  "request": {
    "opIdReq": "login",
    "profileId": "remere_1"
  },
  "result": {
    /* "assignedUserId": "qis:A:A:1:Gib9_cYs", Not used with "login" */
    "sessionUserId": "q3jC9-h",
    "alt_user_id": "qis:A:A:1:Gib9_cYs"
  }
}

RML Response - Property: SocResponse.error (longName: "error")

Type Object; Required if an error occurred.

Contains the error object resulting from the RML Request. A response may only have ONE of "result" or "error". It may not have both.

RML Response - Property: SocResponse.request.opIdReq (longName: "requestOpId")

Type string; Required. (A value must be specified.)

Contains the opId value from the RML Request.

Example RML Response JSON object:
{
  "protocolId": "remereResponse 1.0",
  "id": 1234567890,
  "request": {
    "opIdReq": "registerUserLogin"
  },
  "result": {
    "assignedUserId": "qis:A:A:1:Gib9_cYs",
    "sessionUserId": "q3jC9-h",
    "alt_user_id": "qis:A:A:1:Gib9_cYs"
  }
}

RML Response - Property: SocResponse.result.assignedUserId (longName: "assignedUserId")

Type string; Required if opId="registerUserLogin", and not using a multi-site UserId.

Sent in response to the opId="registerUserLogin" and "setUserId".

When the opId is "registerUserLogin", the browser should set the uid.timestamp_uid property. The RP will create a new assignedUserId and send it to the browser in an encrypted form. The browser should send it to the Login Manager, using LoginManager.updateUserLoginWithServiceResult() in svUlUpdate.updatedUserId. The Login Manager will decrypt the value and store it, associated with the login.

Example RML Response JSON object:
{
  "protocolId": "remereResponse 1.0",
  "id": 1234567890,
  "request": {
    "opIdReq": "registerUserLogin",
    "profileId": "remere_1"
  },
  "result": {
    "assignedUserId": "qis:A:A:1:Gib9_cYs",
    "sessionUserId": "q3jC9-h",
    "alt_user_id": "qis:A:A:1:Gib9_cYs"
  }
}

RML Response - Property: SocResponse.result.sessionUserId (longName: "sessionUserId")

Type string; Required. (A value must be specified.)

Sent by the RP in response to a "login" action. (i.e. The "login" and opId="registerUserLogin")

The encrypted UserId and the UserId session key are extra features to protect User Privacy. They both hide the real UserId from the browser. So that the user can use any browser and the UserId for all RP websites will remain secret from the browser.

The sessionUserId is a temporary value the RP creates to "stand-in" for the real UserId.

The browser must remember the sessionUserId. It must send the sessionUserId as the UserId session key value when performing most actions. Example: opId="logout", "proveUserPresence", etc. If the browser is not logged into the RP website, then the browser must send to the RP an "encrypted UserId". Example: UserId using encryption method 1

RP User Authentication Process

The RP user authentication process (login process) has the following flow:

If the RP verification page receives an HTTP POST request, and the request does not contain the special HTTP parameter (Default name is "capis_request"), then the user's browser does not support the Capis specification. The browser simply followed the "href" attribute of the HTML anchor, like normal, and did not add any "_auth_" parameters. In this case, the RP should redirect the user to some other login method. It can also use the default fallback identity login path. See Appendix D - Browser does not support Capis HTML Attributes

Alternately, it may be easier for the RP verification page to detect a GET request, and redirect on that. But this can only be done if the RP is certain that all GET requests are errors (from older browsers) and all POST requests are correct. Check that the RP does not allow GET requests (in the FiskCatalog), and check that the RP does not send form submissions to the same endpoint.

During the identity proof verification, the RP must check to see if it has a record for the given UserId. If it does have a record for the UserId, then the RP must retrieve all the Credential Verification Rule for the UserId, and the "Credential Verification Methods" for each CVR. Then use the methods to verify each Credential Verification Rule in the identity to the identity proof. If verification fails, then the RP generates and returns a verification error message. (Ex: "The provided identity proof is not valid. Please provide different proof in order to login.")

If RP does not recognize a UserId, then one of two (or more) error messages must be returned. In other words, if an account DOES NOT EXIST for the specified user id. To determine which error message to return, the token must still be validated. Only if the token is valid (and any UserId certificate is also valid), then the user has successfully unlocked the Locked Id. Then the user is who he claims to be, and has proven he owns the UserId. (The user trying to log in is therefore authorized to use the user id he claimed.) If the user is verified, then the return message will be the "unknown user" error message. (This should be "unregistered user". No account exists for this user.) The user should then be prompted to create an account, with the proven user id. (Ex: An account does not exist for that user. Click here to create an account.)

If the token is NOT valid, then the RP should send a generic authentication error message. This prevents unverified users being able to determine if an account exists for any un-owned UserId. The situation where the existence of an account connected to any user id can be queried by anyone, even if they do not own the user id.

Note that an RP SHOULD NOT automatically create an account for identities in response to a login RML Request. (When a user tries to login with an identity, the identity is VERIFIED, but the RP does not have an account for that identity.) Instead, confirmation from the user is required before User Login creation. (To prevent User Login creation due to user error. If the user accidentally selected the wrong identity to log in with, etc.) See User Login creation and rml.opId="registerUserLogin".

If the signature is not valid, then an error message is displayed to the user. Note that the login process does not necessarily require the server to communicate with any other website. (This is unlike Mozilla Persona, Open ID, Facebook Connect, etc.) Only if the "authentication method" is for a third party server (an identity provider, etc.) then communication must be done with a third party. Note that the login process does not send the server (the RP) a password. The server does not store or validate passwords. It stores a public key and validates a digital signature.

========

Capis Javascript implementation (and Remere Login)

The goal is that browsers will implement Remere Login natively. However, until that time, there is a JavaScript implementation.

  1. RP web pages must have the RemereLogin API library. (The code to be executed.)
  2. RP web pages that use rel="servicetrigger" HTML elements should include a script in the head. <script src="capis_library.js"></script>
  3. It hooks up all the ServiceTrigger HTML Elements to use the invokeServiceFromTriggerHtmlElement behavior. (It calls CapisControl.invokeServiceFromTriggerHtmlElement.)
    The Anchor HTML Element and button HTML Elements call the invokeServiceFromTriggerHtmlElement when they are clicked. The Form HTML Elements call invokeServiceFromTriggerHtmlElement when they are submitted.
  4. There must be a way for the library to access the user's RP credentials. The "User Logins" could be stored on the user's machine, or in the browser or at a particular website. (In cookies, in localStorage, etc. How to do this?)
  5. OLD. There must be a way to obtain a "proof of identity". This may involve communicating with an external website. The user's IdP. There must be a standardized way of automatically communicating with the IdP.

Step 1. Attach CapisControl.invokeServiceFromTriggerHtmlElement as a click handler to all Anchor HTML Elements. Use CSS selector "a[rel=servicetrigger]" and "addEventListener" to add a click handler.

Capis API - Function: CapisControl.CAPIS_init()

Return Type void;

This function initializes CAPIS.

Example JavaScript:
function CAPIS_init() {
  // let capisControl_api = navigator.id.capisControl_api;
  // let capisControl_api = window.capisControl;

  // Find all "servicetrigger" HTML elements. Hook them up. (Add an event handler)
  let triggerAnchorList = window.document.querySelectorAll("a[rel=servicetrigger]");
  let triggerButtonList = window.document.querySelectorAll("button[servicetriggerdata]");
  let triggerFormList = window.document.querySelectorAll("form[rel=servicetrigger]");
  let clickList = [];
  triggerAnchorList.forEach(function (item) {clickList.push(item);})
  triggerButtonList.forEach(function (item) {clickList.push(item);})

  clickList.forEach((triggerEl) => {
    triggerEl.addEventListener("click", function (evt){return CAPIS_handleUserEvent(evt);});
  });
  triggerFormList.forEach((triggerEl) => {
    triggerEl.addEventListener("submit", function (evt){return CAPIS_handleUserEvent(evt);});
  });

  /*
   * Create a special "capis_stylesheet" and add it to the page head.
   * The capis_stylesheet is how CAPIS automatically changes how
   * certain HTML elements are displayed.
   * Example: class="capisRemere-LoggedOut", class="capisRemere-LoggedIn",
   * Special Case: class="capisRemere-NoSupport"
   * The anchors/buttons marked with these classes are displayed or hidden.
   *
   * The "NoSupport" classes are special. They are meant to be visible ONLY
   * if the browser does not support CAPIS.
   * When CAPIS loads, it adds a stylesheet rule to hide the "NoSupport" class.
   * Any HTML element with class="capisRemere-NoSupport" will be visible by default.
   * If CAPIS did not load correctly. (i.e. If it is unsupported by the browser.)
   */
  let capis_stylesheet = document.createElement('style');
  ...
  document.head.appendChild(capis_stylesheet);

  // Change the capis_stylesheet in response to CAPIS events. (Such as loginStatusChange.)
  remereLogin_api.addEventListener("loginStatusChange", RemereLogin_onLoginStatusChange);
}
window.addEventListener("load", CAPIS_init);

Capis API - Function: CapisControl.CAPIS_handleUserEvent(uiEvent)

Return Type void;

This function is called as a "click handler" function on a ServiceTrigger HTML Element HTML element. (Such as an anchor, button, form. etc.)

This function calls CapisControl.invokeServiceFromTriggerHtmlElement(htmlElement).

Example JavaScript:
function CAPIS_handleUserEvent(uiEvent) {
  let funcNameV = "CCI.handleUserEvent ";
  let htmlElement = this;
  uiEvent.preventDefault();
  let eventType = uiEvent.type;
  // eventType = "click" or "submit", etc.
  // let htmlElement = uiEvent.target;
  let promise = Promise.resolve("yyy")
  .then(yyy_response => {
    return capisControl_api.invokeServiceFromTriggerHtmlElement(htmlElement);
  }).then(triggerResponse => {
    return triggerResponse;
  }).catch(error => {
    let uiEvent2 = uiEvent;
    let funcNameP = "CCI.handleUserEvent";
    let funcArgSt = "uiEvent [type=" + uiEvent.type + "]";
    CapisError_pushStackFrame(error, funcNameP, funcArgSt);
    let displayErrorMessage = false;
    if (error.message.startsWith("Action canceled")) {
      displayErrorMessage = false;
    } else {
      displayErrorMessage = true;
    }
    if (displayErrorMessage) {
      let stBrief = CapisError_assembleStackTraceBrief(error);
      let msg3 = error.message + "\n" + stBrief.stMsg;
      // alert("Error occurred. " + msg3);
      alert("Error occurred. msg= " + msg3);
    }
    // CCI_popup(error); // Display a popup dialog with the error.
    return Promise.reject(error);
  });
  // return promise;
  return false;
}

The browser needs to treat Anchor HTML Elements as if they had a standard capis JavaScript click handler.

Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }' onclick="function (evt){capisControl_api.handleUserEvent(this, evt)}">Login</a>

RML Remote Usage. Calling the RemereLogin API functions from non-RP web pages

It can be beneficial for a browser to store direct links to an FiskCatalog. Browsers are encouraged to store opId="login" links. This way, the user does not have to load the RP website in order to look for a "setActiveCredentials" link on the RP website. The "serviceCatalogUri" in the login link is generally the same destination as a "setActiveCredentials" link. (As part of its "password manager", the browser remembers all links where the user logged in via the opId="login".) Therefore, the browser or password manager can display an option screen to the user, and it can contain generated links to RPs to change the user's public key at that RP.

Example HTML Anchor:
<a href="..." rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>

Form Signing

An RP can request that a form be cryptographically signed by the user when it is submitted. This may help prove that the user made the submission, or agreed to the terms in the form, etc. An RP does this by setting the FiskTrigger.protocolOp property to be "remere/signForm". (Note that the FiskTrigger.protocolOp property referred to here is not the "action" attribute of the form. Instead, it is one of the FiskTrigger object inside the name="serviceguidedata" attribute.)

Example Form:
<form action="some_uri" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'> </form>

"Form-signing" can be used for:

Form snapshot

A signed form must contain much more data than a regular form submission. This data is contained in a "form snapshot". It is a JSON representation of the form.

How it works

The use of the "remere" protocol and "signForm" opId (along with the rel="servicetrigger" attribute) will cause the CapisControl.invokeServiceFromTriggerHtmlElement(formElement) function to be executed instead of the regular browser behavior when the submit button is pressed. This new function does the same as the regular behavior, validates the form as usual, fires the same events, etc. but as a last step, it causes the CapisControl.invokeServiceOp(protocolId, "signForm", opArgs, fiskProfilePath, triggerView) function to be executed, instead of submitting the form.

The RemereLogin.performServiceOp("signForm", opArgs, fiskProfilePath, triggerView) function causes a "form signature" pop-up dialog to be displayed to the user. (Instead of submitting the form.) The pop-up dialog shows a pull-down menu for the user to select which certificate (which User Login) to use to sign the form. There is also an option to create or import a User Login. After the User Login is selected, the CapisControl.createFormSnapshot(formElement, UserId) function is called. And that creates a form signature and adds it as "proof" to the RML Request JSON object.

When the submit button is pressed, all regular JavaScript code runs (such as a validator, and event listeners). The validator, etc, may stop the form submission before the RemereLogin.performServiceOp("signForm", opArgs, fiskProfilePath, triggerView) function is called.

More details:

The invokeServiceFromTriggerHtmlElement function gets the FiskTrigger.protocolOp property. It then calls CapisControl.invokeServiceOp.

First, the invokeServiceFromTriggerHtmlElement function detects that the triggerHtmlElement is a form. Then it calls the regular "onsubmit" JavaScript code. (a validator, etc.) If the regular code returns a false or otherwise stops the form submission, then invokeServiceOp stops execution. Second, if the form is still to be submitted, then the invokeServiceOp function checks if the opId is "signForm". If it is, then the form signing path is taken. Third, a dialog is presented to the user asking which User Login (and key) they want to use to sign the form. Fourth, the invokeServiceOp function creates a JWT, that has all the data in the form copied to the JWT payload section. Fifth, it signs the JWT with the user's private key, and adds the JWT to the RML Request JSON object, as an additional property.

Example HTML Form:


What is your gender? : :
What kind of car do you want?

Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm", "onSuccess":{"actions":[{"redirectUri":"/some/path/formConfirm.php"}]} }'> <input type="text" name="firstname" placeholder="Enter your first name"><br/> <input type="text" name="lastname" placeholder="Enter your last name"><br/> What is your gender? <label for="i_male">Male</label>: <input type="radio" id="i_male" name="gender" value="male"> <label for="i_female">Female</label>: <input type="radio" id="i_female" name="gender" value="female"><br/> What kind of car do you want?<br/> <select multiple name="carmake"> <option value="volvo">Volvo</option> <option value="toyota">Toyota</option> <option value="ford">Ford</option> </select><br/> <input type="hidden" name="today_date" value="2018-01-12"> <input type="submit" value="Submit"> </form>
Example FiskProfile:
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] }, "requirement_section": {} }
The HTTP values:
Example HTTP:
firstname=Mickey
lastname=Mouse
gender=male
carmake=toyota,volvo
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "signData", "sub": "remere/signData", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId "usk": "q3jC9-h" }, "user_auth_method": "only_MSUID", "redirectUri": "/some/path/page.html", "useCount": 42,
// user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ],
// crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "TBD-CPF", // "format": "TBD-CPF" (not necessary. The default is "TBD-CPF".) "val_p": "mmmm" // base64url encoded JWT or signature. } ] }, "form_details": { "iss": "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k", // The Backer doing the signing. formSnapshot: [ // The form contents. {"n":"firstname", "t":"text", "v":"Mickey"}, {"n":"lastname", "t":"text", "v":"Mouse"}, {"n":"gender", "t":"radio", "v":"male", "o":[{"l":"Male","v":"male","s":true},{"l":"Female","v":"female"}]}, {"n":"vehicle", "t":"checkbox", "v":[], "o":[{"l":"I have a bike","v":"Bike"},{"l":"I have a car","v":"Car"}]}, {"n":"carmake", "t":"select-multiple", "v":["toyota","volvo"], "o":[{"l":"Toyota","v":"toyota","s":true}, {"l":"Volvo","v":"volvo","s":true},{"l":"Audi","v":"audi"}]} ] }, /* "credRegSec" credentialRegSection. Not required for opId="signData". "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { ... } } ], "crList": [] } */ };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256", "sct":192} "header": { "kid": "cd1", "kid2": "e9bc097a-ce51-4036-9562-d2ade882db0d" // "kid": "userIdPK" // "userIdPK" means the public key is in "uid". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] } };
Example Certificate:

The "UserId certificate" JWT. header and claims: (uic_chain[0])

let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, "exp": 1533064012, "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "uid": { "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "usk": "q3jC9-h" }, "cert_pubKeyList": [ { "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." } ] };
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED "csvm": "RDAC" // How to get the sig verification key from the issuer. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. // "csvm": "idr_keystore" // Get the sig verification key from an "Backer keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

The JWT MUST have an issue date, and an issuer string that contains the User Login. That is, the required fields include the "kid" value in the header, and both an "iat" and "iss" claim values. When validating the form, the issuer string is extracted, and used to look up the sig verification key set for that issuer. The "kid" specifies which key was used. (Note that the key used may not belong to the RP. The RP may need to retrieve the list of keys from an internet address.)

Form Snapshot

A form snapshot is a JSON representation of the form. The HTML form is converted into JSON, and most of the form fields and attributes are retained.

A form snapshot must contain much more data than a regular HTTP form submission. A form snapshot is about proving what data a user provided, and thus it is necessary to document the questions on the form, and what options were available for each of them. An HTTP form submission is not sufficient, as it only contains the values that were sent to the server. It doesn't contain the labels, options or even the field types (text, radio button, etc). Furthermore, there is no distinction between the values the user provided, and values from "hidden" input elements. Without this distinction, it would be possible for form authors to add hidden form fields and make it seem that a user agreed to something they were not aware of.

A form snapshot converts each input element in a form into a JSON object. Each converted input element will include the name, type, value, label, and the options available. The options will include the label, value and a marker for if the option is selected. Each option can have its own label.

See CapisControl.createFormSnapshot(formElement).

Capis API - Function: CapisControl.createFormSnapshot(formElement)

Return Type Object;

Converts an HTML form into a JSON object. Retains most of the form fields, attributes, labels and options.

Function info: (parameter list, return value, etc.)
  • @param {DOMElement} formElement; The HTML form element to extract the data from.
  • @return {FormSnapshot} The form snapshot. A JSON object.

This function goes through the form, and converts each input element into a JSON object. Each converted input element will include the name, type, value, label, and the options available. The options will include the label, value and a marker for if the option is selected. Each option can have its own label. (The options are limited to 50 for select fields).

A form snapshot is necessary because the regular HTTP form submission does not retain the questions on the form. It doesn't contain the labels, options or even the field types (text, radio button, etc). Without that data, it is hard to verify what questions the user was answering. See Form Snapshot.

See Form signing.

Details:

The snapshot:

The form snapshot contains a lot more data than a standard form submission. It contains:
Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/signForm", "onSuccess":{"actions":[{"redirectUri":"/some/path/formConfirm.php"}]} }'> <input type="text" name="firstname" placeholder="Enter your first name"><br/> <input type="text" name="lastname" placeholder="Enter your last name"><br/> <input type="text" id="i_nickname" name="nickname"><label for="i_nickname">What you like to be called.</label><br/> What is your gender? <label for="g_male">Male</label>: <input type="radio" id="g_male" name="gender" value="male"> <label for="g_female">Female</label>: <input type="radio" id="g_female" name="gender" value="female"><br/> What kind of vehicles do you have?<br/> <input type="checkbox" id="v_bike" name="vehicle" value="bike" checked> <label for="v_bike">I have a bike</label><br/> <input type="checkbox" id="v_car" name="vehicle" value="car" checked> <label for="v_car">I have a car</label><br/> <input type="checkbox" id="v_boat" name="vehicle" value="boat"> <label for="v_boat">I have a boat</label><br/> <select multiple name="carmake"> <option value="volvo">Volvo</option> <option value="toyota">Toyota</option> <option value="ford">Ford</option> </select><br/> <select name="favoriteFruit"> <option value="apple">Apple</option> <option value="orange">Orange</option> <option value="pear">Pear</option> </select><br/> <input type="hidden" name="today_date" value="2018-01-12"> <input type="submit" value="Submit"> </form>
Example JSON:
// createFormSnapshot example output.
formSnapshot: {
  "creationDate": 1555876543210,
  "fieldList": [ // The list of fields in the form.
    {"n":"firstname", "t":"text", "v":"Mickey"},
    {"n":"lastname", "t":"text", "v":"Mouse"},
    {"n":"nickname", "t":"text", "v":"Big Ears", "l":"What you like to be called."},
    {"n":"gender", "t":"radio", "v":null, "selectedIndex":-1,
      "o":[{"l":"Male","v":"male"},{"l":"Female","v":"female"}]},
    {"n":"vehicle", "t":"checkbox", "v":["bike","car"], "selectedIndexList":[0,1],
      "o":[{"l":"I have a bike","v":"bike","s":true},{"l":"I have a car","v":"car","s":true},{"l":"I have a boat","v":"boat"}]},
    {"n":"carmake", "t":"select-multiple", "v":[], "selectedIndexList":[],
      "o":[{"l":"Toyota","v":"toyota"}, {"l":"Volvo","v":"volvo"}, {"l":"Ford","v":"ford"}]},
    {"n":"favoriteFruit", "t":"select-one", "v":"pear", "selectedIndex":2,
      "o":[{"l":"Apple","v":"apple"}, {"l":"Orange","v":"orange"}, {"l":"Pear","v":"pear","s":true}]},
    {"n":"today_date", "t":"hidden", "v":"2018-01-12"},
  ]
}

In JSON, names and values cannot contain double quotes. So, both the names and values of the form items will need to have any double quotes encoded. (Should use json encoding, backspace, '\"', not html "&quot;")

Example JavaScript code for creating a Javascript object with all the form names and values.
Example JavaScript:
// example implementation
/**
 * Extracts structure and user data from a form as an array of data objects.
 * Objects include name, value, type, option_list and option labels.
 * Important: checkboxes and radio buttons with the same name are combined.
 * Each individual radio button or checkbox is one option for the shared name.
 * Single select items (radio, select-one) will have a value of null if nothing is selected.
 * Multi-select items will have a value of the empty array if nothing is selected.
 * Also, the array of selected options ("so"), is an attempt to minimize the length of the option list.
 * @return {FormSnapshot} The form snapshot. A JSON object.
 */
function CCI_createFormSnapshot(formElement) {
  if (formElement === undefined) {
    throw new Error(funcNameV + "Missing form argument.");
  }
  if (formElement.nodeName !== "FORM") {
    throw new Error(funcNameV + "Invalid 'form' argument. Not a form.");
  }
  let theForm = formElement;
  let formData = {}; // The data to be signed.
  formData.id = formElement.id;
  formData.creationDate = new Date().getTime();
  let fieldList = []; // The list of fields
  let fieldSet = {};
  let fieldMap = new Map();
  // let data_names = {};
  let elem;
  let formLen = formElement.elements.length;
  for(let i = 0; i < formLen; i++) {
    elem = formElement.elements[i];
    let fldObj;
    let addToList = true;
    let prevItem = fieldMap.get(elem.name);
    if (prevItem !== undefined) {
      // Only "radio" and "checkbox" types are allowed to have a prevItem (with the same name).
      if (elem.type == "radio" || elem.type == "checkbox") {
        fldObj = prevItem;
        addToList = false;
      } else {
        throw new Error("Invalid form. Has two fields of type=" + elem.type + " with the same name. name=" + elem.name);
      }
    } else {
      fldObj = {};
      fldObj.n = elem.name;
      fldObj.t = elem.type;
      if (elem.type == "checkbox" || elem.type == "select-multiple") {
        fldObj.v = [];
      } else {
        // fldObj.v = null;
      }
      // fieldMap.set(elem.name, fldObj);
    }
    switch (elem.type) {
      case "hidden":
      {
        fldObj.v = elem.value;
        // fldObj.l = Form_getLabelFor(theForm, elem.id);
      }
        break;
      // These types do not have a "name". submit, reset, button, image
      case "submit":
      case "reset":
      case "button":
      case "image":
        addToList = false;
        break;
      // Do not include passwords in the fieldList.
      case "password":
      // case "file":
      {
        fldObj.v = null; // elem.value
        fldObj.l = Form_getLabelFor(theForm, elem.id);
        // addToList = false;
      }
        break;
      case "select-multiple":
      {
        let valueList = [];
        let optionList = [];
        fldObj.l = Form_getLabelFor(theForm, elem.id);
        fldObj.o = optionList;
        fldObj.selectedIndexList = [];
        let soList = []; // Array of only the selected options.
        for(let k = 0; k < elem.length; k++) {
          let op = {};
          op.l = elem.options[k].text;
          op.v = elem.options[k].value;
          optionList.push(op);
          if(elem.options[k].selected) {
            op.s = true; // option is selected.
            valueList.push(elem.options[k].value);
            soList.push(op);
            fldObj.selectedIndexList.push(optionList.length-1);
          }
        }
        fldObj.v = valueList;
        fldObj.so = soList;
      }
        break;
      case "select-one":
      {
        let theValue;
        let optionList = [];
        fldObj.v = null;
        fldObj.l = Form_getLabelFor(theForm, elem.id);
        fldObj.o = optionList;
        for(let k = 0; k < elem.length; k++) {
          let op = {};
          op.l = elem.options[k].text;
          op.v = elem.options[k].value;
          optionList.push(op);
          if (elem.options[k].selected) {
            op.s = true; // option is selected.
            theValue = elem.options[k].value;
            // fldObj.selectedIndex = (optionList.length-1);
          }
        }
        fldObj.v = theValue;
        fldObj.selectedIndex = elem.selectedIndex;
        if (elem.selectedIndex != -1) { fldObj.v = elem.options[elem.selectedIndex].value; }
      }
        break;
      case "radio":
      {
        if (prevItem === undefined) {
          fldObj.v = null;
          fldObj.o = []; // optionList
        } else {
          fldObj = prevItem;
          addToList = false;
        }
        let optionList = fldObj.o;
        let op = {};
        op.l = Form_getLabelFor(theForm, elem.id);
        op.v = elem.value;
        optionList.push(op);
        if(elem.checked) {
          op.s = true; // option is selected.
          fldObj.v = elem.value;
          fldObj.selectedIndex = optionList.length-1;
        }
      }
        break;
      case "checkbox":
      {
        if (prevItem === undefined) {
          fldObj.v = [];
          fldObj.o = []; // optionList
          fldObj.so = []; // Array of only the selected options.
          fldObj.selectedIndexList = [];
        } else {
          fldObj = prevItem;
          addToList = false;
        }
        let valueList = fldObj.v;
        let optionList = fldObj.o;
        let soList = fldObj.so;
        let op = {};
        op.l = Form_getLabelFor(theForm, elem.id);
        op.v = elem.value;
        optionList.push(op);
        if(elem.checked) {
          op.s = true; // option is selected.
          valueList.push(elem.value);
          soList.push(op);
          fldObj.selectedIndexList.push(optionList.length-1);
        }
      }
        break;
      // case "text", case "textarea", etc.
      default:
      {
        fldObj.v = elem.value
        fldObj.l = Form_getLabelFor(theForm, elem.id);
      }
    }
    if (addToList) {
      fieldList.push(fldObj);
    }
    if (prevItem === undefined) {
      fieldMap.set(elem.name, fldObj);
    }
  }
  formData.fieldList = fieldList;
  formData.fieldSet = fieldSet;
  formData.fieldMap = fieldMap;
  return formData;
}
function Form_getLabelFor(theForm, theId) {
  let label = "";
  let html_labels = theForm.querySelectorAll("label[for='" + theId + "']");
  for( let index=0; index < html_labels.length; index++ ) {
    // Probably need some check in here to ensure the label is visible.
    label += html_labels[index].textContent;
  }
  return label;
}
========

API Data Types

API Data Types Type: string ("json_string") // A quoted string.

API Data Types Type: int // Data MUST be an integer. (Not a float)

API Data Types Type: float // Data MUST be a floating point number.

API Data Types Type: number // Data may be an integer or a float.

API Data Types Type: boolean

API Data Types Type: Object ("simple_object")

API Data Types Type: Array

API Data Types Type: JavaScript Promise ("Promise")

API Data Types Type: JavaScript void

API Data Types Type: Slightly modified JSON ("HAM-JSON")

Type Slightly modified JSON
A slightly modified JSON format for use inside an HTML attribute. (HTML Attribute Modified JSON)

Used by HTML ATTRIBUTE ServiceGuide HTML Element name="serviceguidedata" and ServiceTrigger HTML Element "servicetriggerdata"

There are a few differences from standard JSON. First, property names and string values MAY be enclosed with the caret character (^) instead of the double quote character. This format was added for convenience, because HTML attribute values can be double quoted, and that interferes with standard JSON string quoting. (Internally, all caret characters in the attribute value are simply converted into double quote characters.) A caret character can be included by using "%caret;". A single quote character can be included by using "%sq;".

In order to include a double quote mark in a JSON property name or string value, prefix it with a backslash. HTML entity encoding can also be used. Single Quote (') = (&apos;) Double Quote (") = (&quot;) Less than (<) = (&lt;) Greater than (>) = (&gt;)

// Allow double quoted HTML attributes to contain double quotes. Ex: attr="{^prop1^:"pick up", ^prop2^:false}"
// Allow single quoted HTML attributes to contain single quotes. Ex: attr='{"prop1":"pick up Steven%sq;s food"}'
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "test":"Eat at Lefty&apos;s in \"Smallville\"!", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Login</a>
Example HTML Anchor: using caret quote characters:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata="{ ^protocolOp^:^remere/login^, ^test^:^Eat at Lefty's in \^Smallville\^!^, ^onSuccess^:{^actions^:[{^redirectUri^: ^/some/path/page.html^}]} }">Login</a>

API Library

Optional Javascript Library

A set of API functions for the browser to call in order to perform the behavior required of "auth" elements. These API functions are exposed as Javascript functions.

Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images/logo.png" }, "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"] // "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "supported.uic.csvm": ["basic1", "basic2"] } }
] };

The CapisControl.invokeServiceFromTriggerHtmlElement(htmlElement) is the top level function. It accepts a ServiceTrigger HTML Element. Internally, they call CapisControl.invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView).

Note that "many of" these functions have a "context" of the current browser tab.

All "serviceCatalogUri" values must start with a slash. (Also "smallIcon" values too.) They CANNOT contain a domain name. It's a security risk. This is because the functions can be called directly from JavaScript running in a browser tab. A tab should not be able to log into or out of a different site. (Cross site scripting attack.)

API Table Of Contents

API Class: CapisControl

Global API Object: capisControl_api

API Class: ProtocolHandler

API Class: RemereLogin

Global API Object: remereLogin_api

Level 1 Javascript API functions.

API Class: LoginManager

The LoginManager functions are called from RemereDirector functions.
The LoginManager functions that accept the "fiskClientData" argument are Authenticator functions. (And collectUserLogins is also a required Authenticator function.) The purpose of fiskClientData is to be included in a signature, created by the Authenticator, and returned to the RP. This lets the RP know that the Authenticator received the correct data. For example, The RP can choose to use a parent domainName. The RP needs proof that the Authenticator knew that when it created the signature. The fiskClientData is used when a response from the Authenticator is delivered to the RP. The fiskClientData is used (1) when creating credential signatures, and (2) When CHANGING active credentials (3) when creating a User Login. (Note: creating credential sources does NOT require an fiskClientData.) The credential signatures and attestation signature are signed over the fiskClientData hash.

API Class: RemereDirector

Low level functions, internal to the browser implementation. These are suggestions only. They are not public, and could be implemented completely differently. The first group is called by the RemereLogin functions. Private functions
======== Private Key APIs. ========
  • authsecure.generateKeyPair() -- Generates a key pair. Stores private key securely. -- Also generates an "id" for the key pair. Records the keys internally. Returns only the public key.
  • authsecure.createIdentitySignature(); -- Creates a JWT signed by the userId private key.
  • LoginManager.createUserIdentityProof(header, payload, privateKey);

CapisControl types

API Data Types Type: FiskClientData

@typedef {Object} FiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @property {string} capId; The Client Application Id. Ex: "login.example.com"
  • @property {string} ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @property {string=} appIdSpec; The Auth Application Id. Ex: "example.com/remereApp/1234567890"

API Data Types Type: FiskProfilePath

@typedef {Object} FiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @property {string} fiskProfileId; The FiskProfile Id. Ex: "remere_1"
  • @property {string} fiskDigestId; The FiskDigest Id. Ex: "offer_1"

API Data Types Type: UlFilterOptions

@typedef {Object} UlFilterOptions; The User Login filter criteria. (Limit the returned list.)
  • @property {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @property {Object} searchFiskProfile; The FiskProfile searched for.
  • @property {RapSourceList[]} altRapSourceList; A list of alternate ulSource items.
  • @property {string} capId; The Client Application Id. Ex: "login.example.com"
  • @property {string} ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • The capId limits access. Only some RPs are allowed to create a collectUserLogins request.
  • @object_example mRapSourceItem = {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}

API Data Types Type: UlImportResult

@typedef {Object} UlImportResult; The User Login filter criteria. (Limit the returned list.)
  • @property {string} callId;

API Data Types Type: UlImportData

@typedef {Object} UlImportData; The User Login filter criteria. (Limit the returned list.)
  • @typedef {UlImportData} exportedLogin; The exported User Login to import.
  • @property {string} exportedLogin.userIdRaw; The userId value in the UserLogin.
  • @property {string} exportedLogin.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @property {CredSource[]} exportedLogin.credSrcExportList; A list of exported "Credential Source" objects.
  • @object_example credSrcExportItem = { cci: "834", type: "public-key", coseAlgId: -7}
  • @property {string} exportedLogin.disambigName; The disambiguation name.
  • @property {int} exportedLogin.creationDate; The timestamp when created.
  • @property {int} exportedLogin.lastLoginDate; The timestamp of the last login.
  • @property {string[]} exportedLogin.tagList; The list of "tags" to add to the UserLogin.
  • @property {CredCreation[]} exportedLogin.credCreationList; A list of "Credential Source creation" objects.

API Data Types Type: UlImportOptions

@typedef {Object} UlImportOptions; The User Login filter criteria. (Limit the returned list.)
  • @typedef {UlImportOptions} importOptions; The User Login import options.
  • @property {FiskClientData} importOptions.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @property {RpEntityInfo} importOptions.rpEntityInfo; The RP entity information. (For credential creation.)
  • @property {FiskClientData} importOptions.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @property {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @property {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"

API Data Types Type: CustomType ("CustomObject")

API Data Types Type: ServiceOperationArgs

@typedef {Object} ServiceOperationArgs;
  • @property {string} opId; The operation identifier. Ex: "login"

API Data Types Type: OpResult

@typedef {Object} OpResult;
  • @property {string} fiskProfileId; The FiskProfile Id. Ex: "remere_1"
  • @property {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @property {string} opId; The service operation to be performed. Ex: "login"
  • @property {string} callId; The id of the function call. (It increments.)
  • @property {PhBuildResult} buildResult; The data that was built.
  • @property {Object} requestData; The request that was built.
  • @property {SendResult} opSendResult; The response from the RP service.
  • @property {PhEvalResult} evalResult; The response from the evaluation.

API Data Types Type: PhBuildResult

@typedef {Object} PhBuildResult;
  • @property {Object} requestData; The data that was sent. Contains the proofOfIdentity.
  • @property {string} opId; The operation for the RP to perform. Ex: "login"
  • @property {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @property {string} callId; The unique identifier for the service call.
  • @property {string} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @property {Object} buildValues.savedFiskProfile; The selected FiskProfile.
  • @property {Object} buildValues.capId; The Client Application Id. Ex: "login.example.com"
  • @property {string} buildValues.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @property {FiskClientData} buildValues.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @object_example requestData = { JWA: {payload:"xxx", signatures:[{mm:"xx"}] } }

API Data Types Type: SendResult

@typedef {Object} SendResult;
  • @property {string} callId; The call id

API Data Types Type: PhEvalResult

@typedef {Object} PhEvalResult;
  • @property {string} callId; The call id

API Data Types Type: ResourceReqArgs

@typedef {Object} ResourceReqArgs;
  • @property {string} callId; The call id

API Data Types Type: PhInfoResult

@typedef {Object} PhInfoResult;
  • @property {string} callId; The call id

API Data Types Type: PageConfig

@typedef {Object} PageConfig;
  • @property {string} callId; The call id

API Data Types Type: ProtocolHandler

@typedef {Object} ProtocolHandler;
  • @property {string} callId; The call id

API Data Types Type: FiskTriggerWrap

@typedef {Object} FiskTriggerWrap; Contains the settings and ancillary information. (Includes srcHtmlElement, etc.)
  • @property {FiskTriggerContents} trContents; The trigger contents. (processed and corrected)
  • @property {Object} importContents; The raw import. Before processing. (parsed from JSON.)
  • @property {FiskProfilePath} fiskProfilePath; The FiskProfilePath.
  • @property {DOMElement} srcHtmlElement; The HTML element that acts as the FriskTrigger.
  • @property {TriggerSuccessHandler} onSuccess; The success handler.

API Data Types Type: FiskTriggerView

@typedef {Object} FiskTriggerView;
  • @property {FiskTriggerContents} srcContents; The source contents.
  • @property {DOMElement} srcHtmlElement; The HTML element that is the source of the FriskTrigger.

API Data Types Type: FiskDigestWrap

@typedef {Object} FiskDigestWrap;
  • @property {string} protocolOp; The Service Protocol Id and opId. Ex: "remere/login"
  • @property {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @property {string} opId; The service operation to be performed. Ex: "login"
  • @property {string} offerId; The FiskDigest Id. Ex: "offer_1"
  • @property {string} fiskProfileId; The FiskProfile Id. Ex: "remere_1"
  • @property {Object} htmlElement; The HTML element that was activated.

API Data Types Type: FiskCatalogWrap

@typedef {Object} FiskCatalogWrap;
  • @property {string} protocolUse; The Service Protocol Id. Ex: "remere"

API Data Types Type: FiskCatalogInput

@typedef {Object} FiskCatalogInput;
  • @property {string} protocolUse; The Service Protocol Id. Ex: "remere"

API Data Types Type: FiskProfileWrap

@typedef {Object} FiskProfileWrap; A wrapper for the SocInfo and FiskProfile info. Or null.
  • @resultProperty {Object} profileContents; The FiskProfile contents.
See the FiskDigest info.

API Data Types Type: FiskProfileInput

@typedef {Object} FiskProfileInput; A wrapper for the SocInfo and FiskProfile info. Or null.
  • @resultProperty {Object} profileContents; The FiskProfile contents.
See the FiskDigest info.

API Data Types Type: SvReqContext

@typedef {Object} SvReqContext;
  • @property {Object} requestData; The data that was sent. Contains the proofOfIdentity.
  • @property {string} opId; The operation for the RP to perform. Ex: "login"
  • @property {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @property {string} callId; The unique identifier for the service call.
  • @property {string} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @property {Object} buildValues.savedFiskProfile; The selected FiskProfile.
  • @property {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @property {string} capId; The Client Application Id. Ex: "login.example.com"
  • @property {string} ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @property {string} fiskClientData.challengeKey; The RP challenge value. Base64Url encoded.
  • @property {string} protocolId; The Service Protocol Id. Ex: "remere"

API Data Types Type: FiskProfileValidationResult

@typedef {Object} FiskProfileValidationResult;
  • @property {string} callId; The call id

API Data Types Type: PhSomething

@typedef {Object} PhSomething;
  • @property {string} callId; The call id

API Data Types Type: PhSomething

@typedef {Object} ExtractedFormData;
  • @property {FieldData[]} fieldList; The list of Field Info objects.
  • @property {Object} fieldSet; The set of Field Info objects. By name.

API Data Types Type: PhSomething

@typedef {Object} FormSnapshot;
  • @property {string} id; The id of the form.
  • @property {int} creationDate; The date the snapshot was created. Unix timestamp in milliseconds.
  • @property {FieldRec[]} fieldList; The list of "input elements" in the form.
  • @object_example fldObj = { n: name, t: type, v: value, l: label, o: optionList, so: selectedOptionList }
  • The array items may contain name "n", type "t", value "v", label "l", optionList "o".

CapisControl.pRemereLogin_api

The pRemereLogin_api is a pointer internal to the CapisControl object. It points to a remereLogin_api.

The definition of the const variables.

const remere_login_protocol_id = "remere";

Capis API - Function: CapisControl.invokeServiceFromTriggerHtmlElement(htmlElement)

Return Type Promise<OpResult>

This function is called as a "click handler" function on a ServiceTrigger HTML Element HTML element. (Such as an anchor, button, form. etc.)

The difference between invokeServiceFromTriggerHtmlElement and activateFiskTrigger is that, in effect, invokeServiceFromTriggerHtmlElement extracts the fiskTriggerContents from the htmlTrigger, and then calls activateFiskTrigger. (The invokeServiceFromTriggerHtmlElement function uses the htmlElement arguement as the source of the fiskTriggerContents.)

Internal steps:
Example JavaScript implementation:
// example implementation
/**
 * Invokes the service defined in the special "servicetrigger" attribute of the HTML element.
 * Activates the FiskTrigger object inside the specified HTML element.
 * Extracts the FiskTrigger object from the HTML attribute. Then performs the opId inside the FiskTrigger.
 * @param {DOMElement} htmlElement; The HTML element object.
 * @return {Promise<OpResult>} The response object.
 */
function CCI_invokeServiceFromTriggerHtmlElement(htmlElement) {
  let nodeName = htmlElement.nodeName;
  let elementTitle;
    if (htmlElement.id !== undefined && htmlElement.id != "") {
      elementTitle = "id=" + htmlElement.id;
    } else {
      let classText = "";
      htmlElement.classList.forEach((item, index) => { classText += " " + item; });
      elementTitle = "classList=\"" + classText + "\"";
    }
  let protocolId;
  let opId;
  let promise = Promise.resolve("yyy")
  .then(yyy_response => {
    return CCI_ensurePageConfig();
  }).then(pageConfig => {
    let fiskTriggerWrap = CCI_extractFiskTriggerFromHtmlElement(htmlElement);
    let fiskTriggerCts = fiskTriggerWrap.trContents;
    let protocolId = fiskTriggerCts.protocolId;
    let opId = fiskTriggerCts.opId;
    let opArgs = fiskTriggerCts.opArgs;
    let fiskProfilePath = fiskTriggerWrap.fiskProfilePath;
    return capisControl_api.invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView);
  }).then(serviceOpResponse => {
    return serviceOpResponse;
  }).catch(error => {
    let funcNameP = "CCI.invokeServiceFromTriggerHtmlElement";
    let funcArgSt = "htmlElement [nodeName=" + nodeName + " " + elementTitle + "]";
    CapisError_pushStackFrame(error, funcNameP, funcArgSt);
    return Promise.reject(error);
  });
  return promise;
}

Capis API - Function: CapisControl.activateFiskTrigger(fiskTriggerCts, htmlElement)

Return Type Promise<OpResult>;

Performs the specified "Trigger Action". Performs the opId inside the FiskTrigger.

Called by the browser when the user clicks a Service Trigger HTML Element. (Such as a Anchor HTML Element with rel="servicetrigger" attribute.)

Function info: (parameter list, return value, etc.)
  • @param {FiskTriggerWrap} fiskTrigger; The FiskTrigger object.
  • @param {DOMElement} htmlElement; The HTML element object.
  • @return {Promise<OpResult>} The service operation response object.
Internal steps:

This handler registers an internal function as a failure callback to receive error and failure notifications from the CapisControl.invokeServiceOp function. If "CapisControl.invokeServiceOp" encounters an error, or cannot complete correctly for any reason, then the failure callback is called. That will return control to this handler. It may then decide to display an error message to the user (as a popup or dialog box, etc.), or pass the error on to the browser.

For example: if a login was started, but the user cancels it. or the user supplied the wrong password to the IDP, etc. etc.

Extracts all the Configuration Settings from the anchor element (inside the "servicetriggerdata" attribute). For example, this includes: FiskTrigger.protocolOp, "challengeKey", etc.

Example ServiceTrigger HTML Element:
// arguments <meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a>
See the example_mainFiskCatalog.json document.
Example JavaScript implementation:
// example implementation
/**
 * Performs the "Trigger Action" inside the FiskTrigger. Performs the opId inside the FiskTrigger.
 * @param {FiskTriggerWrap} fiskTrigger; The FiskTrigger JSON contents.
 * @param {DOMElement} htmlElement; The HTML element object.
 * @return {Promise<OpResult>} The service operation response object.
 */
function CCI_activateFiskTrigger(fiskTrigger, htmlElement) {
  let funcNameV = "CCI.activateFiskTrigger ";
  if (fiskTrigger === undefined) {
    throw new Error("Missing fiskTriggerCts argument.");
  }
  let fiskTriggerCtsInput = fiskTrigger.trContents;
  let protocolId;
  let opId;
  // let fiskDigestId;
  let fiskProfilePath;
  let promise = Promise.resolve("yyy")
  .then(yyy_response => {
    return CCI_ensurePageConfig();
  }).then(pageConfig => {
    let fiskTriggerWrap = CCI_extractFiskTriggerFromContents(fiskTriggerCtsInput, htmlElement);
    let fiskTriggerCts = fiskTriggerWrap.trContents;
    protocolId = fiskTriggerCts.protocolId;
    opId = fiskTriggerCts.opId;
    let opArgs = fiskTriggerCts.opArgs;
    fiskProfilePath = fiskTriggerWrap.fiskProfilePath;
    if (protocolId === undefined) {
      throw new Error("Invalid FiskTrigger. Missing the 'protocolId' property. (In the trigger HTML element.)");
    }
    if (opId === undefined) {
      throw new Error("Invalid FiskTrigger. Missing the 'opId' property. (In the trigger HTML element.)");
    }
    return capisControl_api.invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView);
  }).then(serviceOpResponse => {
    return serviceOpResponse;
  }).catch(error => {
    let funcNameP = "CCI.activateFiskTrigger";
    let funcArgSt = "fiskTrigger [protocolId=" + protocolId + " opId=" + opId + " fiskDigestId=" + fiskProfilePath.fiskDigestId + "]";
    CapisError_pushStackFrame(error, funcNameP, funcArgSt);
    return Promise.reject(error);
  });
  return promise;
}

Capis API - Function: CapisControl.performServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView)

Return Type Promise<OpResult>; The "login" operation can be canceled.

Will perform the service action specified by the protocolId and the opId. Will find the protocolId for the service, then call that Protocol Handler. Give the handler the action to perform (the opId) and any arguments (the opArgs). (The arguments are often specified in a service trigger. The FiskTrigger.protocolOp property, etc. Ex: profileId specifies the fiskProfilePath.fiskProfileId value.)

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} opId; The service operation to be performed. Ex: "login"
  • @param {ServiceOperationArgs} opArgs; The set of args to be sent to the service operation.
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @param {FiskTriggerView} triggerView; The trigger that started the event.
  • @return {Promise<OpResult>} The service operation response object.
Example JavaScript implementation:
// example implementation
function CCI_performServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView) {
  return CCI_invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView);
}

Capis API - Function: CapisControl.invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView)

Return Type Promise<Object>; The "login" operation can be canceled.

Performs a service operation.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} opId; The method (function name) for the service to perform. (Ex: "login", "setActiveCredentials", etc.)
  • @param {ServiceOperationArgs} opArgs; The set of args to be sent to the service operation.
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @param {FiskTriggerView} triggerView; The trigger that started the event.
  • @return {Promise<OpResult>} The Protocol Handler response object.
Internal steps:
  • Calls the requested service operation. (fiskProfileId:"remere_1", "protocolOp":"remere/login" or "logout") etc.
  • Returns the Promise created from calling the opId function.

This function returns a Promise. If for any reason this function cannot complete correctly, then the promise's failure callback will be executed. For example, if the user cancels the login attempt, or the user supplied the wrong password.

The promise's failure callback is NOT called if the function completes correctly. For example, if the POST is sent to the website, the failure callback will never be called. Even if the website rejects the login or has some other error. In this example, if the website has an error, it can redirect the browser to a web page detailing the error.

Example JavaScript implementation:
// example implementation
function CCI_invokeServiceOp(protocolId, opId, opArgs, fiskProfilePath, triggerView) {
  let funcNameV = "CCI.invokeServiceOp ";
  // Make sure that CapisControl has read the pageConfig before calling the protocolHandler.
  return this.ensurePageConfig()
  .then(pageConfig => {
    if (protocolId === undefined) {
      throw new Error("Cannot invoke protocol action. Missing 'protocolId' argument.");
    }
    if (opId === undefined) {
      throw new Error("Cannot invoke protocol action. Missing 'opId' argument.");
    }
    if (protocolId.length < 2) {
      throw new Error("Invalid 'protocolId' =" + protocolId + " in HTML trigger. It must be at least 2 characters.");
    }
    if (opId.length < 2) {
      throw new Error("Invalid 'opId' =" + opId + " in HTML trigger. It must be at least 2 characters.");
    }
    if (opArgs === undefined) {
      opArgs = {};
    }
    if (fiskProfilePath === undefined) {
      // throw new Error("Missing fiskProfilePath argument.");
      fiskProfilePath = {};
    }
    let fiskDigestId = fiskProfilePath.fiskDigestId;
    if (fiskDigestId === undefined) {
      // throw new Error("Missing 'fiskDigestId' property.");
    }
    // let fiskProfileId = fiskProfilePath.fiskProfileId;
    if (fiskProfilePath.fiskDigestId === undefined) {
      let prSelect = CCI_getFirstSocInfo(protocolId);
      // fiskProfilePath.fiskDigestId = CCI_defaultFiskDigestId; // "offer_1"
      fiskProfilePath.fiskDigestId = prSelect.offerId;
      fiskProfilePath.fiskProfileId = prSelect.linkedFiskProfileId;
    }
    if (fiskProfilePath.fiskProfileId === undefined) {
      let fiskProfilePath_1 = CCI_getRegisteredFiskProfileInfo(protocolId, fiskProfilePath.fiskDigestId);
      if (fiskProfilePath_1 == null) {
        throw new Error("Invalid FiskDigest. Missing the 'fiskProfilePath' for protocolId=(" + protocolId + ").");
      }
      // fiskProfilePath.fiskDigestId = fiskProfilePath_1.offerId;
      fiskProfilePath.fiskProfileId = fiskProfilePath_1.profileId;
    }
    let protocolHandler = CCI_getProtocolHandler(protocolId);
    let opPromise;
    if (protocolHandler != null) {
      opPromise = protocolHandler.performServiceOp(opId, opArgs, fiskProfilePath, triggerView);
    } else {
      let prNoSupportCfg = CCI_getProtocolNoSupportConfig(protocolId, fiskProfilePath);
      throw new Error("Cannot invoke protocol action. Unregistered protocolId=" + protocolId + ", opId=" + opId);
    }
    return opPromise;
  }).then(serviceOpResponse => {
    // se9_debug.log("CapisControl.invokeServiceOp: resolved. response=" + JSON.stringify(serviceOpResponse));
    // TODO: In the future, reload the page here.
    // window.location.reload();
    // window.location.reload();
    let serviceOpResponse2 = serviceOpResponse;
    return serviceOpResponse;
  }).catch( error => {
    let funcNameP = "CCI.invokeServiceOp";
    let funcArgSt = "protocolId=" + protocolId + " opId=" + opId + " fiskDigestId=" + fiskProfilePath.fiskDigestId;
    CapisError_pushStackFrame(error, funcNameP, funcArgSt);
    return Promise.reject(error);
  });
}

Capis API - Function: CapisControl.getProtocolHandler(protocolId)

Return Type Object;

Will find and return the Protocol Handler for the specified Service Protocol Id.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @return {ProtocolHandler} The Service Protocol Handler. Or null.

Capis API - Function: CapisControl.ensurePageConfig()

Return Type Promise<PageConfig>; page_config_repository

Ensures the CapisControl has been initialized, then returns the PageConfig settings for the current page. The "pageConfig" is primarily a list of FiskDigest.

Function info: (parameter list, return value, etc.)
  • @return {Promise<PageConfig>} The CAPIS config values specified in the HTML page.

Calling this function will run the CapisControl initialization, if it has not already been performed. The initialization loads the information for the page. This includes the FiskDigest settings, Capis document, authAppId, etc. For example, it extracts the FiskDigest settings from the ServiceGuide HTML Element in the page. Retrieves the Capis document. Gets the authAppId from the Capis document.

Fetches the FiskCatalog from the RP.

This function finds the ServiceGuide HTML Element in the current HTML page head. Extracts the a FiskDigest from it. (i.e. FiskCatalog Detail) Extracts the serviceCatalogUri to use. Fetches the FiskCatalog from the RP. Returns a new Configuration Settings object.

As a final step, this function stores a copy of the PageConfig settings and FiskProfile so that it is available for future use.

The configuration descriptor details the authentication abilities that the RP authentication service has.

The defaultValues.socSiteCatalogDocUri of the FiskCatalog is a "well-known" URI. (Example: "RP_example.com/.well-known/mainFiskCatalog.json".)

The serviceCatalogUri option overrides the default Capis document location. It describes where the FiskCatalog is located.
Example Javascript use:
// Example JavaScript using "ensurePageConfig"
function doSomething(arg1) {
  let authAppId;
  let buildContext;
  // let fiskClientData = {};
  let ancPolicy = "allowSOWA"; // isSameOriginWithAncestors
  return capisControl_api.ensurePageConfig()
  .then(pageConfig => {
    let protocolId = "remere";
    let fiskDigestList = pageConfig.fiskDigestList;
    let fiskProfileId = "remere_1";
    let fiskDigestId = "offer_1";
    let fiskProfilePath = {fiskDigestId:fiskDigestId, fiskProfileId:fiskProfileId};
    let fiskProfileWrap = CCI_getFiskProfileInfo(protocolId, fiskProfilePath);
    let fiskProfileCts = fiskProfileWrap.pContents;
    let authAppId_2 = fiskProfileCts.appIdSpec;
    let baseFiskClientData = pageConfig.baseFiskClientData;
    let fiskClientData = {};
    let fiskDigestCts = fiskProfileWrap.fiskDigestWrap.sgContents;
    fiskClientData.challengeKey = fiskDigestCts.challengeKey;
    fiskClientData.challengeTime = fiskDigestCts.challengeTime;
    fiskClientData.capId = baseFiskClientData.capId;
    fiskClientData.ancPolicy = baseFiskClientData.ancPolicy;
    return remereLogin_api.buildServiceRequest("login", opArgs, fiskProfilePath, triggerView)
  }).then(phBuildResult => {
    authAppId = phBuildResult.authAppId;
    let requestData = phBuildResult.requestData;
    /** @type {SvReqContext} */
    let svReqContext = {};
    svReqContext.protocolId = "remere";
    svReqContext.opId = opId;
    svReqContext.callId = phBuildResult.callId;
    svReqContext.callDetails = phBuildResult.callDetails;
    svReqContext.fiskProfilePath = fiskProfilePath;
    buildContext = svReqContext;
    return remereLogin_api.sendRequestToService(svReqContext, requestData);
  }).then(SendResult => {
    if (opSendResult.statusOK) {
      ...
    }
    return remereLogin_api.evaluateServiceResponse(buildContext, opSendResult);
  }).then(evalResult => {
    return evalResult;
  });
}

Capis API - Function: CapisControl.getPageConfig()

Return Type PageConfig; Returns null if the PageConfig has not been created. (If the CapisControl has not been initialized.)

Gets a copy of the PageConfig settings for the current page. (i.e. The set of ConfigSettings, stored values) The Capis document, authAppId, FiskDigest settings.

The user may need to call ensurePageConfig() before calling this function. Will return null if the CapisControl has not been initialized.

Function info: (parameter list, return value, etc.)
  • @return {PageConfig} The CAPIS config values specified in the HTML page. Or null.

Capis API - Function: CapisControl.getFiskProfileInfo(protocolId, fiskProfilePath)

Return Type Object; Returns null if the FiskProfile cannot be found. Or if the CapisControl has not been initialized.

Gets the "FiskProfile Repository" for the specified protocol. This is the set of all FiskProfile definitions. (Note, this gets BOTH the FiskProfile objects from the HTML page, and from the FiskCatalog.) The FiskProfile describes the service requirements, etc...

The user may need to call ensurePageConfig() before calling this function. Will return null if the FiskProfile cannot be found. Or if the CapisControl has not been initialized.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @return {FiskProfile} A wrapper for the SocInfo and FiskProfile info. Or null.

Capis API - Function: CapisControl.getRegisteredFiskProfileInfo(protocolId, fiskDigestId)

Return Type Object; Returns null if the registered FiskProfile cannot be found. Or if the CapisControl has not been initialized.

Gets the "ProtocolConfig" for the specified fiskDigestId.

The user may need to call ensurePageConfig() before calling this function. Will return null if the registered FiskProfile cannot be found. Or if the CapisControl has not been initialized.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} fiskDigestId; The FiskDigest Id. Ex: "offer_1"
  • @return {FiskProfilePath} The ProtocolConfig item. Or null.

Capis API - Function: CapisControl.getResourceInfo(protocolId, resourceId, resourceArgs)

Return Type Object; Throws an Error if the resourceId is invalid. (Cannot return null on error, as that is a valid info value.)

Gets the details of the specified resource. Will find and return the InfoObject for the specified "resourceId". Will throw an Error if the resourceId is invalid. (Cannot return null on error, as that is a valid info value.)

The difference between getResourceInfo and invokeServiceOp is that getResourceInfo executes immediately. It does NOT return a Promise.
Function info: (parameter list, return value, etc.) * Gets a known value from the underlying APIs. * DOES NOT return a Promise.
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} resourceId; The id of the resource. Ex: "config"
  • @param {ResourceReqArgs} resourceArgs; The call options.
  • @return {PhInfoResult} The result value. Will throw an Error on invalid resourceId.

Capis API - Function: CapisControl.createFormSnapshot2(formElement)

Return Type Object;

Converts an HTML form into a JSON object. Retains most of the form fields, attributes, labels and options.

Function info: (parameter list, return value, etc.) * Creates a FormSnapshot object from the Form element. * DOES NOT return a Promise.
  • @param {DOMElement} formElement; The HTML form element to extract the data from.
  • @return {FormSnapshot} The form snapshot. A JSON object.

Capis API - Function: CapisControl.extractFiskTriggerFromHtmlElement(htmlElement)

Return Type Object;

Extracts the Attribute information from the HTML element. Reads the "config" attribute of the htmlElement, and converts it into a JSON object. The socAttr "jsonContent" property contains the data as a JSON object. The attribute is usually the "servicetriggerdata" attribute.

Function info: (parameter list, return value, etc.)
  • @param {Object} htmlElement; The HTML element to extract from.
  • @param {Object} otherOptions; Other options. (Usually undefined.)
  • @return {FiskTriggerWrap} A wrapper object containing the attribute information.
Example HTML. ServiceTrigger HTML Element:
<meta name="serviceguidedata" content='{ "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "https://RP_example.com/some/path/mainFiskCatalog.json" }'/> ... <a href="remere://example.com/remereServiceOp?op=registerUserLogin" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/registerUserLogin", "ulCreationCfg": {"userIdGenMethods": ["st1", "ct1"], "rawUserId": "qis:A:A:1:Gib9_cYs"}, "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'>Sign Up</a> ... <form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }'> <input type="text" name="username" value="" placeholder="Enter a username" /> </form>
See the example_mainFiskCatalog.json document.
Example JavaScript implementation:
// Example JavaScript using a "service trigger" let fiskTrigger = capisControl_api.extractFiskTriggerFromHtmlElement(htmlElement); // example output let fiskTrigger = { "jsonContent": { "protocolOp":"remere/registerUserLogin", "ulCreationCfg": {"userIdGenMethods": ["st1", "ct1"], "rawUserId": "qis:A:A:1:Gib9_cYs"}, "onSuccess":{"actions":[{"redirectUri": "/some/path/page.html"}]} }, "srcMetadata": { "srcHtmlElement": htmlElement // Reference to the original htmlElement } }; let fiskDigest = { "jsonContent": { "challengeTime": 1599876543210, "challengeKey": "8fK2BzE9MQ2phX6RLu1VzD4F0wgWJ3In8dKNeGo9YdK", "serviceCatalogUri": "./_capis/mainFiskCatalog.json" }, "srcMetadata": { "srcHtmlElement": fiskDigestHtmlElement // Reference to the original htmlElement } }; let fiskCatalog = { "jsonContent":
{ "id": "main_2", "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "endpointBlock": {"sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "appIdSpec": "example.com/remereApp/2022010100", "mediation": "required" }
] }
};
Example JavaScript implementation:
// example implementation
/**
 * Extracts a FiskTriggerWrap object from the htmlElement.
 * (1) Extracts a special HTML attribute, converts it into a JSON object. (jsonObject)
 * (2) If htmlElement is a form, then extracts the specified fields into the JSON.
 * (3) Converts the JSON object into a FiskTriggerWrap.trContents object.
 * @param {DOMElement} htmlElement; The HTML element to extract from.
 * @return {FiskTriggerWrap} The extracted FiskTrigger object.
 */
function CCI_extractFiskTriggerFromHtmlElement(htmlElement) {
  const funcNameV = "CCI.extractFiskTriggerFromHtmlElement ";
  if (htmlElement === undefined) {
    throw new Error(funcNameV + "htmlElement is undefined.");
  }
  const attributeName = han_serviceTrigger_dataAttr;
  let attrWrap = CCI_extractJsonAttributeWrapFromHtmlElement(htmlElement, attributeName);
  let jsonObject = attrWrap.jsonObject;
  let nodeName = htmlElement.nodeName;
  if (nodeName == "FORM") {
    let fieldData = CCI_extractFieldDataFromForm(htmlElement);
    // The form data has priority.
    // THe form data should OVERWRITE any existing jsonObject properties with the same name.
    fieldData.fieldList.forEach((field, index) => {
      jsonObject[field.name] = field.value;
    });
  }
  let fiskTriggerWrap = CCI_extractFiskTriggerFromContents(jsonObject, attrWrap.href, htmlElement);
  return fiskTriggerWrap;
}

Capis API - Function: CapisControl.validateServiceProfile(fiskProfile, listItrInfo)

Return Type FiskProfileValidationResult; fiskProfileValidationResult={isSuccess: true}

Validates the service profile. (The FiskProfile)

Function info: (parameter list, return value, etc.)
  • @param {FiskProfileInput} fiskProfile; The FiskProfile object to be validated.
  • @paramProperty {string} fiskProfile.srcHostname; The hostname part of the source URI.
  • @param {SpListItr} listItrInfo; The FiskProfile listIterator object.
  • _paramProperty {int} listItrInfo.loopIndex; The loop counter. (For use in error messages.)
  • @return {FiskProfileValidationResult} If the FiskProfile is valid.
  • @typedef {Object} valResult

Capis API - Function: CapisControl.validateServiceCatalog(fiskCatalog, listItrInfo)

Return Type FiskCatalogValidationResult; fiskCatalogValidationResult={isSuccess: true}

Validates the service catalog. (The FiskCatalog)

Function info: (parameter list, return value, etc.)
  • @param {FiskCatalogInput} fiskCatalog; The FiskProfile object to be validated.
  • @paramProperty {string} fiskCatalog.srcHostname; The hostname part of the source URI.
  • @param {SpListItr} listItrInfo; The FiskProfile listIterator object.
  • _paramProperty {int} listItrInfo.loopIndex; The loop counter. (For use in error messages.)
  • @return {FiskCatalogValidationResult} If the FiskProfile is valid.
  • @typedef {Object} valResult

Capis API - Function: CapisControl.addProtocolEventListener(protocolId, eventId, listener, listenerOptions)

Return Type Object; {failureReason: "alreadyExists"}

Adds a JavaScript event listener.

The listener callback function will be called with an eventObject as the first parameter. The eventObject will contain "eventId", "protocolId", "isBefore" (used when listenerOptions.capture=true), and other properties.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.
  • @typedef {Object} result
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: CapisControl.removeProtocolEventListener(protocolId, eventId, listener, listenerOptions)

Return Type Object; {removeCount: 1}

Details here.

Function info: (parameter list, return value, etc.)
  • @param {string} protocolId; The Service Protocol Id. Ex: "remere"
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.
  • @typedef {Object} result
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: CapisControl.sendRequestToService(svReqContext, fetchInfo)

Return Type Promise<SendResult>; The "send" operation can be canceled.

Sends the RML Request to the Remote Service. // For use by the ProtocolHandler objects.

Sends the requestData object to the RP endpoint. In whatever manner the RP designated in the FiskCatalog.

Function info: (parameter list, return value, etc.)
  • @param {SvReqContext} svReqContext; The service request context. buildContext from buildServiceRequest.
  • @param {Object} fetchInfo; The fetch info.
  • @paramProperty {string} fetchInfo.uri; The URI address to send the request to.
  • @paramProperty {string} fetchInfo.method; The HTTP method. "GET" or "POST".
  • @paramProperty {Array} fetchInfo.queryParamList; The list of data to be sent to the RP service.
  • Example: paramItem = {name:"xxx", type:"json_object", value:{prop1:"xxx", prop2:"yyy"} }
  • @paramProperty {string} fetchInfo.contentEncodingType; The contentEncodingType. "JSON".
  • @paramProperty {Object} fetchInfo.data; The data to be sent to the RP service. Contains the "proof of identity", etc.
  • @return {Promise<SendResult>} The response from the RP service.
  • @typedef {Object} send_response
  • @resultProperty {boolean} @result.statusOK; If the fetch was a success.
  • @resultProperty {Object} @result.jsonData; The data returned from the service.
  • @resultProperty {string} @result.url; The URL that was contacted by the request.
  • @resultProperty {Object} @result.fetchResponse; The wrapped fetch response object.
Example JavaScript implementation:
// example implementation
function CCI_sendRequestToService(svReqContext, fetchInfo) {
  let send_response = {};
  let opId = svReqContext.opId;
/*
  let fiskProfile = svReqContext.fiskProfile;
  let pContents = fiskProfile.pContents;
  let authAppId = fiskProfile.authAppId;
    let buildValues = CLD_getBuildValues(svReqContext.callId);
    let lipId = buildValues.lipId;
    let lomaId = buildValues.lomaId;
*/
  let requestData = fetchInfo.data;
  return Promise.resolve("yyy")
  .then(mmm_respose => {
    // itemRecordDatabase.recordAction(lomaId, lipId, actionOptions);
    let sUri = fetchInfo.uri;
    let dataParamList = fetchInfo.queryParamList;
    if (!(fetchInfo.method === undefined)) { fetchInfo.method = "POST";}
    let fetch_url;
    let fetch_init;
    let http_method = fetchInfo.method;
    if (http_method == "POST") {
      fetch_url = sUri;
      if (http_method == "POST") {} // Use fetchInfo.contentEncodingType.
      let body_data = {};
      dataParamList.forEach((item, index) => { body_data[item.name] = item.value; });
      fetch_init = {method:"POST", body: body_data};
      fetch_init.headers = { 'content-type': 'application/json' };
    } else if (http_method == "GET") {
      // let query_str = createQueryString(dataParamList);
      let query_str = "";
      dataParamList.forEach((item, index) => {
        if (index != 0) query_str += "&";
        query_str += encodeURIComponent(item.name) + "=" + encodeURIComponent(JSON.stringify(item.value));
      });
      fetch_url = sUri + "?" + query_str;
      fetch_init = {method:"GET"};
    } else {
      throw new Error("Cannot send request. Invalid protocol=" + http_method);
    }
    // Fetch API, not supported by older browsers.
    return fetch(fetch_url, fetch_init);
  }).then(fetch_response => {
    send_response.svRequestId = svReqContext.callId; // TODO this is the svRequestId.
    send_response.fetchResponse = fetch_response;
    send_response.url = fetch_response.url;
    send_response.statusCode = fetch_response.status;
    send_response.statusOK = false;
    if (!fetch_response.ok) {
      return null;
    }
    return fetch_response.json();
  }).then(json_response => {
    if (json_response != null) {
      send_response.jsonData = json_response;
      send_response.statusOK = true;
    }
    return send_response;
  }).catch(error => {
    console.log("Error in sendRequestToService. error=" + error.message);
    return Promise.reject(error);
  });
}

Capis API - Function: ProtocolHandler.getResourceInfo(resourceId, resourceArgs)

Return Type Object; Throws an Error if the resourceId is invalid. (Cannot return null on error, as that is a valid info value.)

Gets the details of the specified resource. Will find and return the InfoObject for the specified "resourceId". Will throw an Error if the resourceId is invalid. (Cannot return null on error, as that is a valid info value.)

The difference between getResourceInfo and performServiceOp is that getResourceInfo executes immediately. It does NOT return a Promise.
Function info: (parameter list, return value, etc.) * Gets a known value from the underlying APIs. * DOES NOT return a Promise.
  • @param {string} resourceId; The id of the resource. Ex: "config"
  • @param {ResourceReqArgs} resourceArgs; The call options.
  • @return {PhInfoResult} The result value. Will throw an Error on invalid resourceId.

Capis API - Function: ProtocolHandler.performServiceOp(opId, opArgs, fiskProfilePath, triggerView)

Return Type Promise<OpResult>; The "login" operation can be canceled.

Performs a service operation.

Function info: (parameter list, return value, etc.)
  • @param {string} opId; The method (function name) for the service to perform. (Ex: "login", "setActiveCredentials", etc.)
  • @param {ServiceOperationArgs} opArgs; The set of args to be sent to the service operation.
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @param {FiskTriggerView} triggerView; The trigger that started the event.
  • @return {Promise<OpResult>} The Protocol Handler response object.

Capis API - Function: ProtocolHandler.validateServiceProfile(fiskProfile, listItrInfo)

Return Type FiskProfileValidationResult; fiskProfileValidationResult={isSuccess: true}

Validates the service profile. (The FiskProfile)

Function info: (parameter list, return value, etc.)
  • @param {FiskProfileInput} fiskProfile; The FiskProfile object to be validated.
  • @paramProperty {string} fiskProfile.srcHostname; The hostname part of the source URI.
  • @param {SpListItr} listItrInfo; The FiskProfile listIterator object.
  • _paramProperty {int} listItrInfo.loopIndex; The loop counter. (For use in error messages.)
  • @return {FiskProfileValidationResult} The response object.
  • @typedef {Object} validation_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: ProtocolHandler.validateServiceCatalog(fiskCatalog, listItrInfo)

Return Type FiskCatalogValidationResult; fiskCatalogValidationResult={isSuccess: true}

Validates the service catalog. (The FiskCatalog)

Function info: (parameter list, return value, etc.)
  • @param {FiskCatalogInput} fiskCatalog; The FiskProfile object to be validated.
  • @paramProperty {string} fiskCatalog.srcHostname; The hostname part of the source URI.
  • @param {SpListItr} listItrInfo; The FiskProfile listIterator object.
  • _paramProperty {int} listItrInfo.loopIndex; The loop counter. (For use in error messages.)
  • @return {FiskCatalogValidationResult} If the FiskProfile is valid.
  • @typedef {Object} valResult

Capis API - Function: ProtocolHandler.addEventListener(eventId, listener, listenerOptions)

Return Type Object; {failureReason: "alreadyExists"}

Adds a JavaScript event listener.

The listener callback function will be called with an eventObject as the first parameter. The eventObject will contain "eventId", "isBefore" (used when listenerOptions.capture=true), and other properties.

Function info: (parameter list, return value, etc.)
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.
  • @typedef {Object} result
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: ProtocolHandler.removeEventListener(eventId, listener, listenerOptions)

Return Type Object; {removeCount: 1}

Details here.

Function info: (parameter list, return value, etc.)
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.
  • @typedef {Object} result
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: LoginManager.getPassbookList()

Return Type Promise<Object>;

Gets the list of all the Passbook in the LoginManager.

Function info: (parameter list, return value, etc.)
  • @return {Promise<Object>} The response object.
  • @typedef {Object} getPbList_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {PassbookRec[]} @result.passbookList; The list of PassbookItem objects.
  • @resultProperty {PassbookRec} @result.passbookList[]; A passbookItem.
  • @resultProperty {string} @result.id; The Passbook identifier. Ex: "p2"
  • @resultProperty {string} @result.name; The name of the Passbook. Ex: "John's Work"

Capis API - Function: LoginManager.createPassbook(pbCreationData)

Return Type Promise<Object>;

Creates a new Passbook with the specified properties. (i.e. The passbook name.)

Function info: (parameter list, return value, etc.)
  • @param {Object} pbCreationData; The passbook creation data.
  • @paramProperty {string} pbCreationData.name; The name to give to the passbook.
  • @paramProperty {int} pbCreationData.selectionGrade; The priority (weight) of the Passbook.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} createPb_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {string} @result.passbookId; The Passbook identifier. Ex: "p2"

Capis API - Function: LoginManager.getPassbookDetails(passbookId)

Return Type Promise<Object>;

Gets the info of the Passbook. Primarily, this gets the name of the Passbook.

Function info: (parameter list, return value, etc.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2"
  • @return {Promise<Object>} The response object. Or null.
  • @typedef {Object} getPbDetail_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {string} @result.passbookId; The Passbook identifier. Ex: "p2"

Capis API - Function: LoginManager.setPassbookDetails(passbookId, pbDetail)

Return Type Promise<Object>;

Sets the info of the Passbook. Primarily, this sets the name of the Passbook.

Function info: (parameter list, return value, etc.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2"
  • @param {Object} pbDetail; The Passbook detail values.
  • @paramProperty {string} pbDetail.name; The new passbook name.
  • @paramProperty {int} pbDetail.selectionGrade; The priority (weight) of the Passbook.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} setPbDetail_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {string} @result.passbookId; The Passbook identifier. Ex: "p2"

Capis API - Function: LoginManager.collectUserLogins(ulFilterOptions, fiskClientData)

Return Type Promise<Object>; Contains some JavaScript arrays (does NOT return a promise??)

Gets a list of matching User Logins, and their match grade. Also returns that list of all Passbooks.

Searches the Login Manager for User Logins that meet the RP requirements. (i.e. For User Logins that can be used at the RP.) It takes the appIdSpec and altRapSourceList as input, and restricts its results based on the capId.

One item in the list will be the "default". (pre-selected for the user) This is usually the most-recently used login for the website. The caller can use the result list of User Login in a dialog box that is displayed to the user, asking which User Login to use.

Function info: (parameter list, return value, etc.)
  • @param {UlFIlterOptions} ulFilterOptions; The User Login filter criteria. (Limit the returned list.)
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @return {Promise<CollectUlResult>} The response object. (Type CollectUlResult)
  • @typedef {Object} collectLogins_response
  • @resultProperty {PassbookRec[]} @result.passbookList; The list of all the User Passbooks.
  • @resultProperty {UserLoginQualCollection} @result.primaryRapUlResult; The information about the primary authAppId.
  • @resultProperty {UserLoginQualCollection[]} @result.altRapUlResultList; A list of the RapUlResult objects.
  • @resultProperty {string} @result.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @resultProperty {UserLoginRec[]} @result.q1UloList; The list of qualified UserLogins. (That survived filtering.)
  • @resultProperty {UserLoginRec[]} @result.q2UloList; The list of un-registered UserLogins. (That survived.)
Example API use.
// Example JavaScript using a FiskProfile
function doSomething(fiskProfile) {
  let altRapSourceList = [
        {"appId":"capis4.example.com/remereApp/0000000007", "colMode":"SSO", "appIdT":1604102400, "appId3":"2020-10-31"},
        {"appId":"capis4.example.com/remereApp/2022010100", "colMode":"SSO", "appIdT":1609459200, "appId3":"2022-01-01"}
  ];
  let loginConfig = fiskProfile["loginConfig"];
  if (loginConfig !== undefined) {
    altRapSourceList = loginConfig["altRapSourceList"];
  }
  let ulFilterOptions = { altRapSourceList: altRapSourceList, authAppId: "example.com/remereApp/2022010100" };
  ulFilterOptions.searchFiskProfile = fiskProfile;
  let lomaQResultItem = {};
  return LoginManager.collectUserLogins(ulFilterOptions, fiskClientData)
  .then(collectLogins_response => {
    lomaQResultItem.qualList = collectLogins_response.qualUserLoginList;
    lomaQResultItem.unqualList = collectLogins_response.unqualUserLoginList;
  });
}
function LMI_collectUserLogins(ulFilterOptions, fiskClientData) {
}

Capis API - Function: LoginManager.importUserLogin(passbookId, exportedLogin, importOptions)

Return Type Promise<Object>;

Imports a User Login into the browser. (How to import?)

Attempts to import a User Login into the Passbook. The UserLogin data was previously exported from a (different) LoginManager.

This function returns a Promise. It can fail. The User Login can be malformed, or the protocol can be unknown or unsupported.

// Adds a User Login to the browser. (How to import?) Callback asks for the private key?
Function info: (parameter list, return value, etc.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2" (Where to store the import.)
  • @param {UlImportData} exportedLogin; The exported User Login to import.
  • @param {UlImportOptions} importOptions; The User Login import options.
  • @return {Promise<UlImportResult>} The response object.

Capis API - Function: LoginManager.createUserLogin(passbookId, authAppId, ulCreationData, fiskClientData)

Return Type Promise<Object>;

Creates a new User Login that is associated with the authAppId. Adds it to the specified Passbook. Creates all the requested credentials for the User Login.

Takes the appIdSpec as the authAppId argument. Returns an object that contains the lipId of the new User Login. On error, returns an error object.

Note that only certain capId are allowed to create User Logins that user certain authAppId. See appIdSpec.

Function info: (parameter list, return value, etc.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2" (Where to store the new User Login.)
  • @param {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @param {Object} ulCreationData; Object containing other creation options.
  • @paramProperty {string} ulCreationData.disambigName; The disambiguation name. (If needed)
  • @paramProperty {string} ulCreationData.initUserId; The initial User Id. Requested by the client.
    Warning: This only sets the *original* userId. (Before the request to the RP.) (Example: If using userid_creation.claim_ms_ulc.)
  • @paramProperty {CredCreation[]} ulCreationData.credSourceCreationList; A list of "Credential Source creation" objects.
    Example: credCreationInfo_item = { "cci":"834", "type":"public-key", "coseAlgId": -7}
  • @paramProperty {string[]} ulCreationData.capAccessList; The list of capId items (Foreign?) allowed to use the UserLogin.
    Example: capAccessList: [{"capId":"example.com"}, {"capId":"arc.example.com"}]
  • @paramProperty {string} ulCreationData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} ulCreationData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @paramProperty {RpEntityInfo} ulCreationData.rpEntityInfo; The RP entity information. (For credential creation.)
  • @paramProperty {Object} ulCreationData.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.challengeKey; The RP challenge value. Base64Url encoded.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} createUserLogin_response
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id. Ex: "u34"
  • @resultProperty {int} @result.creationDate; The date the UserLogin was created. (Unix currentTS)
  • @resultProperty {Array} @result.createdCredentialList; The list of credentials that were created.
    Example: createdCredential_item = { id: "cd2", cci: "834", type: "public-key" }
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
Internal steps:
  • Call generateKeyPair()

Capis API - Function: LoginManager.getUserLoginDetails(lipId, fiskClientData)

Return Type Promise<Object>; The User Login object.

Gets the specified User Login and the contained credential list.

Returns null if the lipId is invalid. If there is no User Login for that lipId. (Or if the access restrictions are wrong.)

This does not return the actual credentials. It only returns a list of the credential information. Their ID, type and public keyHandle (if applicable).

Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {FiskClientData} fiskClientData; The collected client data from the HTML page. (capId, ulRapAccess}
  • @return {Promise<Object>} The details object. Or null. If the User Login is not found.
  • @typedef {Object} ulDetails
  • @resultProperty {boolean} @result.isRpRegistered; Is true if the User Login has been registered with the RP.
  • @resultProperty {string} @result.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @resultProperty {string} @result.passbookId; The Passbook identifier. Ex: "p2".
  • @resultProperty {string} @result.enc_user_id; The encrypted UserId. Example: "qis:A:H:1:K8h3_TfXr6ZtLq7d1m8k".
  • @resultProperty {string} @result.disambigName; The name to display. Only used if multiple User Logins for the same RP.
  • @resultProperty {int} @result.creationDate; The date the User Login was created. (Unix timestamp)
  • @resultProperty {int} @result.modifiedDate; The date the User Login was modified. (Unix timestamp)
  • @resultProperty {int} @result.lastLoginDate; The date the User Login was last used. (Unix timestamp)
  • @resultProperty {int} @result.rpRegDate; The date the User Login was registered at RP. (Unix timestamp)
  • (There are only 3 kinds of modification. Change enc_user_id, disambigName, or credential list.)
  • @resultProperty {Array} @result.activeCredentialIdList; The array of active credential Ids.
  • @object_example activeCredentialIdList = [ "cd2", "cd15", "cd3" ] Composed of only credential ids.
  • @resultProperty {Array} @result.activeCredentialDetailList; The array of active credentials.
  • @object_example activeDetailItem = {id:"cd2", type: "public-key"}
  • @resultProperty {Array} @result.stagedCredentialIdList; The array of staged credentials.
  • @object_example stagedCredentialIdList = [ "cd2", "cd15", "cd3" ] Composed of only credential ids.
  • @resultProperty {Array} @result.completeCredentialList; All the existing credentials for the User Login.
  • @object_example completeList_item = {id:"cd2", type: "public-key", coseAlgId: -7}

Capis API - Function: LoginManager.setUserLoginDetails(lipId, ulDetail, fiskClientData)

Return Type Promise<Object>; Object containing boolean "isSuccess" property.

Updates the User Login with the new info. Can change only 6 things, the "assigned" UserId, the display_name, the passbookId, the "staging" list of credentials, the "confirmed" list of credentials and the "addTagList".

The ulDetail can contain the following properties. "updatedUserId", "disambigName", "passbookId", "addTagList" and "removeTagList".

Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {Object} ulDetail; The new UserLogin detail values.
  • @paramProperty {string} ulDetail.disambigName; The new display name, set by the user. (Only used if a Passbook contains multiple User Logins for the same RP.)
  • @paramProperty {string} ulDetail.passbookId; The Passbook identifier. Ex: "p2".
  • @paramProperty {Array} ulDetail.addTagList; The list of "tags" to add to the User Login.
  • @paramProperty {Array} ulDetail.removeTagList; The list of "tags" to remove from the User Login.
  • @paramProperty {string} ulDetail.updatedUserId; The new userId to be changed to.
  • @param {FiskClientData} fiskClientData; The collected client data from the HTML page. (capId, ulRapAccess}
  • @return {Promise<Object>} The response object.
  • @typedef {Object} setDetails_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id. Ex: "u34"

Capis API - Function: LoginManager.updateUserLoginWithServiceResult(lipId, svUlUpdate, capId)

Return Type Promise<Object>; Object containing boolean "isSuccess" property.

Updates the User Login with the new info. Can change only 6 things, the "assigned" UserId, the display_name, the passbookId, the "staging" list of credentials, the "confirmed" list of credentials and the "addTagList".

The svUlUpdate can contain the following properties. "updatedUserId", "confOpId", "credConfirmList".

Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {Object} svUlUpdate; The UserLogin update. From the RP service. After the requested operation.
  • @paramProperty {string} svUlUpdate.updatedUserId; The new user id. May be encrypted. Usually given by the RP. (in assignedUserId.)
  • @paramProperty {Array} svUlUpdate.credConfirmList; The credential confirm results from the RP.
    Example: credentialConfirmItem = {id: "cd2", error:"invalid"}
  • @paramProperty {string} svUlUpdate.confOpId; The Confirmation Operation Id. Ex: confOpId = "registerUserLogin".
    Must be one of "registerUserLogin", "setActiveCred", "login", "importUserLogin". (Result after RP registration or login.)
  • @param {string} capId; The Client Application Id. Ex: "login.example.com" (limits access to only some RP)
  • @return {Promise<Object>} The ulUpdate response object.
  • @typedef {Object} ulUpdate_response
  • The response object can contain an "error" property instead of result data.
  • @resultProperty {Object} @result.error; The error object.
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id. Ex: "u34"

Capis API - Function: LoginManager.createUserLoginCredentials(lipId, credCreationInfo, fiskClientData, rpEntityInfo)

Return Type Promise<Object>;
Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {CredCreationInput[]} credCreationList; Credential creation options.
  • @param {FiskClientData} credCreationInfo.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @param {RpEntityInfo} credCreationInfo.rpEntityInfo; The RP entity information. (For credential creation.)
  • @paramProperty {CredCreationInput[]} credCreationInfo.credCreationList; A list of "Credential Source creation" objects.
    @object_example credSrcCreationItem = { "cci":"834", "type":"public-key", "coseAlgId": -7}
  • @paramProperty {string} fiskClientData.challengeKey; The RP nonce value. base64url encoded.
    The challengeKey may be needed for the newly created credentials to have proper attestation data. The challengeKey is optional. It is only needed for WebAuthn, where it proves "freshness" of the attestation data.
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @return {Promise<Object>} The response object.
  • @typedef {Object} createCred_response
  • @resultProperty {boolean} @result.isSuccess; If the creation succeeded.

Capis API - Function: LoginManager.setActiveCredentialsPrep(lipId, credActivationInfo, fiskClientData)

Return Type Promise<Object>;

Sets the credentials that will make up the "set of active credentials". This is the first step in setting the "active" credentials at the RP. The active credentials will be set when the RP confirms the "set of active credentials".

After this function is called, the RP must be sent a "setActiveCredentials" RML Request. If the RP responds with a success, then the browser will update the "active" credentials in the Login Manager. (The RemereDirector.evaluateServiceResponse() function calls LoginManager.setUserLoginDetails(lipId, ulDetail, capId).)

The input must specify which credentials to use. This is actually two lists, (1) a list of existing credential IDs and (2) a list of new credentials to be created. For each specified new-credential-info, the Login Manager will create a new credential source for use with the specified User Login, and add the credential ID to the "createdCredentialList" that is returned. (The credential sources are kept secret internally to the User Login. The credential ID and type are returned.)

Returns an object containing two lists. (1) a list of the "created" credential info objects. This list items contain the credential id, the type, and other public settings. (i.e. the algorithm) (2) a list of the "selected" credentials. This contains the credential IDs for credentials that were not created, and previously existed.

Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {Object} credActivationInfo; Credential activation and creation options.
  • @paramProperty {FiskClientData} credActivationInfo.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {RpEntityInfo} credActivationInfo.rpEntityInfo; The RP entity information. (For credential creation.)
  • @paramProperty {Array} credActivationInfo.credSourceCreationList; A list of "Credential Source creation" objects.
    @object_example credCreationInfo_item = { "cci":"834", "type":"public-key", "coseAlgId": -7}
  • @paramProperty {Array} credActivationInfo.existingCredentialIdList; A list of which existing credentials to use.
  • @object_example existingCredentialIdList = [ "cd2", "cd15", "cd3" ] Composed of only credential ids.
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @return {Promise<Object>} The response object.
  • @typedef {Object} stageCred_response
  • @resultProperty {Array} @result.createdCredentialList; The list of credentials that were created. (id, type)
  • @object_example createdCredItem = { id: "cd14", cci: "834", type: "public-key" }
  • @resultProperty {Array} @result.selectedCredentialIdList; The list of staged credentials that already existed.
  • @object_example selectedCredentialIdList = [ "cd2", "cd15", "cd3" ] Composed of only credential ids.

Will create the requested new credentials listed in the "credSourceCreationList".

For new public key attestation, the RP needs a single bit of information about any registration: was this public-key generated in a certified device or not?

Capis API - Function: LoginManager.createUserIdentityProof(lipId, opId, fiskClientData, buildOptions)

Return Type Promise<Object>; The "createProof" operation can be canceled.

Creates a Proof of Identity for the specified User Login. (i.e. For the lipId.) More specifically, it creates a JSON Web Signature object. It is for use (as the "JWS" property) in a RML Request.

Creates the contents of a User Authentication service request, for delivery to an RP. Created to meet the RP's requirements. It will contain a proof-of-identity. The success callback receives a "Proof of Identity" container object. Or null.

This function uses the provided "authAppId" as the "aud" (audience) field in the proof-of-identity.

Returns a promise. The returned object contains either (1) the "proof-of-identity" object. (The "JWS" property.) Or (2) The UserId Assertion and the values for the UserId certificate.

Function info: (parameter list, return value, etc.)
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34"
  • @param {string} opId; The method (function name) for the service to perform. (Ex: "login", "setActiveCredentials", etc.)
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.challengeKey; The RP challenge value. Base64Url encoded.
  • @param {Object} buildOptions; Other options.
  • @paramProperty {string} buildOptions.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} buildOptions.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • (capId used for the "audience".)
  • @paramProperty {string} buildOptions.fiskProfileId; The FiskProfile Id. Ex: "remere_1"
  • @paramProperty {string} buildOptions.fiskDigestId; The FiskDigest Id. Ex: "offer_1"
  • @paramProperty {string} buildOptions.reqUserId; The client requested UserId string.
    Used when opId="registerUserLogin" or "setUserId". (Example: If using userid_creation.claim_ms_ulc.)
  • @paramProperty {Object} buildOptions.sessionNotes; Anything special the RP requires from the session.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} ulProof_response
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id. Ex: "u34"
  • @resultProperty {string} @result.sessionUserId; The temporary userId to use in the session.
  • @resultProperty {Object} @result.JWS; The JWS object. (JSON Web Signature)
  • @resultProperty {string} @result.JWS.payload; The payload. A Base64Url encoded string.
  • @resultProperty {Array} @result.JWS.signatures; The signatures array.
Internal steps:
  • Determine if the JWS will need a credential_registration section. (This is the case for opId="registerUserLogin" and "setActiveCredentials".)
  • If credential_registration is required, then call createNonPublicKeyProofItems.
  • If credential_registration is required, then determine if backup keys are needed.
  • Create the payload. Include the fields that the RP requires.
  • Look up the private key(s) for the User Login.
  • Sign the proof-of-identity with the private keys.

Additional option. As part of the process, it may be desirable to create a new key-pair and send the public key to the RP as part of the proof. This key may be used to secure session communication between the browser and the RP. If this is done, the private key must be stored internally to the browser. Example: {lipId:"u34", RP_domainName:"123done.org", private_key:"xxxx"}

Example Javascript use:
// Example JavaScript using "createUserIdentityProof" lipId = "u34" resolvedOptions buildOptions = {opId:"login", profileId:"remere_1", for_future_use:"something..."} // return value: a Promise // Promise resolves to: proof_assertion = {
uid_cert_chain:[{uic.format:"JWS", uic.csvm:"OIDCD", "ucv": { "payload":"82h7vb...", "signatures": [...] } }]
}
Example JavaScript implementation:
// example implementation
function LMI_createUserIdentityProof(lipId, opId, fiskClientData, buildOptions) {
  let authAppId = buildOptions.authAppId;
  let opArgs = buildOptions.opArgs;
  // let protocolOp = buildOptions.protocolOp; // Example: "remere/login"
  let opId_2 = buildOptions.opId; // Example: "login"
  let fiskProfileId = buildOptions.profileId; // Example: "remere_1"
  let sigInput = buildOptions.sigInput;
  let active_values = credentialStore_getActiveValues(); // created previously from the FiskProfile and pageConfig.
  let service_info = active_values.service_info;
  let proof = {};
  let payload = {};
  let signatures = [];
  proof.payload = payload;
  proof.signatures = signatures;
  payload.fiskProfileId = buildOptions.fiskProfileId; // "remere_1";
  payload.opIdReq = opId;
  // payload.nonce = fiskClientData.challengeKey
  payload.challengeKey = fiskClientData.challengeKey
  payload.cht = fiskClientData.challengeTime;
  // payload.redirectUri = opArgs.redirectUri;
  payload.iat = new Date().getTime();
  let ulCreationCfg = opArgs.ulCreationCfg;
  // payload.uid.clientReqUserId = ulCreationCfg.userIdConfig;
  // payload.uic.format = ?;
  // payload.uid_cert_chain = [];
  return Promise.resolve("yyy")
  .then(yyy_response => {
    let sigInput2 = payload
    return bLoginManager.createNonPublicKeyProofItems(lipId, fiskClientData, sigInput2, buildOptions);
  }).then(assert_response => {
    let proof_list = assert_response.assertion.cpl;
    // proof.signatures = proof_list;
    proof_list.forEach(function (item, index, array) {
      let m2 = { header: { kid: item.cid_p}, protected: {typ_p: item.typ_p, fmt_p: item.fmt_p}, signature: item.val_p};
      signatures.push(m2);
    });
    credentialStore_storeUserLoginHistory(lipId);
    return proof;
  });
}

Capis API - Function: RemereLogin.performServiceOp(opId, opArgs, fiskProfilePath, triggerView)

Return Type Promise<OpResult>; The "login" operation can be canceled.

Performs a service operation.

Function info: (parameter list, return value, etc.)
  • @param {string} opId; The method (function name) for the service to perform. (Ex: "login", "setActiveCredentials", etc.)
  • @param {ServiceOperationArgs} opArgs; The set of args to be sent to the service operation.
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @param {FiskTriggerView} triggerView; The trigger that started the event.
  • @return {Promise<OpResult>} The Protocol Handler response object.
Example JavaScript implementation:
// example implementation
function RML_performServiceOp(opId, opArgs, fiskProfilePath, triggerView) {
  let funcNameV = "RML.performServiceOp ";
  // Return an "serviceOpResult". This may contain (probably contains) a opSendResult.
  if (fiskProfilePath === undefined) {
    throw new Error("Missing fiskProfilePath argument.");
  }
  let fiskProfileId = fiskProfilePath.fiskProfileId;
  let serviceOpResult = {};
  serviceOpResult.protocolId = "remere";
  serviceOpResult.opId = opId;
  serviceOpResult.opArgs = opArgs;
  serviceOpResult.fiskProfilePath = fiskProfilePath;
  let buildContext;
  return this.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView)
  .then(phBuildResult => {
    buildContext = phBuildResult;
    /** @type {SvReqContext} */
    let svReqContext = {};
    svReqContext.protocolId = "remere";
    svReqContext.opId = opId;
    svReqContext.callId = phBuildResult.callId;
    svReqContext.callDetails = phBuildResult.callDetails;
    svReqContext.fiskProfilePath = fiskProfilePath;
    serviceOpResult.svReqContext = svReqContext;
    return this.sendRequestToService(svReqContext, phBuildResult.requestData);
  }).then(SendResult => {
    serviceOpResult.opSendResult = opSendResult;
    CLW_fireEvent("sendRequest", opId, false, opArgs);
    if (!opSendResult.statusOK) {
      // Error connecting to the service.
      let url = opSendResult.url;
      let statusCode = opSendResult.fetchResponse.status; // For debug
      let statusText = opSendResult.fetchResponse.statusText;
      throw new Error("Bad response from service. statusCode=" + statusCode + " statusText=" + statusText + " url=" + url);
    } else {
      return RML_evaluateServiceResponse(buildContext, opSendResult);
    }
  }).then(evalResult => {
    serviceOpResult.evalResult = evalResult;
    CLW_fireEvent("evalResult", opId, false, opArgs);
    let eval_success = true; // evalResult.statusOK
    if (eval_success) {
      // Notify the user that the opId succeeded.
      // Can refresh the page or navigate to a different page.
      // location.reload(); or location.href = opArgs.redirectUri;
    }
    return serviceOpResult;
  }).catch(error => {
    let funcNameP = "RML.performServiceOp";
    let funcArgSt = "opId=" + opId + " fiskDigestId=" + fiskProfilePath.fiskDigestId;
    CapisError_pushStackFrame(error, funcNameP, funcArgSt);
    return Promise.reject(error);
  });
}

Capis API - Function: RemereLogin.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView)

Return Type Promise<PhBuildResult>; The "login" operation can be canceled.

This function is a small wrapper for RemereDirector.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView). It exists to allow browser extensions a place to implement additional functionality before calling the "unmodifiable" RemereDirector functions.

Installed extensions can redefine this function. Since this function returns a promise, the new functionality can even include an online API call. For example, it could query an online storage system that stores identities for the user. And the online API call could be done only if the original function fails.

Function info: (parameter list, return value, etc.)

!!!!This function can show popup dialogs or redirect the user.!!!!

Internal steps:
  • Call CapisControl.ensurePageConfig() to get the "FiskDigest repository".
  • Create a local fiskClientData object.
  • Call RemereDirector.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView, fiskClientData, fiskClientData)
Example JavaScript implementation:
// example implementation
function RML_buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView) {
  let authAppId;
  let capId = window.location.hostname;
  let ancPolicy = "allowSOWA"; // isSameOriginWithAncestors
  // let fiskClientData = {capId: capId, pathname: window.location.pathname};
  return capisControl_api.ensurePageConfig()
  .then(pageConfig => {
    let fiskDigestList = pageConfig.fiskDigestList;
    let fiskProfileWrap = RML_getFiskProfileInfo("remere", fiskProfilePath);
    let fiskProfileCts = fiskProfileWrap.pContents;
    let authAppId_2 = fiskProfileCts.appIdSpec;
    let baseFiskClientData = pageConfig.baseFiskClientData;
    let fiskClientData = {};
    let fiskDigestCts = fiskProfileWrap.fiskDigestWrap.sgContents;
    fiskClientData.challengeKey = fiskDigestCts.challengeKey;
    fiskClientData.challengeTime = fiskDigestCts.challengeTime;
    fiskClientData.capId = baseFiskClientData.capId;
    fiskClientData.ancPolicy = baseFiskClientData.ancPolicy;
    let fiskClientData = {};
    fiskClientData.capId = baseFiskClientData.capId;
    fiskClientData.ancPolicy = baseFiskClientData.ancPolicy;
    fiskClientData.wrapFiskProfile = fiskProfileWrap;
    fiskClientData.pageConfig = pageConfig;
    // fiskClientData.fiskClientData = fiskClientData;
    return pRemereDirector_api.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView, fiskClientData, fiskClientData);
  }).then(phBuildResult => {
    authAppId = phBuildResult.authAppId;
    if (RML_activeValues.activeRapId === undefined) {
      RML_activeValues.activeRapId = authAppId;
    }
    return phBuildResult;
  });
}

Capis API - Function: RemereLogin.sendRequestToService(svReqContext, requestData)

Return Type Promise<SendResult>; The "send" operation can be canceled.

Sends the requestData object to the RP endpoint. In whatever manner the RP designated in the FiskCatalog.

Function info: (parameter list, return value, etc.)
  • @param {SvReqContext} svReqContext; The service request context. (The phBuildResult from buildServiceRequest.)
  • @param {Object} requestData; The data to be sent to the RP service. Contains the "proof of identity", etc.
  • @return {Promise<SendResult>} The response from the RP service.
  • @typedef {Object} send_response
  • @resultProperty {boolean} @result.statusOK; If the fetch was a success.
  • @resultProperty {Object} @result.jsonData; The data returned from the service.
  • @resultProperty {string} @result.url; The URL that was contacted by the request.
  • @resultProperty {Object} @result.fetchResponse; The wrapped fetch response object.
Example Javascript use:
// Example JavaScript using "sendRequestToService" let opArgs = { "protocolId: "remere", "opIdReq": "login",
uid_cert_chain:[{ uic.format:"JWS", uic.csvm:"OIDCD", "ucv": { "payload":"82h7vb...", "signatures": [...] } }],
redirectUri:"http://example.com/some/path/page.html" }; function doSomething(arg1) { let authAppId; let buildContext; // Could call remereLogin_api.performServiceOp("login", arg1, fiskProfilePath); return remereLogin_api.buildServiceRequest("login", opArgs, fiskProfilePath, triggerView) .then(phBuildResult => { authAppId = phBuildResult.authAppId; let requestData = phBuildResult.requestData; /** @type {SvReqContext} */ let svReqContext = {}; svReqContext.protocolId = "remere"; svReqContext.opId = opId; svReqContext.callId = phBuildResult.callId; svReqContext.callDetails = phBuildResult.callDetails; svReqContext.fiskProfilePath = fiskProfilePath; buildContext = svReqContext; return remereLogin_api.sendRequestToService(svReqContext, requestData); }).then(SendResult => { if (opSendResult.statusOK) { ... } return remereLogin_api.evaluateServiceResponse(buildContext, opSendResult); }).then(evalResult => { return evalResult; }); }
Example JavaScript implementation:
// example implementation
function RML_sendRequestToService(svReqContext, requestData) {
  let opId = svReqContext.opId;
  let profileId = "remere_1";
  return pRemereDirector_api.sendRequestToService(svReqContext, requestData)
  .then(SendResult => {
    CLW_fireEvent("sendRequest", opId, false, opSendResult);
    let jsonData = opSendResult.jsonData;
    let statusOK = opSendResult.statusOK;
    return opSendResult;
  }).catch(error => {
    console.log("Error in sendRequestToService. error=" + error.message);
    return Promise.reject(error);
  });
}

Capis API - Function: RemereLogin.evaluateServiceResponse(svReqContext, opSendResult)

Return Type Promise<PhEvalResult>;

Update the Remere Login Status at the RP, add the result to history.

Function info: (parameter list, return value, etc.)
  • @param {SvReqContext} svReqContext; The service request context. Used to create the request to the RP service.
  • @param {SendResult} opSendResult; The response from the RP service.
  • @paramProperty {boolean} opSendResult.statusOK; If the fetch was a success.
  • @paramProperty {Object} opSendResult.jsonData; The data returned from the service.
  • @return {Promise<PhEvalResult>} The response from the evaluation.
Example JavaScript implementation:
// example implementation
function RML_evaluateServiceResponse(svReqContext, opSendResult) {
  return pRemereDirector_api.evaluateServiceResponse(svReqContext, opSendResult)
  .then(evalResult => {
    CL_fireEvent(...);
    return evalResult;
  });
}

Capis API - Function: RemereLogin.getLoginStatus()

Return Type Object;

Gets the current Remere Login Status at the RP, the FiskProfile, etc. (Which service was used to login.) This function does NOT return the lipId. It does not return the raw UserId.

Function info: (parameter list, return value, etc.)
  • @internal {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @return {Object} The User Login status info. Or null.
  • @typedef {Object} ulStatus
  • @resultProperty {string} @result.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id is NOT available. Ex: "u34"
  • @resultProperty {string} @result.lomaId; The LoginManager identifier is NOT available. Ex: "m1"
  • @resultProperty {string} @result.userId; The userId is NOT available. Example: "f234"
  • @resultProperty {string} @result.sessionUserId; The temporary UserId that is logged in.
  • @resultProperty {int} @result.statusCode; The state of the login. Ex: 2 = logged_out
  • @resultProperty {boolean} @result.hasError; If there is an error.
  • @resultProperty {string} @result.errorCode; If there is an error. 1=unknown authAppId. 2=missingRecord.
  • @resultProperty {boolean} @result.noRecord; There is no record for the given authAppId. (uninitialized).
  • @object_example loginStatus = {authAppId:authAppId, sessionUserId:"none", statusCode:2} (2="logged_out")

The login status is a number. 1 = logged_in, 2 = logged_out.

This function does NOT return the lipId or the raw UserId, because that information is secret / protected. The lipId MUST NOT be known to the RP. Because the RP can use it to identify the Authenticator. (The lipId for the RP is unique to the Authenticator.) For added security, the raw UserId SHOULD not be known to the browser User Agent, as a modified user agent can record the UserId. (And not delete it after use.) As a potential security precaution, a sessionUserId is created and used in the proof of identity, so that the raw UserId is not revealed to the User Agent. If the sessionUserId were not used, then a browser would be given the real UserId that is used to login to the RP. An insecure or modified browser could record the UserId and/or transmit it to a third party, etc. Once a UserId is known, various means can be attempted to compromise the password / private key.

Example JavaScript implementation:
// example implementation
function RML_getLoginStatus() {
  let authAppId = RML_getStoredRapId();
  if (authAppId == null) {
    authAppId = "error.example.com/error";
    return {hasError: true, errorMsg: "Not initialized. The current authAppId is unknown.", isLoggedIn: undefined};
  }
  let loginStatusInfo = pRemereDirector_api.getLoginStatus(authAppId);
  CL_fireEvent(...);
  return loginStatusInfo;
}

Capis API - Function: RemereLogin.logoutAll()

Return Type Promise<Object>.

Sends a logout request to all logged-in "auth" websites. This function is callable by users via the toolbar, etc. (calls logout on all logged-in websites)

Example Javascript use:
// Example JavaScript using "logoutAll"
remereLogin_api.logoutAll(); // Note the function does not take opArgs as an argument.

Capis API - Function: RemereLogin.validateServiceProfile(fiskProfile, listItrInfo)

Return Type FiskProfileValidationResult; fiskProfileValidationResult={isSuccess: true}

Validates the service profile. (The FiskProfile)

Function info: (parameter list, return value, etc.)
  • @param {FiskProfileInput} fiskProfile; The FiskProfile object to be validated.
  • @paramProperty {string} fiskProfile.srcHostname; The hostname part of the source URI.
  • @param {SpListItr} listItrInfo; The FiskProfile listIterator object.
  • _paramProperty {int} listItrInfo.loopIndex; The loop counter. (For use in error messages.)
  • @return {FiskProfileValidationResult} If the FiskProfile is valid.

Capis API - Function: RemereLogin.addEventListener(eventId, listener, listenerOptions)

Return Type Object; {failureReason: "alreadyExists"}

Adds a JavaScript event listener.

The listener callback function will be called with an eventObject as the first parameter. The eventObject will contain "eventId", "protocolId", "isBefore" (used when listenerOptions.capture=true), and other properties.

Function info: (parameter list, return value, etc.)
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.

Capis API - Function: RemereLogin.removeEventListener(eventId, listener, listenerOptions)

Return Type Object; {removeCount: 1}

Details here.

Function info: (parameter list, return value, etc.)
  • @param {string} eventId; The event to listen for. Ex: "loginStatusChange" or "all"
  • @param {Object/function} listener; The listener object to receive notification. (Contains function to be executed.)
  • @param {Object} listenerOptions; Additional options when to fire the event.
  • @paramProperty {string} listenerOptions.stageName; The stage name. Ex: "build", "send", "eval". (i.e. Before or after the event is sent to the RP.)
  • @paramProperty {boolean} listenerOptions.capture; To capture the event BEFORE it is executed.
  • @return {Object} The result object.

Capis API - Function: RemereLogin.importUserLogin(lomaId, passbookId, exportedLogin, importOptions, fiskClientData)

Return Type Promise<Object>;

Imports a User Login into the browser. (How to import?)

Attempts to add a User Login to the internal Passbook.

This function returns a Promise. It can fail. The User Login can be malformed, or the protocol can be unknown or unsupported.

Note, installed extensions can redirect this function to add identities to an online storage system.

Function info: (parameter list, return value, etc.)
  • @param {string} lomaId; The LoginManager identifier. Ex: "m1" (Where to store the import.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2" (Where to store the import.)
  • @param {UlImportData} exportedLogin; The exported User Login to import.
  • @param {UlImportOptions} importOptions; The User Login import options.
  • @return {Promise<UlImportResult>} The response object.
Example JavaScript implementation:
// example implementation
function RML_importUserLogin(lomaId, passbookId, exportedLogin, importOptions, fiskClientData) {
  let authAppId;
  return capisControl_api.ensurePageConfig()
  .then(pageConfig => {
    // let pageConfig = capisControl_api.getPageConfig();
    let fiskDigestId = "offer_1"; // TODO
    let fiskProfileId = "remere_1";
    let fiskProfilePath = {fiskDigestId:fiskDigestId, fiskProfileId:fiskProfileId};
    let fiskProfileWrap = CCI_getFiskProfileInfo("remere", fiskProfilePath);
    let fiskProfileCts = fiskProfileWrap.pContents;
    authAppId = fiskProfileCts.appIdSpec;
    return pRemereDirector_api.importUserLogin(lomaId, passbookId, exportedLogin, importOptions, fiskClientData);
  }).then(import_response => {
    CL_fireEvent(...);
    return import_response;
  });
}

Capis API - Function: RemereLogin.setBrowserUser(browserUser)

Return Type Promise<Object>;

Change the currently active user in the browser. This will perform a "logoutAll" to close all active login sessions. Then it will change the current User profile used to login to RPs. It does NOT call RemereLogin.connectLoginManagers.

This does NOT authenticate the user or connect to any LoginManagers. During authentication, the specified user's authentication prompt is displayed.

Capis API - Function: RemereLogin.connectLoginManagers(browserUser)

Return Type Promise<Object>;

Display the browser's popup dialog, asking the user to connect to any LoginManagers. The argument "browserUser" is optional. If it is not supplied, then the default_browser_user is used.

Function info: (parameter list, return value, etc.)
  • @param {string} browserUser; The current user that is logged into the browser.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} connectLoma_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: RemereLogin.disconnectLoginManagers(lomaIdList)

Return Type Promise<Object>;
Function info: (parameter list, return value, etc.)
  • @param {string[]} lomaIdList; The list of LoginManager IDs to disconnect.
    The lomaIdList can also be the special value "all".
  • @return {Promise<Object>} The response object.
  • @typedef {Object} disconnectLoma_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: RemereLogin.acquireNewUserIdCertificate(UserId)

Return Type Promise<Object>;

Get a new User Id Certificate, from the Identity Provider. This will probably require the user to re-prove themselves to the IdP. (re-enter their password, etc.)

Capis API - Function: RemereLogin.getUserIdCertificate(UserId)

Return Type Promise<Object>;

Get the cached User Id Certificate from the browser.

Capis API - Function: RemereDirector.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView, clientBadgeWrap, fiskClientData)

Return Type Promise<PhBuildResult>; The "login" operation can be canceled.

This function is a small wrapper for LoginManager.createUserIdentityProof(). It exists to allow browser extensions a place to implement additional functionality before calling the "unmodifiable" LoginManager functions.

Function info: (parameter list, return value, etc.)
  • @param {string} opId; The method (function name) for the service to perform. (Ex: "login", "setActiveCredentials", etc.)
  • @param {ServiceOperationArgs} opArgs; The set of args to be sent to the service operation.
  • @paramProperty {Object} opArgs.criteriaChangeSet; The changes to make to the UIC. Only used if opId="registerUserLogin" or "setActiveCredentials"
  • @param {FiskProfilePath} fiskProfilePath; The 'path' parts that identify the FiskProfile.
  • @paramProperty {string} fiskDigestId; The FiskDigest Id. Ex: "offer_1"
  • @param {FiskTriggerView} triggerView; The trigger that started the event.
  • @param {ClientBadgeWrap} clientBadgeWrap; The client information.
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @paramProperty {string} fiskClientData.appIdSpec; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @paramProperty {FiskClientData} fiskClientData.wrapFiskProfile; The selected FiskProfile.
  • @paramProperty {RpEntityInfo} fiskClientData.pageConfig.rpEntityInfo; The PageConfig values RP entity information. (For UserLogin creation.)
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.challengeKey; The RP challenge value. Base64Url encoded.
  • @return {Promise<PhBuildResult>} The response object.

!!!!This function can show popup dialogs or redirect the user.!!!!

Internal steps:
Example JavaScript implementation:
// example implementation
function RLD_buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView, clientBadgeWrap, fiskClientData) {
  let lipId;
  let lomaId;
  if (fiskProfilePath === undefined) {
    throw new Error("Missing fiskProfilePath argument.");
  }
  let fiskProfileId = fiskProfilePath.fiskProfileId;
  let fiskProfileWrap = fiskClientData.wrapFiskProfile;
  // let pageConfig = fiskClientData.pageConfig;
  // let fiskProfileWrap = CCI_getFiskProfileInfo("remere", fiskProfilePath);
  let fiskProfileCts = fiskProfileWrap.pContents;
  let authAppId = fiskProfileCts.appIdSpec;
  let opId2 = opId; // One of "login", "setActiveCredentials", etc.
  let capId = fiskClientData.capId;
  return Promise.resolve("yyy_response")
  .then(yyy_response => {
    if (opId == "login") {
      let ulFilterOptions = {};
      ulFilterOptions.searchFiskProfile = fiskProfileCts;
      ulFilterOptions.authAppId = fiskProfileCts.appIdSpec;
      ulFilterOptions.capId = capId;
      ulFilterOptions.ancPolicy = fiskClientData.ancPolicy;
      ulFilterOptions.altRapSourceList = altRapSourceList;
      ulFilterOptions.opArgs = opArgs;
      return this.selectUserLogin(ulFilterOptions);
    } else { ... }
  }).then(selectLoginResponse => {
    lipId = selectLoginResponse.lipId;
    lomaId = selectLoginResponse.lomaId;
    let buildOptions = {};
    buildOptions.opArgs = opArgs;
    buildOptions.authAppId = fiskProfileCts.appIdSpec;
    let loginManager_api = A2_getLoginManager(lomaId);
    return loginManager_api.createUserIdentityProof(lipId, opId, fiskClientData, buildOptions);
  }).then(proof => {
    // check values
    let phBuildResult = {};
    // phBuildResult.requestData = proof;
    phBuildResult.requestData = { JWS: proof.JWS};
    phBuildResult.fiskProfile = fiskProfileCts;
    phBuildResult.opArgs = opArgs;
    phBuildResult.opId = opId;
    return phBuildResult;
  });
}

Capis API - Function: RemereDirector.sendRequestToService(svReqContext, requestData)

Return Type Promise<SendResult>;

Sends the RML Request to the Remote Service.

Sends the requestData object to the RP endpoint. In whatever manner the RP designated in the FiskCatalog.

Function info: (parameter list, return value, etc.)
  • @param {SvReqContext} svReqContext; The service request context. buildContext from buildServiceRequest.
  • @param {Object} requestData; The data to be sent to the RP service. Contains the "proof of identity", etc.
  • @return {Promise<SendResult>} The response from the RP service.
  • @typedef {Object} send_response
  • @resultProperty {boolean} @result.statusOK; If the fetch was a success.
  • @resultProperty {Object} @result.jsonData; The data returned from the service.
  • @resultProperty {string} @result.url; The URL that was contacted by the request.
  • @resultProperty {Object} @result.fetchResponse; The wrapped fetch response object.
Example JavaScript implementation:
// example implementation
function RLD_sendRequestToService(svReqContext, requestData) {
  let send_response2 = {};
  let opId = svReqContext.opId;
  let authAppId = svReqContext.authAppId;
  return Promise.resolve("yyy")
  .then(mmm_respose => {
    let buildValues = CLD_getBuildValues(svReqContext.callId);
    let lipId = buildValues.lipId;
    let lomaId = buildValues.lomaId;
    // itemRecordDatabase.recordAction(lomaId, lipId, actionOptions);
    let fiskProfileWrap = buildValues.savedFiskProfile;
    let fiskProfileCts = fiskProfileWrap.pContents;
    let active_endpoint = fiskProfileCts.endpointList[0];
    let sUri = active_endpoint.sUri;
    // Note: Different implementations may split the requestData up into multiple parameters.
    let templateList = [{name:"capis_request", type:"json_object"}];
    let item_0 = Object.assign({}, templateList[0], {value: requestData});
    let dataParamList = [item_0];
    if (!(active_endpoint.method === undefined)) { active_endpoint.method = "POST"}
    let fetchInfo = {method:"GET", contentEncodingType: "JSON"};
    fetchInfo.uri = sUri;
    fetchInfo.queryParamList = dataParamList;
    return capisControl_api.sendRequestToService(svReqContext, fetchInfo);
  }).catch(error => {
    console.log("Error in sendRequestToService. error=" + error.message);
    return Promise.reject(error);
  });
}

Capis API - Function: RemereDirector.evaluateServiceResponse(svReqContext, opSendResult)

Return Type Promise<PhEvalResult>;

Evaluates the RML Response, sent to the browser in response to the RML Request.

Update the Remere Login Status at the RP, add the result to history. Calls the thisRemereDirector_api.setCurrentLoginStatusValues function.

If the opId is "registerUserLogin" (or "setActiveCredentials"), this function call LoginManager.updateUserLoginWithServiceResult(lipId, svUlUpdate, capId).

Function info: (parameter list, return value, etc.)
  • @param {SvReqContext} svReqContext; The service request context. Used to create the request to the RP service.
  • @param {SendResult} opSendResult; The response from the RP service.
  • @paramProperty {boolean} opSendResult.statusOK; If the fetch was a success.
  • @paramProperty {Object} opSendResult.jsonData; The data returned from the service.
  • @return {Promise<PhEvalResult>} The response from the evaluation.

Capis API - Function: RemereDirector.getLoginStatus(authAppId)

Return Type Object;

Gets the current Remere Login Status at the RP, the FiskProfile, etc. (Which service was used to login.) This function does NOT return the lipId. It does not return the raw UserId.

Function info: (parameter list, return value, etc.)
  • @param {string} authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @return {Object} The User Login status info. Or null.
  • @typedef {Object} ulStatus
  • @resultProperty {string} @result.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id is NOT available. Ex: "u34"
  • @resultProperty {string} @result.lomaId; The LoginManager identifier is NOT available. Ex: "m1"
  • @resultProperty {string} @result.userId; The userId is NOT available. Example: "f234"
  • @resultProperty {string} @result.sessionUserId; The temporary UserId that is logged in.
  • @resultProperty {int} @result.statusCode; The state of the login. Ex: 2 = logged_out
  • @resultProperty {boolean} @result.hasError; If there is an error.
  • @resultProperty {string} @result.errorCode; If there is an error. 1=unknown authAppId. 2=missingRecord.
  • @resultProperty {boolean} @result.noRecord; There is no record for the given authAppId. (uninitialized).
  • @object_example loginStatus = {authAppId:authAppId, sessionUserId:"none", statusCode:2} (2="logged_out")

The login status is a number. 1 = logged_in, 2 = logged_out.

Example JavaScript implementation:
// example implementation
function RLD_getLoginStatus(authAppId) {
  let loginStatusInfo = rpLoginStatusManager.getLoginStatus(authAppId);
  if (loginStatusInfo == null) {
    let loginStatusInfo2 = {noRecord: true, isLoggedIn: false};
    return loginStatusInfo2;
  }
  return loginStatusInfo;
}

Capis API - Function: RemereDirector.importUserLogin(lomaId, passbookId, exportedLogin, importOptions, fiskClientData)

Updates the "ActiveLogin" status list.
Return Type Promise<Object>;
Function info: (parameter list, return value, etc.)
  • @param {string} lomaId; The LoginManager identifier. Ex: "m1" (Where to store the import.)
  • @param {string} passbookId; The Passbook identifier. Ex: "p2" (Where to store the import.)
  • @param {UlImportData} exportedLogin; The exported User Login to import.
  • @paramProperty {Array} exportedLogin.tagList; The list of "tags" to add to the UserLogin.
  • @param {UlImportOptions} importOptions; The User Login import options.
  • @paramProperty {FiskClientData} importOptions.fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {RpEntityInfo} importOptions.rpEntityInfo; The RP entity information. (For credential creation.)
  • @paramProperty {Object} importOptions.credSourceCreationList; A list of "Credential Source creation" objects.
  • @param {FiskClientData} fiskClientData; The collected client data. {challengeKey, capId, ancPolicy}
  • @paramProperty {string} fiskClientData.capId; The Client Application Id. Ex: "login.example.com"
  • @paramProperty {string} fiskClientData.ancPolicy; The ancestor policy. Ex: "allowSOWA"
  • @return {Promise<Object>} The response object.
  • @typedef {Object} import_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: RemereDirector.logoutAll()

Return Type Promise<Object>.
Updates the "ActiveLogin" status list.

Capis API - Function: RemereDirector.selectUserLogin(ulFilterOptions)

Return Type Promise<Object>; The "login" operation can be canceled.

This may display a dialog and ask the user what credential to use. There may be a setting to use a specific credential. Or, to use the only matching credential.

!!!!This function may show popup dialogs or redirect the user.!!!!

Function info: (parameter list, return value, etc.)
  • @throws Error if the user cancels the selection.
  • @param {UlFIlterOptions} ulFilterOptions; The User Login filter criteria. (Limit the returned list.)
  • @return {Promise<Object>} The response object.
  • @typedef {Object} selectedUserLogin
  • @resultProperty {string} @result.ulSelectionType; The type of selection. Ex: "ULC_createUserLogin"
  • @resultProperty {string} @result.lipId; The LoginItem Permanent Id. Ex: "u34"
  • @resultProperty {string} @result.lomaId; The LoginManager identifier. Ex: "m1"
  • @resultProperty {string} @result.crtPassbookName; The passbook creation name.
  • @resultProperty {string} @result.crtUserLoginName; The UserLogin creation name.
Internal steps:
  • Call LoginManager.collectUserLogins(ulFilterOptions)
  • May call thisRemereDirector_api.displayUserLoginChooser
Example Javascript use:
// Example JavaScript using "selectUserLogin"
  // Example Promise resolves to:
  let lipId;
  let opArgs = { FiskProfile: fiskProfileCts };
  return thisRemereDirector_api.selectUserLogin(ulFilterOptions)
  .then(selectedUserLogin => {
    lipId = selectedUserLogin.lipId;
  });
Example JavaScript implementation:
// example implementation
function RLD_selectUserLogin(ulFilterOptions) {
  let authAppId = ulFilterOptions.authAppId;
  let capId = ulFilterOptions.capId;
  let storageOptions;
  let altOptions; // OK if it is undefined.
  return LoginManager.collectUserLogins(ulFilterOptions)
  .then(collectLoginsResponse => {
    ulFilterOptions.regUserLoginArray = collectLoginsResponse.qualUserLoginList;
    ulFilterOptions.unregUserLoginArray = collectLoginsResponse.unqualUserLoginList;
    storageOptions = {unregPassbookArray: collectLoginsResponse.unregPassbookArray};
    return thisRemereDirector_api.displayUserLoginChooser(ulFilterOptions, storageOptions, altOptions);
  }).then(chooseUserLogin_response => {
    let collectedUserLogins = {lipId: chooseUserLogin_response.chosen_lite_id};
    return collectedUserLogins;
  });
}

Capis API - Function: RemereDirector.displayUserLoginChooser(selectionOptions, storageOptions, altOptions)

Return Type Promise<Object>; The "login" operation can be canceled.

Asks the user which User Login to use.

Shows a dialog to the user with three lists, and asks which option to use. A list of User Logins, a list of creation/storage options, and a list of alternate sources.

Internal steps:
  • Displays the three lists to the user in a popup dialog.
  • The dialog asks the user to select a User Login, or to add a new one.

Capis API - Function: RemereDirector.selectActiveCredentials(lipId, userLoginInfo, scoOptions)

Return Type Promise<Object>;
This function throws an Error if the user canceled the operation.

Changes which user credentials are "active". (used to authenticate the user. For login, etc.) Creates a new "set" which contains references to existing credentials. Returns an object with "add", "remove" and "create" properties. The first 2 are lists containing existing credential references. The "create" property is a list of credential creation instructions.

This function can return (1) multiple selected credentials, and (2) multiple new credential creation instructions. (Instructions to create public key pairs or passwords etc.) This function is used by the "registerUserLogin" and "setActiveCredentials" opId. This function may display a dialog to the user, asking for their input. (To select credentials or create new credential instructions. A password, etc.) The dialog should contain the number and type of credentials and Credential Verification Rule that are required and supported by the RP. (i.e. Display to the user the RP requirements.)

Function info: (parameter list, return value, etc.)
  • @throws Error if the user cancels the selection.
  • @param {string} lipId; The LoginItem Permanent Id. Ex: "u34" (This may be = -1)
  • @param {Object} userLoginInfo; Contains the UserLogin userId, etc.
  • @paramProperty {string} userLoginInfo.authAppId; The Auth Application Id. Ex: "example.com/remereApp/1234567890"
  • @param {Object} scoOptions; "Select Credential Authentication" options.
  • @paramProperty {Object} scoOptions.specFiskProfile; The selected FiskProfile.
  • @paramProperty {string} scoOptions.capId; The Client Application Id. Ex: "login.example.com"
  • @return {Promise<Object>} The selection info.
  • @typedef {Object} acSelection_response
  • @resultProperty {Array} @result.selectedCredIdList; The selected existing credentials.
  • @resultProperty {Object} @result.ulAuthProfile; The credential profile that was selected.
  • @resultProperty {CredCreation[]} @result.credCreationList; The credentials to be created.

Capis API - Function: RemereDirector.setCurrentLoginStatusValues(authAppId, newValues)

Return Type Object;

Update the Remere Login Status at the RP, add the result to history.

Capis API - Function: RemereDirector.setPendingLoginStatusValues(authAppId, newValues)

Return Type Object;

Update the Remere Login Status at the RP, add the result to history.

Capis API - Function: RemereDirector.connectLoginManagers(browserUser)

Return Type Promise<Object>;

Display the browser's popup dialog, asking the user to connect to any LoginManagers. The argument "browserUser" is optional. If it is not supplied, then the default_browser_user is used.

Function info: (parameter list, return value, etc.)
  • @param {string} browserUser; The current user that is logged into the browser.
  • @return {Promise<Object>} The response object.
  • @typedef {Object} connectLoma_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

Capis API - Function: RemereDirector.disconnectLoginManagers(lomaIdList)

Return Type Promise<Object>;
Function info: (parameter list, return value, etc.)
  • @param {string[]} lomaIdList; The list of LoginManager IDs to disconnect.
    The lomaIdList can also be the special value "all".
  • @return {Promise<Object>} The response object.
  • @typedef {Object} disconnectLoma_response
  • @resultProperty {boolean} @result.isSuccess; If the function was a success.

API Examples

Example JavaScript 1:
let siteInfo = {
  "rpInfo": {
    "siteName": "ACME Corporation",
    "smallIcon": "/images2/logo.png"
  }
};
let opArgs = {
};
let login_promise = remereLogin_api.performServiceOp("login", opArgs, fiskProfilePath, triggerView);
Example JavaScript 2:
function doSomething(opArgs) {
  // Could call remereLogin_api..performServiceOp("login", opArgs, fiskProfilePath, triggerView);
  return remereLogin_api.buildServiceRequest("login", opArgs, fiskProfilePath, triggerView);
  .then(phBuildResult => {
    return remereLogin_api.sendRequestToService(phBuildResult);
  }).catch(function (error) {
    console.log('Cannot create identity proof. Error is: ' + error);
  });
}

API Library Notes

  1. Note that the "serviceCatalogUri" and the ServiceGuide HTML Element name="serviceguidedata" attribute values must start with a slash. (And "smallIcon" values too.)

    Note: The API functions must know additional information about the caller or the browser tab. Because the serviceCatalogUri values passed in CAN ONLY START WITH /. The functions must be able to get the domain name from somewhere.
  2. The browser maintains a list of the Remere Login Status for all known Capis authAppId items. This lets it perform the RemereLogin.logoutAll() function.
    The opId="login" and "logout" functions work together to update the list. The "login" function updates the "RP Remere Login Status list" during every login attempt, and success/fail result. It either adds a record to the list, or updates the RP record. The record contains the lipId, the domain, the serviceCatalogUri and the "login status". The login status is initially set to "pending_login". If the login succeeds, the record is updated to a "logged_in" login status. When the "logout" function is called, it changes the status of the item to a "pending_logout". And then to a "logged_out" status if the RP responds with a success. The "RemereLogin.logoutAll" function uses this list to log out of all the remaining active logins.
  3. After a login, the browser should either redirect or refresh the page, to display sensitive information. (If there is a redirectUri property.)
    For logout, the browser should redirect or refresh the page, to remove sensitive information.
  4. Note: It is acceptable for RP websites to receive the UserId certificate. (Via RemereLogin.getUserIdCertificate() function.) A UserId certificate is not a security risk. The UserId certificate is already given to all RPs as part of the "backed UserId Assertion". (In Mozilla Persona.)
    The UserId Certificate only authorizes a public key. Websites can do nothing without the related private key.
  5. RemereLogin.sendRequestToService()
  6. RemereLogin.buildServiceRequest(opId, opArgs, fiskProfilePath, triggerView). It gets a non-expired UserId certificate, then uses it to generate a signed UserId Assertion. If there isn't a valid UserId Certificate, then it may cause the user to login to their identity provider.
  7. Note that these JavaScript API functions return a promise. If for any reason the functions cannot complete correctly, then the promise's failure callback will be executed. For example, if the user cancels the login attempt, or the user supplied the wrong password.

    The promise's failure callback is NOT called if the function completes correctly. For example, if the POST is sent to the website, the failure callback will never be called. Even if the website rejects the login or has some other error. In this example, if the website has an error, it can redirect the browser to a web page detailing the error.
  8. When using an ServiceTrigger HTML Element, the CapisControl.invokeServiceFromTriggerHtmlElement() function registers an internal function as the callback to receive error notifications. For example: if a login was started, but the function does not complete correctly (because the user cancels it), control is returned to anchorAction() function. It may then decide to display a popup to the user, or pass the error on to the browser.
  9. Note that browser extensions can redefine the native Javascript functions. This allows extensions to easily modify the process.
  10. The login function ends by either the user canceling the login opId, or with the browser sending a POST to the website. Either of these events will finish the login dialog pop ups and return control to the current browser tab.
  11. This means the RP needs to create an endpoint to handle a POST (For example, the "/_capis/remere/requestOp.json" address) to accept a UserId Assertion and verify it, and then redirect the user to wherever else the site wants the user to go.

Goals

Note: the general term for websites or other systems that want to use a sign-in system to log in users, is "Relying Party". (RP).

The top goals:

  1. Make it possible for users to sign into websites with a few clicks. (No text entry.)
  2. Eliminate manual sign-in forms on websites. Users should never be prompted to manually type sign-in data into a form on a website.
  3. Create a standard way for a browser to perform the sign-in process. One that does not involve individual form submissions on websites. The process must be standardized, so that it can be automated and extended.
  4. A key goal is that a user should be able to use an unmodified browser (without extensions) and the browser should be able to detect a login attempt, and allow the user to access their "credential storehouse" (i.e. their password manager) to select a login identity.
  5. Create a standard way for RPs to inform browsers of their login requirements. (In the FiskCatalog.)
  6. Create a standard way for an RP to mark which HTML elements it uses for the sign-in process. So the browser does not have to guess which HTML elements and forms are used for authentication.
  7. Create a standard way for an RP to add any necessary Configuration Settings to the HTML page. (Such as a nonce or challengeKey value.)
  8. Create a standard way for browsers to send RML Requests (e.g. login attempts) to an RP. A way for the browser to send the user's selected "proof of identity" to an RP.
  9. Create a standard way for browsers to communicate with a "credential storehouse" (such as a password manager), and get temporary access to credentials. The storehouse can be online, or a physical item the user carries.
  10. Make it extremely easy for RP authors to implement. Do not require advanced scripting, Do not require the use of an advanced messaging API. Make it simple.
  11. The RP must be able to choose the types and number of proof it wants to allow, and/or require. It must be able to require multiple factors of authentication (i.e. multiple credentials) for a claimed identity. (i.e. A bank, etc. may have additional requirements.) For example, an RP can require the use of public key signatures instead of passwords. It can require a mix of different credential types. Such as a password and two public key signatures, etc.
  12. It must be extensible. So that modifications and improvements can be made in the future.
  13. It must provide clear hooks for other software to interface with. In particular, browser extensions, such as password managers, must be able to integrate themselves into the process.
  14. Automate the sign-in process. Make the browser and the RP do all the work.
  15. Make a standard way for the browser and the RP to communicate sign-in matters to each other. So that the sign-in process can be automated.
  16. Make it more secure. (Websites can store public keys for users, instead of passwords.)
  17. Make it easy for users to use public key cryptography, instead of passwords, to sign-in to websites. (i.e. Start to eliminate passwords.)
  18. Eliminate the need for users to remember usernames and passwords for each website.
  19. Reduce the website's custom interaction in the sign-in process. Do not require any special actions of RPs. No advanced scripting, or verification. Sign-in must work without JavaScript.
  20. It must support multi-factor authentication. i.e. sending RPs a "proof of identity" that contains a multiple credentials, or different types.
  21. As much as possible, the new standard sign-in process must not create any problems or impose any security risks. (Such as requiring all browsers to trust a particular website.) It must work without requiring JavaScript in RP web pages. Do not build in a single point of failure. Like requiring the RP communicate with a third-party website, which may be inactive, or can be hacked.
  22. The user authentication process should be able to be used by any compliant RP, Even non websites. For example, video games may wish to authenticate a user's permanent ID. And organizations that encourage user reviews may want to authenticate their users. The reviews would be annotated with a user's permanent ID.

The goals are met in a few steps.

  1. Created a standard way for websites to inform browsers of sign-in HTML elements, options and requirements.
    Solution: Created a few new HTML attributes that websites can augment HTML elements with. Websites can publish a FiskCatalog describing their sign-in process and requirements.
  2. Created a standard way for browsers to make RML Requests, and send an expandable credentialRegistration list, with a mix of different credential types, to websites. It must support using public key cryptography and discourage using passwords.
    Solution: The browser creates a JSON object as an RML Request. It sends the JSON to the website "Service Endpoint" in whatever manner the website defined in its FiskCatalog.
  3. Created a standard multi-step process for how browsers perform User Authentication. (Note that "user authentication" includes more than just login. It includes account creation and changing credentials. Adding, removing and changing passwords and public keys, etc.) This allows browser extensions (and password managers, etc.) to more easily hook into and modify the User Authentication process.
    Solution: The browser detects when one of the special HTML anchors is clicked, and calls an internal API function. It will automatically create and send a "service request" to the website. It contains the user authentication and the opId to be performed by the RP.
  4. A "proof of identity" contains one UserId and **multiple** credentials. All the included credentials should have the same UserId. This is due to supporting multi-factor authentication. An RP can require multiple Credential Proofs during authentication (i.e. during login). Because of this, the "UserId" (a.k.a. a user identifier) is used to identify the "set" of credentials that the user wants to login with. (A user uses an "User Identifier" to authenticate themselves.) An identity contains one and only one UserId. But it can contain multiple proofs. All the proofs are inside the "proof of identity".
Other goals:

Term Definitions

Credential Verification Method
The process by which a particular type of Credential Verification Rule can be satisfied by a "credential". (i.e. How a credential can be proven to be valid.) Each Credential Verification Rule must have an associated type or "Credential Verification Method" so that they can be used to test if a "credential" meets the requirements of the CVR.

Term Definitions for Multi-site User Identifiers

Further terms defined for Multi-site User Identifiers.

Multi-site User Identifier (MSUID)

A type of identifier designed to be used at multiple RPs. It is the opposite of a "Single-site UserId". These type of UserIds are (1) universally unique, (2) easily distinguished from a Single-site UserId, (3) ownership is provable, and (4) their use is restricted at RPs. Only the "owner" can use it. RPs must not allow a Multi-site UserId to be used unless a user can prove he owns it. In this manner, the UserId is guaranteed to be available at an RP for the owner to use. A single user can have multiple "Multi-site UserIds". Each one can be used independently, at multiple RPs.
A Locked Id is a type of "User Identifier". All Locked Id MUST ONLY use the "no-leak" Credential Verification Methods, Credential Verification Rule, such as a public-key. They cannot use a "raw password". As the password would have to be public, in order to let RPs verify that a user "owns" the identifier.

Multi-site UserIds must follow a format specification. QIF is one such format specification. In order to restrict their use at RPs, a Locked Id format specification usually requires the use of characters that are restricted to Single-site UserId. These characters include the colon, semicolon, comma, equal sign, etc. For example, the QIF specification begins all identifiers with "qis:". This makes conforming identifiers (1) label themselves as using the "QIF" format, (2) distinguishable from a "Single-site UserId", by requiring them to include a character that is usually restricted to "Single-site UserId". (i.e. the colon)

Most permanent user identifiers are Locked Id. Email addresses are almost Locked Id as well. "Multi-site UserIds" are usually issued by a controlling organization, so as to guarantee the identifiers are universally unique and follow the format specification.
All "Locked Id" must be "reserved" at RPs. That is, RPs MUST NOT register a Locked Id to any of its users, unless they first verify that the user can unlock the ID. The primary concern is with account creation. An RP must not allow the creation of an account using a Locked Id that is not owned by the user. This prevents errors and malicious users from creating accounts using someone else's UserId. This is not as big a problem as it might seem, as Multi-site UserIds intentionally include characters that are not allowed in Single-site UserId at most RPs. So, by default, a Multi-site UserId is not a valid UserId at most RPs. Usually, RPs must intentionally include support for Multi-site UserIds.
An email address is almost a Multi-site UserId. It does obey a format specification, and is universally unique. Users can also "prove" that they own the identifier. Almost. (They can prove that they receive email at the address.) However, it lacks two qualities. First, there needs to be a restriction, so that all RPs, during account creation, should verify that the user "owns" the identifier. That he can receive email at the email address. Second, the user doesn't really "own" the identifier, it is a service provided by a website. Which means that the user can actually lose control of the identifier, and another entity (the service provider) has access to all of his accounts.
The Locked Id, uses a public "UserId binding document" rather than a database record stored by the RP. Because the same identity is used by multiple RP. The regular User Identities, stored at RPs, do not need a separate, public, binding document, as the RP stores the Credential Verification Rule for a UserId in a database record.
Online Identity
An identity that uses a universally unique identifier. The UserId may or may not be permanent (i.e. a permanent UserId). The UserId in an "online identity" must follow a strict naming convention. QIF is one such convention.
Identity Provider
See Identity Provider.
Permanent User Identifier (PUID)
A UserId that meets certain qualifications so as to make it both universally unique, and permanent. That is, the identifier is permanently owned by the user. Control of the identifier cannot be taken away. No one else will ever have the ability to prove they "own" the identifier. Unless the identifier is compromised. (Authorities may publish lists of compromised identifiers in an effort to prevent abuse.) Permanent identifiers must follow a strict format specification, and are therefore usually Locked Id. Many QIF namespaces create permanent identifiers. (However, the "A:A:" types are not globally unique. Therefore, they cannot be used to create permanent identifiers.)
Any type of account at a website, including all email addresses, are NOT permanent. Because the account provider (e.g. the email provider) has control of the identifier, and can re-use or re-assign the account name to another user at some future date. However, it is easy to create a PUID from an account name, by adding a timestamp. The issuing authority certifies that at the time of creation, there was only a single PUID creation request with the specified payload string. (A payload that matches the account name.)
A Multi-site UserId does not have to be permanent. However, permanent identifiers are usually Locked Id. As it doesn't make a lot of sense to make a permanent identifier that isn't universally unique, and doesn't conform to a format specification.
Examples:
  • "qis:A:H:2:mK7HnbndYb746GvnYkrU" // Type "A:H:2" contains a base64url encoded COSE_algId public key.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq" // Type "A:F1:2" contains a type-C PermanentBackerId, a base64url userNum and a timestamp-id.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
Identity Namespace
Part of the QIF specification, it is the name of the standards body that the QIF identity adheres to. It identifies the set of standards the identity supports. A namespace is the "group" or "category" of the identity. A QIF identity must have one, and only one, namespace. Each namespace has its own formats, requirements and standards. Each namespace has a single authority organization that creates new Identifier Lock Id. The authority may be a registrar, it may accept and register new user identifiers (UserId) submitted from users. Or it may disallow user submissions, and issue all user identifiers itself. (An example is when a namespace uses a user identifier (UserId) that contains a timestamp or a number that is incremented.) The namespace defines the type and number of Credential Verification Rule that its identities can have. (For example, by a certificate issued by the namespace authority.) RP websites can choose to support or require only certain types of identities.
Example: "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
(See identity detail.)
Identity Provider (IDP)
An entity that provides identity services for its users. A user can change their IDP at any time, without changing their identity.

An Identity Provider (IDP) "verifies access" to an identity. It issues a temporary access token (An "UserId certificate") to the browser. An IDP usually controls the identity resource itself. (An email provider, etc.)

ID Broker
An indirect type of IDP. It does not control the identity resource itself, but can supposedly verify access somehow. It can act as an intermediary to other IDPs. It creates "UserId certificate"s. Because brokers do not control the identity resource, relying on them (by an RP) requires an additional level of trust.
The identity verification process

An RP receives a "proof of identity" from a user during login. The RP must validate the proof of identity.

The algorithm looks like this:

Extract the "claimed identity" (the UserId) from inside the proof of identity. Examine the claimed identity to determine its type, class, etc. Obtain the "UserId certificate" for the identity, and verify it. Look in the certificate to get the set of Credential Verification Rule (CVR). Then verify each CVR using the appropriate "Credential Verification Method". That is, input the proof of identity as an argument to the "identity verifier". Note that each type of "CVR" has its own Credential Verification Method. The "identity verification process" will use or invoke the appropriate "verification method" for each CVR in the identity.

Benefits of the design:

Benefits of this proposal

Benefits of the proposed User Authentication Flow

It limits what RP websites are required to do. A website must only do four things (1) publish a FiskCatalog detailing what types of authentication it supports. (2) modify its login pages to use ServiceTrigger HTML Elements with the special HTML attributes. (3) Set up an "Service Endpoint" to receive the RML Request. (4) Validate the RML Requestand the contained "proof of identity". (5) Send a RML Response. (Either a "success" or an "error".) The RP does not have to implement any complicated scripting or secure channel communication with a FIDO authenticator device. It can not get access to the list of login credentials that the browser has.

More Detail

This specification creates some additional HTML attributes that can be used to modify elements, usually the anchor and form elements. (A "button" can be used as well, etc.) By using the attributes, the usual "click" or "submit" behavior of the element is overridden with a special "authentication" behavior. If an anchor has the (rel="servicetrigger") attribute, then the "href" attribute is not used. If a form has the (rel="servicetrigger") attribute, then the form's "action" attribute is not used. Instead, the "click" event (and "submit" event of forms) triggers the special "authentication" behavior.

A more detailed look at the special "authentication" behavior looks like this. First, the browser reads the opId to be performed by the RP from among the attributes of the HTML element. Note that "login" to a website is only one of the actions that can be performed. Second, the browser reads the website's FiskCatalog, which describes what types of authentication the website supports. Third, the browser reads the existing store of login credentials available to it, and determines if user intervention is necessary. (If it should have the user pick which login credentials to use.) If needed, the browser then displays a prompt to the user listing the identities the browser has that match the website's requirements. (Filtering out those identities that use features that the website doesn't support.) From this list, the user is asked to select which identity to login with, or to enter some other login credentials (e.g. username and password) which will be sent to the website instead.

After the login credentials have been selected, the browser will create a "proof of identity" from the selected identity or other login credentials. If the browser does not have a current proof of identity, it will require the user to obtain a new "UserId certificate" from their IDP. After the proof is created, the browser then creates a RML Request. It is a single JSON object that contains the proof and all other options.

The browser sends the service request to the "Service Endpoint" in whatever manner the RP designated in the FiskProfile. (i.e. In the FiskCatalog.) Usually the method is an HTTP POST. The default name of the HTTP parameter is capis_request. See Capis Default Values.

In this manner, the creation and sending of the "proof of identity" to the website is automated. It prevents the user from having to enter any "proof" or passwords, or any text manually into a form. Thus, it provides strong user authentication and reduces the problems associated with creating and remembering many on-line credentials.

This proposal gives a second ability as well. A website can attach additional attributes to a web form, and these will signal the browser that the form contents should be digitally signed. With a private key from the user's identity. The "special behavior" of an Form HTML Element will detect these additional attributes upon form submission. And the browser will display a prompt to the user to guide them through reviewing their submitted form content and selecting an identity to sign it with. After the user input, the form contents are automatically signed and the signature data is added to the RML Request JSON object. This is then sent to the website, likely as a special HTTP parameter named capis_request.

User Identity Detail

A "User Identity" is a combination of a user identifier (i.e. a UserId) and some method of proving ownership (or control) of the identifier. This generally consists of some Credential Verification Rule and some way of "binding" the CVR to the UserId. A user identity can be thought of as a UserId that is "owned" by the user (that the user can prove he controls).

In general, an identity consists of four parts, an identifier (i.e. a "UserId"), a set of one or more Credential Verification Rule (CVR) (e.g. public key, algorithm, etc.), a set of one or more "secrets" (e.g. private key, password, etc.), and some way of "binding" the set of CVR to the UserId. The last three, CVR, secrets and binding, work together to allow the owner to prove ownership. The "secrets" are the only thing that can create a valid "proof of identity", and are known only to the owner.

There are two general types of identities, "local" and "Locked". A "Single-site UserId" can only be used at a single RP. It uses a Single-site UserId, and the RP stores the Credential Verification Rule. (i.e. The RP may store passwords or public keys in a database table.) A "Locked Id" can be used at multiple RPs. It has a universally unique Locked Id, and some kind of "binding that defines the Credential Verification Rules. And it must be careful to ONLY use "no-leak" Credential Verification Methods. (It cannot use a "raw password", as that is insecure. All RPs would have to know the same password.)

A "Locked Id" must be universally unique and easily distinguishable from a "Single-site UserId".

A "username and password" is an example of a Single-site UserId. It has a Single-site UserId, which is not universally unique. Thus, the identifier can only be used at a single RP. Furthermore, the password Credential Verification Method "leaks". It reveals the secret to the RP. (It delivers the password to the RP, and the RP compares it to the password that it has stored.)

In order to login, a user must generate a "proof of identity" and the RP being logged in to must verify the proof. An "identity verifier" accepts a proof of identity as input, finds the "binding" for the UserId, extracts the Credential Verification Rule from the binding, and verifies that the proof of identity meets all the CVR criteria. The CVR are verified by (1) identifying the type of the CVR (i.e. a public key) and (2) executing the Credential Verification Method for the type of CVR. Types of CVR are "public-key", "password", "federated", etc.

In order to prove ownership, the RP must know how to verify each type of CVR in the identity. This means the RP must have an "Credential Verification Method" for each CVR type.

The RP must have a "Credential Verification Method" for each type of "Credential Verification Rule" (e.g. a public key) that it supports. For example, it can have a method to verify a password, a public key signature, IDP signature, federated signature, etc. If an RP only has one or two CVR verification methods, then the RP is limited to accepting identities that contain only those types of CVR.

A working email address is an example of a Locked Id. However, it has several problems. An RP website would have to send the user a code through their email every time the user wanted to login. (To prove control over the email address.) The user's identity would depend on a service that can go down. Someone else can prove control over the email address. (i.e. the email provider.) And the user can lose control of their identity. (The email provider can lock out the user, shut down their account, and even reassign the email address to someone else.)

An alternate name for the "identity verification method" is "authentication protocol".

A UserId that is universally unique must follow a strict naming format. QIF is one such format.

All *known* Locked Id formats SHOULD BE reserved at all RPs. An RP SHOULD NOT allow a Locked Id to be used (used to create a new account, etc.), without first verifying that the user has proven ownership of the UserId. This prevents the creation of accounts using someone else's UserId. However, RPs can only reserve UserIds that meet some specific criteria. In general, RPs should not allow UserIds that include the special characters colon, semicolon, comma, and equal sign, without requiring a "proof of identity". Specifically, any UserId that starts with "qis:A:H:" or "qis:A:F1:" is reserved by the QIF format.

Usually an identity has at least 2 different secrets (e.g. private keys.) To verify the proof of identity, the RP looks up the "binding" for the claimed identity. It ensures that the proof was created from the identity secrets, by verifying each CredentialProofItem with the appropriate Credential Verification Method.
A Single-site UserId (Such as the username in a regular username and password) is an identity. Although it is a bad one. First, use of a Single-site UserId is not limited to the "owner". It can be used by different people at different websites. Therefore, it cannot be used as a universal identifier. Second, using a "password" is bad, and extremely insecure. The Credential Verification Method of a "password" requires each website to have a copy of the user's secret (the password). (See Identity detail.)

(The proof can be some sort of private and public key, verification by an RP, IDP, etc.)

An identity has a scope, within which, the identity is unique. Simple identities, such as a username and password, are only unique within the RP website where they are used. More advanced identities are globally unique. However, even a globally unique identity is not enough, if the identity can be re-assigned to another user. Such is the case with accounts at websites. (Such as an email address.) These types of identities are dependent on the website. At any time, the website can re-assign the account, giving it to someone else. Or, the website could be hacked, or cease operation. Or the website could be temporarily down or without power, due to a hurricane, etc.

Examples of a UserId:
  • "little_bo_peep12345" // A username at an RP. Only unique at a single RP.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq" // Type "A:F1:2" contains a type-C PermanentBackerId, a base64url userNum and a timestamp-id.

An RP must have a "Credential Verification Method" for each type of Credential Verification Rule that it supports. This is how a "proof of identity" can be verified.

An identity must have a binding. i.e. how it is bound to its set of "Credential Verification Rule. (Each CVR type can be verified by a Credential Verification Method".)

Users typically do not see or interact with the raw UserId. They are not required to remember it, or enter it into a form on a website. Instead, they are shown a "display name" like: "john@example.com created on 2017-07-23".

Canonical Identity Code (QIF) Detail

QIF conforms to the requirements of a URN scheme. To use it as a URN, prefix it with "urn:". Example: "urn:qis:A:F1:1:ACMA_org:M4j-q".

Users typically do not see or interact with the raw UserId. They are not required to remember it, or enter it into a form on a website. Instead, they are shown a "display name" like: "john@example.com created on 2017-07-23".

QIF defines a uniform representation of usernames used at individual websites. These use the "A" idClass. The payload contains the username and the RP domain, separated with a colon.

A QIF UserId starts with the text "qis:", then contain the namespace, idClass, subClass, and the payload, all separated with the colon character ": ".

Each QIF namespace defines its own rules for how identifiers are created.

The "Z9" namespace is reserved for examples. (Like "example.com".)

The "Z9" namespace defines the following types. "Z9:D92" stands for "timestamp,account" it is an account at a website. (username and URI) "Z9:D91" stands for "timestamp,email". "Z9:D93" is an email address (This identifier does not have a timestamp, so it is not permanent.)

Examples:
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "urn:qis:A:F1:1:ACMA_org:M4j-q" // A URN version of a QIF.
  • "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq" // Type "A:F1:2" contains a type-C PermanentBackerId, a base64url userNum and a timestamp-id.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
  • "qis:A:F2:2:facebook.com:M4j-q:XSAErLq" // Type "A:F2:2" contains a domain name, a base64url userNum, and a timestamp-id.
  • "qis:Z9:D91:XSAErLq:john@example.com" // Type "Z9:D91" contains an email address.
  • "qis:Z9:D92:facebook.com:XSAErLq:SarahConnor37" // Type "Z9:D92". Payload is an account at a website.
  • "qis:Z9:D93:XSAErLq:john@example.com" // Type "Z9:D93". Payload is an email address.
  • "qis:Z9:D94:mydomain.com/somepage.html" // Type "Z9:D94". Payload is a URI. (like OpenID)
  • "qis:Z9:D88:facebook.com:SarahConnor37:FacebookConnect1.0" // Type "Z9:D88". A federated id.
  • "qis:Z9:M2:gno.mn~cccccwhgyes" // Type "Z9:M2". payload is an IDP uri and a device id. The IDP requires signed input from a key-fob One-time password generator with id "cccccwhgyes".

Any RP can create its own "A:A:" user identifiers. The "A:A:" namespace does not contain any globally unique identifiers.

A person could create (or mimic) a "A:A:" UserId by concatenating their username and the RP domain name, but they would be missing something. When the "A:A:" namespace organization creates the identity, it also creates the "UserId certificate" that binds the UserId to the protocol that makes the UserId a federated identity. The "UserId certificate" could contain the Facebook Connect protocol and version. The "UserId certificate" is signed with the public key of the "A:A:" organization.

An "A:A:" identifies a username at a particular RP. In general, this is not something to make public, what accounts a person has at certain websites is privileged information. However, some accounts can be more public to some people. If a user wants to, an "A:A:" CAN be used as a federated identity. The "A:A:" namespace does not define a direct protocol to allow a third party (i.e. an organization. A second RP) to verify the user controls the account at the RP. The RP in question (i.e. Facebook, Google, twitter, etc.) can provide their own authentication service to prove that the user does control an account at the RP. A QIF identifier with type="A:A:" starts the payload with the (1) the UserId at the RP, (2) The domain name of the RP. (3) name of the protocol the RP uses to remotely authenticate the user.

QIF Detail 2

Local identifier

An identifier without a means to make it universally unique. i.e. It does not contain an issuing organization, or a public key. Example: the "qis:A:A" namespace is reserved for these types of identifiers.

Local Identifiers do not include an organization's ID in the identifier. Users and organizations are free to create their own Single-site UserId. It is recommended that the Single-site UserIds that contain timestamps use the time of creation of the identity (i.e. the current time). But this is not required by any authority.

Local Identifiers can be issued by an organization, or they can be created by a user and registered with the organization.

Local Identifiers do not have a managing authority. Identifiers do not need to be issued by a managing authority. (Because they do not contain the authority's identifier.)

Permanent Identifiers

A permanent identifier includes a timestamp.

The "A:F2:2" type includes a base64url timestamp. This makes the identifier permanent. If the user's account username changes, the system must create a new identifier with a new timestamp. If the user simply changes the password associated with the identifier, then the identifier does not change.

Reasons for making a permanet identifier.

(for adding a timestamp to a "A:A:" identifier.)

Systems that use or rely on the identifier can be assured that they will always use the same identifier. Even if the user's identifier at the RP changes in the future, even if someone else is assigned that username, the permanent identifier does not change. It may no longer contain the correct username, but the Password manager" or other system will contain the current username inside the record with the permanent label. It can still identify the user's account at the RP, to other systems. The user WAS able to login with the stated username, on the stated time. A timestamp allows a password manager or other systems to permanently identify an account at the RP. No matter if the username changes.

Imagine that the Single-site UserId is used as a federated identity. Like Facebook connect. Now imagine that the user's identifier at the RP (Facebook) changes. Either the user did it without realizing that login to his other accounts rely on the username, or Facebook changed the username. Perhaps due to the user changing their real name, a court order, or some other problem. With the timestamp in the identifier, Facebook has the option to continue to accept the old identifier. As well as any new username that is created for the user. Both can work. Both can have different password, and public keys, etc.

Imagine two different "credential storehouses", that try to synchronize their contents. The user could have created an entry for the same RP in both of them. Take the case where the two entries share the same usernme and there is no timestamp. With a timestamp, both entries are unique. In particular, the password manager can continue to use the permanent identifier, even if the username has changed.

QIF A1 Namespace

This section describes how the QIF "A1" namespace does things.

In the "A1" namespace, QIF identifiers are "issued" by an issuing authority. The "namespace ID" (NID) identifies the organization that issued the identifier. (i.e. "A1" is the issuing authority.) A new QIF identifier is generated in response to a request. The QIF namespace A1 only creates permanent IDs.

The id creation request must contain at least one public key. It may also contain either a "name string" or a "federation protocol". The "A1" organization assigns a "registration number" to the request. It then creates one or two "id certificates". The first certificate will contain only the registration number and does not have the extra argument. The second certificate is created if the extra argument was submitted. It will contain both the registration number and the "name string" or "federation protocol". The "id certificate" will contain (1) the registration number, (2) the "name string" or "federation protocol" (depending), (3) the CVR. (Credential Verification Rule e.g. public key(s) ), (4) a digital signature. The id certificate is signed by the "A1" issuing authority.

The "A1" namespace type "ri" identifier is intentionally designed to be anonymous. An attacker can gain no personally identifiable information from it. No first name, or nationality, place of origin, spoken language, etc. Even the issuing authority does not know any personally identifiable information about the user.

The "A1" namespace defines the payload to begin with a "type" specifier.

The "A1" namespace defines the following types. "ri" stands for "registration identifier". "ts" stand for "timestamp,string". "tf2" stands for "timestamp, federated identifier mode 2"

The "registration identifier" is typically limited to alphanumeric and dashes. For the "A1" organization it is a decimal number representing the number of the user_identifier registration.

The "A1" "timestamp,string" type_payload contains a network style timestamp, followed by a colon and then some string. The timestamp is a 10 (or more) digit number representing the number of seconds since Jan 1, 1970. To create a "timestamp,string" user_identifier, a user must submit a string value and some other info (i.e. a public key), and the "A1" organization will issue a new UserId and user_identifier. The UserId will start with "qis:A:F1:2:" (then "ACMA_org:M4j-q:XSAErLq"), followed by a base64url timestamp of the submission, followed by a colon, and then finally a decimal integer. (i.e. the submitted string.) The "A1" organization will also return an "user_identifier certificate", that ties the UserId to the extra information that the user submitted (i.e. to the public key).

The "A1" namespace creates permanent UserIds from either an internal counter (the decimal integer) or by combining a timestamp and a string (the "timestamp,string" option). For instance, the string can be an email address or an account name at a website. The "A1" organization certifies that only one UserId is ever issued with the same payload string, and the same timestamp. (i.e. At the time of creation, there was only a single UserId creation request with the specified payload string that was "accepted".) If, for some reason, the "A1" organization receives two or more requests with the same specified payload string, at the same time, it will accept only one of them. And will either return an error code to the others, or delay the response until the next timestamp is available. Therefore, the UserId is universally unique. It is also permanent, because the "A1" organization cannot control the user_identifier. (It does not have access to the public key, etc.)

The user_identifier created by the "A1" namespace will ONLY use the "no-leak" Credential Verification Methods, such as a public-key. This will make the user_identifier safe to use at multiple RPs.

All identifiers in the same namespace can use the same "Credential Verification Rule".

The "A1" namespace only creates permanent identifiers. Therefore, they are always available. Even if the issuing server is shut down. They are permanent UserIds. They are not dependent on the continued good behavior of a website. This can be created in a few ways.

In both cases, the UserId needs an associated "binding". (This can be an user_identifier certificate) The document binds the UserId with one or more Credential Verification Rule (CVR) (e.g. public keys) The "proof of identity" can be verified by verifying each the proof has every required CVR. A CVR can be proven with the appropriate "Credential Verification Method". A digital signature can therefore be used to prove ownership of the user_identifier.

Once the "user_identifier certificate" is issued, the issuing server doesn't have to do anything. There is no user account or day-to-day operation. With one exception. The public key used to sign the "user_identifier certificates" must be publicly available. It is therefore necessary for the issuer's public key to be published at multiple, well-known places on the internet. In case the issuer website looses power, etc.

Using Multi-Site UserIds

Using A Federated UserId

In order to sign in, an RP website must receive proof that the user owns (or has temporary access to) a federated UserId. The idea is that a user must sign in to their identity provider once per browsing session, to get this proof of access. Which may last for 6 hours or so. After that, the browser can use this "UserId certificate" to create a different "proof of identity" for any number of RP websites, in order to log in. Until the "UserId certificate" expires, or the user "signs out" of the UserId, or the browser detects a lapse in user activity and automatically "signs out" of the UserId.

A Locked Id is used to make things easy for the user. They can use the same UserId on every website, because it's reserved for them. It can't be used by anyone else. Unlike a Single-site UserId can be. (Like a username in a regular username and password.) RPs only register a Locked Id to someone if the user can unlock the ID. (i.e. If proof of ownership of the UserId is provided.)

When the user clicks on an "auth" HTML anchor, the browser can (optionally) show a dialog to the user containing a list of User_Logins for that RP. Ones that it knows about. The default User Login is pre-selected in the list. The user is free to add additional User Logins to the list as well. This behavior can be customized in the browser. Perhaps the user only wants to use one User Login and does not want the additional User Login choices to be displayed in the popup.

The "proof of identity" is composed of two parts, the "UserId Certificate" and the "UserId assertion". First, the "UserId Certificate" is the proof created by the identity provider and given to the browser. Second, the UserId Assertion is the proof created by the browser and given to the individual RPs. The UserId Certificate *temporarily* authorizes the browser to use an identity. It proves that the identity provider authorized it for a period of time. If the UserId Certificate has expired, the user will not be able to log into any RPs. So the browser will need to perform an extra step and get a fresh UserId Certificate from the identity provider. This usually involves having the user log into the identity provider. Because an UserId Certificate usually lasts at least several hours, the extra step is usually only required once per browsing session.

This specification is designed so that the user will be required to prove their identity about once every 24 hours. (That they are forced to log into their identity provider again every 24 hours.) This is done by forcing the UserId Certificate to last for only about 24 hours. So the user is forced to obtain a new "UserId Certificate".

This specification only defines the communication between the browser and the RP. First, how the RP can advertise to the browser what types of identities and protocols it supports. Second, how the browser is to send the "proof of identity" to the RP. Third, the process by which the RP must verify the proof. Including both the UserId Assertion and the "UserId Certificate". (It intentionally does not define exactly what form an UserId Certificate must be.) Fourth, it defines a set of JavaScript API functions that the RP web page can call. This specification does not define how the browser obtains or creates an "UserId Certificate". It does not define how the browser communicates with the IDP.

An ID broker serves as an intermediary to some category of IDP. It is also an IDP itself. It may simply translate requests and responses between the browser and the real IDP into the necessary protocol. Alternately, it can verify the user access to the identity by itself. (It can be a fallback IDP, if something is wrong with the actual IDP.) However, because the broker does not control the identity, it usually has to do some sort of workaround. For example, if the payload is an email address, then the IDP is defined in the email address. However, the IDP may not support the required protocol. It is easier for the browser to use an ID broker. The broker can determine what protocol the IDP supports, how to communicate with the IDP, or if it needs to verify the email address itself (with an email containing a code or url). (The RP must contact the IDP to get the signature verification keys.)

Additional items of proof can be sent to the RP in the HTTP request, besides the regular proof of identity. This allows logins to use 2 factor authentication if the RP supports it. To do this, the RP must state what type of authentication it supports. This is done by adding properties starting with "supported" to the "FiskProfile" used by the page. For example, the RP can store a public key for each identity. Note that this requires the identity (in the user's browser or elsewhere) to store the related private key. (It can be a different private key for each RP.) When logging into an RP, the private key for the RP would be used to create an additional key signature. This additional proof would be sent to the RP in the same HTTP request as the proof of identity. And the RP would verify it against the public key it has stored for the identity. (When creating an account, the RP may allow additional public keys to be set. The public keys can be sent to the RP in an additional Request property. The keys can be in JWK format. When logging into an RP, the second signature can be sent to the RP in this second property.) (See "req_factor2".)

Identity verification is done by the RP. Both the UserId Certificate and the UserId Assertion are given to the RP. The RP must verify both of them, and verify they are for the same identity. Once both have been verified, the RP will log the user in, and send a RML Response to the browser. The browser will either redirect the user to a different page or refresh the current page or part of the page. (So that the "login" link becomes a "logout" link. etc.)

Note that the RP can require an additional (2nd factor authentication) during login. If the RP stores a public key for each user, and requires signature for that key from the user, in addition to the proof of identity, then it will extract the UserId from the UserId Assertion and use that to lookup the public key for that user in its database. It will then use the public key to verify the UserId assertion (verify its signature).

If both the "UserId Certificate" and the "UserId Assertion" use JWT, then we can use a "backed UserId Assertion". Which is just both JWTs combined with the tilde character "~".

See the "req_ic" property.

UserId Certificate Detail

A "UserId certificate" is a digitally signed statement that contains a "Federated UserId" and a set of public keys, and binds them together. It proves that the Issuer of the certificate approved the set of public keys. So the keys can be used to verify a user claiming the UserId. (It may also contain other types of "Credential Verification Rules" beyond public keys.) It is a type of "UserId binding document". It should be issued by either the Backer Organization or by a delegated "identity provider".

The UserId Certificate is obtained by the browser. From the Backer Organization of the UserId, or from an identity provider. The browser gives it to the RP along with the UserId Assertion. The certificate is commonly in the form of a JWT. The UserId Certificate can be used again and again until it expires. A UserId Certificate is one part of the "proof of identity" necessary to log a user into an RP. A "UserId Assertion" and "UserId Status Report" are the other parts.

Note that the term "UserId certificate" is not specific. This specification is designed to work with as many different types of UserIds and UserId protocols as possible. Including those that have not been created yet. Due to this, the term "UserId Certificate" is somewhat vague. The actual type of the UserId Certificate depends on the IDP. It is usually a JSON Web Token (JWT). But other types of tokens can be used as well. This specification intentionally does not define exactly what form a UserId Certificate must be, but it instead defines what the UserId Certificate needs to include, and what the RP needs to verify.

In the communication between the browser and the RP, the types of UserId Certificate that can be used depend on what the RP supports. In order for a UserId Certificate to be used, the RP must support both its type and verification protocol. See the "supported.uic.format" and "supported.uic.csvm" properties in the RML FiskProfile. Whatever type of UserId Certificate the browser decides to use, the specifics of it are sent to the RP in the "req_ic_type" and "req_uic.csvm" Request properties.

The UserId Certificate may contain:

A "UserId certificate" MUST contain these things. A UserId. An RP public key. The issuer. The issue time. And a digital signature, signed by the IDP.

The "UserId certificate" JWT. header and claims:

Example RML Request payload:
let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, "exp": 1509457689, // Should be a max of 4 years after issue date "iat". (2 years?) "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. // "sub": "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq", "cert_pubKeyList": [ { // The key used to login to RPs. "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b3c34...", "e" : "93bc32..." } ], "cr_key": { // The key used to make changes to the IDP. (change the "UserId certificate") "kty": "RSA", "kid": "202881bf8834", "alg": "RS256", "n" : "4b3c34...", "e" : "93bc32..." }, "scd": "idp_example.com", // status checker domain "sci": "qis:A:F1:1:ACMA_org:M4j-q" // status checker identity (provides a public key) }
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // TODO RRRR "jku": "https://keystorage.com/cvk/authority/cvk.json", "csvm": "idr_keystore" // Get the sig verification key from a "Backer keystore". // Note: The entire key set should be cached by the RP. If the cert issue time is after the cache date, refresh the cache. }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

Note that the combination of the "sub" and the "iat" uniquely identify the "UserId certificate". There is no separate "UserId certificate ID".

The "UserId certificate" has two public keys. One key is used to sign in to RPs, one key is used as a revocation key. (The CR public key is used to sign requests to the registrar. For example, to revoke the old RP key and create a new RP public key and certificate.)

The user doesn't interact with the registrar directly, instead, they interact with the IDP. A new "UserId certificate" request is created by the user signing into the IDP, and filling out a form. The user must sign the form (the request to the registrar) with the CR public key. (This private key is secret from the IDP. So the IDP cannot change the registrar without user approval.) The user can include a new RP public key in the form. (And keep the private RP key a secret from the IDP.) The IDP sends the request to the registrar. This request possibly contains an IDP signature, so the registrar can verify what IDP made the request. The registrar receives the request and responds with either a proper result or an error message. Both are signed by the registrar (including the error message). The registrar sends the result to the IDP. The IDP sends the result to the browser. After a successful result, the browser asks the IDP for a copy of the new certificate.

To change the IDP, the user goes to a new IDP, creates an account and fills out a form. Then the user signs the form with the CR public key. The new IDP does the rest. (The new certificate that is created is registered with the new IDP. The browser then gets a copy of the certificate from the IDP.)

The issuer field must be a URI containing the domain name, (scheme, port and path?). It must not have any query or fragment components.

A UserId Certificate is verified by getting the sig verification key from the issuing IDP, and verifying the digital signature.

"Identity changes" that will cause a new "UserId certificate" to be made. Note that the request to the registrar must be properly verified before the change can be made.

All changes to identities are permanently recorded. In an unmodifiable ledger. If an identity is ever altered, (by malicious hackers) there is a record of what did the alteration. And when. (The creation of a new identity is similarly recorded.)

UserId Assertion Detail

A "UserId Assertion" is a digitally signed statement that claims that the issuer of the assertion (i.e. the user or browser) is authorized to use a UserId. It provides proof that the issuer has possession of, or access to, the set of secrets (i.e. private keys) that are used in the UserId. A UserId Assertion contains the UserId, the date, etc. and a set of signatures of the private keys. The private keys used to sign the assertion must match the public keys that are associated with the UserId.

These public keys may be stored locally by the RP (i.e. as Credential Verification Rules), or they may be bound to the UserId in a UserId certificate or other secondary document. For example, they may be in a "UserId Status Report".

A UserId Assertion is a JWT. It has a signature signed by the private key. The signature can be verified by the public key contained in an "UserId certificate". It is generated by the browser and sent to the RP that is being logged into. A UserId Assertion is one half of the "proof of identity" necessary to log a user into an RP. The "UserId certificate" is the other half.

the recipient URI (domain name or url address)(The recipient address is the url of the RP.) The UserId Assertion must be sent to the RP with the UserId Certificate.

A UserId Assertion may contain:

The sig verification key is in the UserId Certificate. The "kid" should identify which public key in the "UserId certificate" to use. (If there is more than one public key in the certificate.)

It turns out that only the audience and the expiration time are required. The sig verification key is the public key in the "UserId certificate". As a result, an example header and claims section of an UserId Assertion would look like:

Example RML Request payload:

The UserId Assertion JWT. header and claims:

let payload_unencoded = { "iat": 1503971280, // REQUIRED or OPTIONAL? "exp": 1503971430 // REQUIRED "aud": "http://example.com", // "sub": "qis:A:F1:1:ACMA_org:M4j-q" // The UserId is not necessary in using a UserId Certificate. };
let jws_assertion =
{ "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", // "kid": "49a857b610f19", // "kid" is not usually required in an "UserId Assertion". key is in the certificate. "vkm": "certificateF" // Uses the sig verification key in the "UserId certificate". } }] };
Example Certificate:

The "UserId certificate" JWT. (uic_chain[0]) header and claims:

let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, // REQUIRED "exp": 1503971430, // REQUIRED "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "uid": { "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId }, "cert_pubKeyList": [ { "pkf": "JWK", "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." } ] };
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. "csvm": "idr_keystore" // Get the sig verification key from an "identity registrar keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

The audience field must be a URI (or an array of URI) containing the origin. That is, the scheme, domain, and port of the RP that the user wants to sign into. (The scheme, domain, port and path.) It must not contain any query or fragment components. (Ex: "aud": "http://localhost:10001" is valid.)

Embedded JWT - Nested JWT?

A JWT can be embedded in another JWT. It does not have to be "nested" inside the payload.

Example RML Request payload:
let payload_unencoded = { "iat": 1503971280, // REQUIRED "exp": 1503971430, // REQUIRED "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. // "aud": "http://example.com", "cert_pubKeyList": [ { "pkf": "JWK", "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." }, { // Using the JWS Compact Serialization. "pkf": "JWS-CS", "uic.csvm": "YY", "ucv": "Angh4786Gtd9s7dhfkYFfaKXKsi.HFNs9dlNsf98ZH7ld6Fg.JNfh32098dHFYewg98Cdk" }, { // Using the JWE Compact Serialization. (Has 4 dots) // Note that JWE have a "ciphertext" property instead of a "payload" property. "pkf": "JWE-CS", "ucv": "Angh4786Gtd9s.HFNs9dlNsf98Z.JNfh32098dH.M38Hfd8Hre.cHu9Mu3" }, { // Using the general JWS JSON Serialization. "pkf": "JWS", "ucv": { "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQ ogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", "signatures": [ {"protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": {"kid":"2010-12-29"}, "signature": "cC4hiUPoj9Eetdgtv3h....." }, {"protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKA..." } ] } } ] };
Example Certificate:
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ ... ] };

UserId Status Report

A "UserId Status Report" informs the RP of the current status of "UserId certificate"s. Notably, which "UserId certificate"s are no longer valid. (They could have been superseded, retired or revoked. etc.) For example: the public key in an "UserId certificate" could have been revoked. The "ISR" is a document created and signed by the user's IDP, and stored by the user's browser. It is delivered by the browser to all RPs along with the "UserId certificate". (In fact, you can consider it to be "stapled" to the "UserId certificate".) It is REQUIRED. The RP should not allow a log in without it. It is a form of OCSP stapling.

(RIST report. Form of OCSP stapling) The "UserId Status Report" contains:

The "UserId Status Report" JWT. header and claims:

Example RML Request payload:
let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, "exp": 1503971430, // Should be a max of 4 hours after issue time "iat". "sub": "qis:A:F1:1:ACMA_org:M4j-q", // Matches the "sub" of the "UserId certificate" "cit": 1503971280, // The "certificate issue time" of the "UserId certificate". "cstat": "revoked", // Either "valid" or some reason why it's not valid. "cert_pubKeyList": [ { "pkf": "isrkey", "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b3c34...", "e" : "93bc32..." } ], "lpr": 15098765432, // last password reset time at IDP. (Did the user just recently reset their password at the IDP?) "duc": 3, // "device use counter" (is this the first time this specific machine has logged in as this user?) "ila": 3, // number of invalid login attempts since last valid login to IDP. "pas": 45 // IDP rating of user's password streangth. (at the IDP) } let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "9c56a8534db7564f6", // "kid" is REQUIRED. ID of the issuer key. "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. // TODO RRRR "jku": "https://idp_example.com/keys.json" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] }

The signature of the Identity Provider (IdP). The signature must match the IdP public key in the "IdP status report". The RPs may require the report to be less than 8 hours old. (expiration time)

The "UserId Status Report" can only identify one "UserId certificate" as valid. It only holds the status for a single "UserId certificate", and allows that one to be marked as valid or invalid. All othere "UserId certificate"s are automatically invalid. Note that the IDP can mark the single "UserId certificate" as invalid, and thereby lock the user out of all of his RP accounts. This ability is intentional. It allows the IDP to respond to security threats immediately, without the user's knowledge. If the IDP uses this ability maliciosly, or is hacked, the user can change which IDP they use.

The "UserId Status Report" contains the IDP's approval (or disapproval) of a single "UserId certificate". The IDP can "invalidate" (or "revoke") an "UserId certificate" at any time and for any reason. This allows the IDP to respond to compromised RP keys, invalidly issued certificates and compromises of certificate issuers. The IdP can even deny approval of ALL "UserId certificate"s. For example: if there is a misbehaving certificate issuer. Until the IDP can verify the certificate issuer is re-certified.

The "UserId Status Report" uses two fields to contain the IDP's approval, the "current "UserId certificate" issue time" ("cit") and the "current "UserId certificate" RP status" ("cstat"). The "cit" specifies the issue time of the specified "UserId certificate". ONLY this certificate could be valid. The "cstat" specifies if that "UserId certificate" is, in fact, valid. Only an "UserId certificate" with an issue time matching the "cit" can be valid, all "UserId certificate"s with an older or newer issue time are invalid. If the browser does not have an "UserId certificate" matching the "current "UserId certificate" issue time", then the browser SHOULD obtain a new "UserId certificate". It can obtain the new "UserId certificate" from the IDP, or create a new one at the identity registrar. (This will involve communicating with the identity registrar, providing proof of the CR private key, to create a new RP public key and certificate, then registering the new certificate with the IDP.)

Note that the combination of the "sub" and the "cit" uniquely identify the "UserId certificate". There is no separate ""UserId certificate" ID".

If a user a certificate issuer changes its public key, then it must issue a new "UserId certificate". Because all previously issued certificates from that issuer are no longer valid. The IdP can inform all RPs of the need to update by changing the "current "UserId certificate" issue time" for that certificate issuer.

Note that the report can be specific. It can tell the RP why the older certificates were invalidated.

The "UserId Status Report" MUST be provided to the RP websites during authentication. Along with the other identity information. If the RP does not receive a valid "UserId Status Report", then the RP must either deny the login, or must get a current report from a different Identity Provider.

==== Potential abuse, a misbehaving IDP ====

Consider the case of a misbehaving IDP. An IDP has the power to lock users out of their accounts at RPs. It can refuse to create a UserId Status Report, it can use an old or future "most recent "UserId certificate" issue time", or it could declare the key status as something other then "valid". However, these problems do not allow an IDP to change the identity itself, or to log into RP accounts as the user. If the IdP behaves badly, the user can switch IdPs.

However, if the IDP can backdate an ISR and has access to the private key corresponding to the public key that was invalidated... Or, if it refuses to update the ISR when the key should be revoked...

For this reason, it is recommended that RPs keep a timestamp of the last valid "UserId certificate" that was used to log in. If the IDP ever approves an older "UserId certificate", that is a sign the IDP is misbehaving. Do not allow the log in. And report the problem to the user (through email) or to the identity registrar.

IDP API

A server must implement the IDP API in order to be an IDP.

This is the json document at .well-known/rdac-configuration (The RDAC standard)
Example JSON:
{
  "keys_location": "/idp/keys.json", // Where to get the key set
  "isr_location": "/idp/isr", // Where to get the "UserId Status Report". Request must be signed by the 
  "verifyHOTP": "/idp/verifyHOTP", // Must pass in "?identity=qis:A:F1:1:ACMA_org:M4j-q"
  "gic": "/idp/getUserIdCertificate", // Get the "UserId certificate" (pass in the UserId)
  "vemail": "/idp/verifyEmail", // Verify the email address. Request must be signed by identity.
// Sends a link and a code to the email address.
  "evt": "/idp/getEmailVerificationTimestamp", // Get the most recent timestamp when the email was verified.
  "other": "other"
}

OLD Persona IDP API

OLD OLD OLD

BrowserID document

Persona IdP requires that the document "https://yourdomain/.well-known/browserid" must be served (1) over SSL (2) with a content type of "application/json" (3) from the same subdomain or higher. (example.com, not www.example.com)
Example JSON:
{
    "public-key": {
        "kty": "RSA",
        "alg": "RS256",
        "n": "8281890540510513441018722749588539160922
		1288015566078542117409373192106382993306
		5372736775574820852047369750675671118310
		0592132299112716501334044356371338598345
		6311886801211241492470711576322130577278
		5755292028400527536125760614505605881021
		3990784685450125232755130348221350526585
		3706269864950437458242988327",
        "e": "65537"
    },
    "authentication": "/persona/sign_in.html",
    "provisioning": "/persona/provision.html"
}
==== OR ====
{
  "authority": "subdomain.example.com"
}
Example HTML:
The provisioning page must have the necessary JavaScript functions. So include them.
<script src="https://login.persona.org/provisioning_api.js"></script>
The authentication page needs to include the JavaScript API. So include them.
<script src="https://login.persona.org/authentication_api.js"></script>

Portier

Portier Broker uses OpenID.Connect https://broker.portier.io/.well-known/openid-configuration "jwks_uri": "https://broker.portier.io/keys.json"
Example JavaScript:
{
    "jwks_uri": "/keys.json", // Turns into "https://broker.portier.io/keys.json"
    "authentication": "/auth", // must add query arguments
    "provisioning": "/persona/provision.html"
}
github.com/portier/demo-rp/blob/master/server.py
@app.post('/login')
def login_post():

	// The RP should discover what the user's IDP is.
    # FIXME: The Authentication Service Endpoint should be discovered, not hardcoded
    url = SETTINGS['BrokerURL'] + '/auth?' + query_args
    return redirect(url)

Change the User's Identifier

An RP account SHOULD provide a means for users to change their UserId.

Users can change their UserId at an RP like this:

In order to change the UserId, the RP must (1) Separate users and accounts. and (2) Allow authorized users to be added and removed from accounts.

Implementation steps (for the RP)

The above can be accomplished by using an internal user table that maps user identities to accounts. When a user logs into the RP, they are allowed to access the appropriate account(s).

Note that it is possible for a single UserId to map to multiple accounts. (It would also be possible for a UserId to map to zero accounts. Such as a UserId that was removed as an "authorized user" from an account.) This case requires an additional implementation from the RP, it requires that there to be a way for the user to select which account to use.

A very simple way to handle multiple accounts, is for the RP to allow a user to login, and then immediately afterward, to display to the user a page with the list of accessable accounts, and ask the user which account they want to use. After selecting an account, the user experiences that single account until they log out. This way, the RP does not have to change any part of their website or status displays. Other RPs can offer more advanced options to display "the current account" and an option to change accounts, while the user is logged in.

In the simple case, the RP may choose to impose a restriction and mandate that a UserId map to one and only one account. This would result in the following. 1. Two accounts would not be able to add the same UserId as an "authorized users. 2. All "authorized users on an account would be special-case UserIds. Those UserIds would ONLY be used as an authorized user on that specific account, and would not be able to be an authorized user on any other account, or "own" its one account. 3. A user would not be able to switch accounts without logging out and logging in as a different UserId.

However, with only a few changes, this configuration can allow users to grant *limited access* to other users. (See "grantAccess".)

RPs SHOULD provide an AUTOMATED way of changing a user's identity. This allows a user to easily change their identity at all their RPs using a single command. A script can be run (in the user's browser) that will loop through all of a user's RP accounts and change the user's identity at each RP. This will prevent the user from having to log in to every RP website to change their user-identity. However, unrestricted access to this API may be dangerous. The RP SHOULD require additional verification before doing it. (i.e. before adding a new identity to the account through the external API.) For example, the RP can send the user an email to verify that they want to grant "owner" access to a new identity.

The "grantAccess" API allows users to add and remove identities from their accounts through an external API. It goes one step further and also assigns an "account_permission_role" to each added identity. The basic account_permission_roles of "owner" and "none" should exist at every RP. "owner" is the highest account_permission_role. While "none" removes all account access of the identity from the account. RPs can optionally implement other account_permission_roles.

In order to change the user's identity using the "grantAccess" API... The user grants a new identity "owner" account access, then login as the new identity and set the old identity's account_permission_role to "none" (Which revokes the old identity's access.)

Granting account access to other users

An RP may allow its users to grant some level of account access to other users and services. (Non-person users can be called a "robot" users.)

This is what the "grantAccess" opId does. This API also provides a means for users to change their identity.

When granting access to another user, the "account_permission_role" to give the user must be specified. The basic account_permission_roles are "none" and "owner", although there can be more. For example: (none, limited, read-only, full, owner) Only the "none" and "owner" levels are required. The highest account_permission_role is "owner". The "owner" level is even higher than any "full" account_permission_role, as "full" **access** may be only for the specified user, and may not include the ability to change account access of other users. The "owner" account_permission_role is usually only given when a user wants to change their identity, and wants to grant the new "user" (i.e. their new identity) full account control.

A user cannot grant another user greater access than it has itself. Additionally, an RP can choose to restrict the "grantAccess" ability to only the owner of the account. So only the owner of an account can change the access rights given to other users.

An RP only needs to implement 2 account_permission_roles, "owner" and "none". In other words, granting a UserId all account access, and revoking all account access from a UserId. All other account_permission_roles are optional.

An RP may choose to implement additional account_permission_roles other than "owner" and "none", or it may implement such levels through some interface other than "grantAccess". (For example, the RP can require the owner to use an html page, etc. to grant account access.)

An account MUST have at least one "owner". The grantAccess="none" command will NOT reduce access of a user if that user is the only "owner" on the account. Such an attempt will result in an error.

For example: a mother may give "read-only access" to her music account to her daughter. Such access SHOULD not allow the daughter to access credit card information or make any purchases. If the daughter performs "grantAccess", nothing will happen to the mother's account, because the daughter does not have the ability to "grantAccess" to other users of the mother's account.

Actually, sharing an account has always been possible, it just required the user to share their account password with another person or service. Which bypassed all security controls. This new way allows a person or service to use their own identity at an RP, even if they do not have an account at the RP, and be able to access another user's account. (An account they have been given permission to access.) Because each user can "login" to the RP with their own identity, the RP can implement access controls and security. It can monitor and control what user is allowed to access which account.

Doing this gives two benefits. It allows the user to change their identity. And it allows users to give other people or services partial access to their account. (Rather than giving them their password.)

The "grantAccess" API is a minor extension to the "change-user-identifier" API. To implement, change the internal user table that maps user identities to accounts in the following manner:

  1. Add an account_permission_role to each identity-account entry. (So that entries that allow a user identity to access an account also specify an account_permission_role for the identity.)
  2. Allow a single user identity to map to multiple accounts. (i.e. a user identity can access multiple accounts, if the owners of the accounts allow the user to access their accounts.)
  3. Provide some way for a user with multiple account access to select which account to use. (Some sort of account selection process.) Note that a user identity does not necessarily have to be an "owner" of any account at the RP. (The identity can belong to a "robot" that doesn't need its own account.)

Certificate Signature Verification Protocol

The Certificate Signature Verification Protocol is the method the RP should follow to get the sig verification key for a UserId certificate. (These types of certificate contain a Federated UserId.)

The RP first retrieves either the sigInfo.vkm "Certificate Signature Verification Method" value from the RML Request or the uic.csvm from the UserId certificate.

Note that the "RML FiskProfile.supported.uic.csvm" value tells the browser what CSVM values the RP supports.

The sig ver_key set are public keys owned by the Issuer of the UserId Certificate. The issuer is either the identity provider (or Backer) of the Federated UserId contained in the UserId certificate. The public key(s) are used to validate the signatures of the UserId certificate.

The "sigInfo.csvm" value tells the RP how to get the sig verification key to validate the "UserId certificate". It determines the url where the location of the sig verification key can be obtained. This is a special case of the "UserId Certificate verification protocol", where the RP already knows how to read the UserId certificate. The "csvm" value can be contained in the UserId Certificate, or it can be delivered separately. (In the req_uic.csvm parameter.)

A JWT contains an "csvm" field, which can contain the value. The RP uses the "csvm" field as a hint to discover where the sig verification key is.

The RP uses the "csvm" value in an attempt to verify the JWT in the RML Request. To do this, the RP must get a sig verification key or keys.

VKM Value List

Valid VKM values are:

In the case of a JWT, the "csvm" value can be used instead of the other means of getting the sig verification key. (i.e. instead of the "jwk", "jku", "x5c", and "x5u" header parameters.)

The "csvm" value is only "necessary" if the UserId certificate does not otherwise specify where the sig verification key should be obtained.

The "csvm" value should really be in the JWT. If the JWT does not include an alternate means of getting a sig verification key (in the "jwk", "jku", "x5c", and "x5u" header parameters), then it should include a "csvm" parameter in the JWT payload. It stands for "verification key discovery". It specifies how the RP should contact the issuer to get the verification keys for the UserId certificate. Values include "OIDCD" and "BrowserID".

If the UserId Certificate is an X.509 certificate, then the "verification key" is not needed. There is already a well-defined process for verifying X.509 certificates. The default JWT verification process should be able to verify the x.509 certificate. (Follow the certificate chain to the certificate authority. The CA.) Since the UserId Assertion is always a JWT, and the JWT verification process already has special handling for an X.509 certificate. (The JWT contains the x.509 certificate in the "x5c" header parameter.)

[jwks_uri. In a JWT ...] the JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.
From OpenID Connect Discovery
The RP may get the verification key by requesting it from a special url.

If the the value is "OIDCD" ("OpenID.Discovery"), then perform "OpenID Connect Discovery 1.0".

If the the value is "BrowserID", then perform "BrowserID".

XXXX XXXX XXXX XXXX

Possible future values for "req_uic.csvm".

For some cases, (i.e. Facebook Connect login) the user's id must be is provided to the RP in the "req_auth_id" parameter.
Note that Google, Yahoo and Microsoft all implement OpenID Connect. ALL have well-known public key urls. accounts.google.com -- "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs" login.yahoo.com -- "jwks_uri": "https://login.yahoo.com/openid/v1/certs" login.live.com -- "jwks_uri": "https://nexus.live.com/public/partner/discovery/key" Known identity namespaces. UserId Certificate verification protocols. Note that "supported.uic.csvm" has a default value of ["basic1"]. "basic1" includes "basic1" includes: "BrowserID", "RDAC", and "OIDCD" based logins. "OIDCD" includes "GoogleLogin", "WindowsLiveID", "FacebookConnect" OpenID Connect Discovery (OpenID.Discovery) is built on top of "OAUTH 2.0".

vkmc_value_list "site_stored_vks"

Use this value in the RML Request.signatureItem.vkm property to tell the RP that the sig verification key is stored at the RP.

"site_stored_vks" -- retrieve from the RP.

vkmc_value_list "certificateF"

Use this value in the RML Request.signatureItem.vkm property to tell the RP that the sig verification key is in the UserId certificate.

"idc" -- retrieve from the "UserId certificate".

vkmc_value_list "OIDCD"

Use this value in the UserId certificate.signatureItem.csvm property to tell the RP that the sig verification key can be retrieved by using OpenID.Discovery.

"OIDCD" -- retrieve from the issuer by using OpenID Connect.

vkmc_value_list "BrowserID"

Use this value in the UserId certificate.signatureItem.csvm property to tell the RP that the sig verification key can be retrieved by using BrowserID.

"BrowserID" -- retrieve from the issuer by using BrowserID.

vkmc_value_list "RDAC"

Use this value in the UserId certificate.signatureItem.csvm property to tell the RP that the sig verification key can be retrieved by using RDAC.

"RDAC" -- retrieve from the UserId. A UserId is the issuer. ("signData")

vkmc_value_list "idr_keystore"

Use this value in the UserId certificate.signatureItem.csvm property to tell the RP that the sig verification key is stored at the RP.

"idr_keystore" -- retrieve from the id keystore. (At the RP?)

vkmc_value_list "isrkey"

Use this value in the UserId certificate.signatureItem.csvm property to tell the RP that the sig verification key is stored at the ISR UserId Status Report.

"isrkey" -- retrieve from the UserId Status Report.

UserId Assertion Verification

The combination of a UserId certificate and an UserId Assertion is enough to prove a user's identity. (Proof of identity)

First, the RP checks the claims in the assertion. If the assertion is expired or intended for a different RP, it's rejected. This prevents malicious re-use of assertions. (Must be the same origin. Same scheme, domain and port.)

Second, the RP validates the signature on the assertion. It compares the signature with the public key inside the UserId certificate. If the key and signature match, the RP is assured that the browser really does possess the private key associated with the UserId Certificate.

Last, the RP verifies the UserId certificate. It fetches the IdP's public key (Possibly from its well-known document) and verifies that the IdP's public key matches the signature on the UserId Certificate. If it does, then the RP can be certain that the UserId Certificate really was issued by the domain in question (by the IDP).

Be sure to note those points again. The RP must verify that the UserId Assertion is (1) current and (2) a login attempt for itself. (3) That the UserId in the UserId Certificate matches the claimed UserId. (The one that the user is trying to log in as). (4) That the UserId Certificate and assertion are legitimate. After that, the RP is done and can authenticate the user as the identity contained in the UserId Certificate.

The UserId in the UserId certificate must match the claimed UserId. (the identity the user is trying to log in as.) If the communication from the browser to the RP contains other UserIds, all of them must match. (There MUST BE a UserId in the UserId Certificate. If the UserId Certificate is a JWT, it's the "sub" parameter in the JWT payload.) A UserId may be supplied as a separate parameter to the RP as part of the HTTP request. (Is there a UserId in the UserId Assertion? A "sub" claim in the JWT payload? If there is, it must match too.)

Certificate Verification Steps

First, verify the claims in the certificate. That it is not expired, and that the subject matches the identity in the assertion. (Otherwise it's the wrong certificate.)

Check to make sure the certificate contains a public key. If there isn't one, then validating the certificate isn't going to do any good. The public key is needed to verify the UserId Assertion. (In a JWT, the public key is located in the "pubkey" field in the JWT payload.)

Second, verify the certificate's signature. To do this, the sig verification key must be obtained. The certificate may contain information about where teo retrieve the key.

If the "hint" needs to be used, then perform the "Certificate Signature Verification Protocol" to look up the key set. All the keys in the set may be tried. Or, if the certificate contains some sort of "kid" field, then the list of keys can be filtered to only the key with the correct kid.

If there is more than one Identity Certificate in the chain, then reject the certificate unless each certificate after the first one is properly signed by the prior certificate's public key.

If the first certificate in the chain (or only certificate when there is only one) is not properly signed by the expected issuer's public key, reject the certificate. The expected issuer is the IDP of the identity in the last certificate. (or the issuer listed in the first certificate if the email-address domain does not support BrowserID.??)

JWT Validation

The UserId certificate sent to the RP is probably a JWT. (Although other values are allowed.) This is a detailed walkthrough of how to validate the UserId Assertion, if it is a JWT.

The UserId certificate is sent in the "req_ic" HTTP parameter. The verification process uses both the "req_ic_type" and "req_uic.csvm" parameters. The "req_ic_type" parameter must be "JWT" (or not exist, since "JWT" is the default) for this function to be called.

The RP must get the sig verification key from the issuer, and use that to verify the UserId certificate.

==== When validating a JWT, implementations should be careful to NOT allow "none" as the value of the "alg" field in the header. In fact, implementations should NOT use even look at the "alg" field in the header. Implementations should use the "kid" field in the header instead. Use the key ID to look up the key and use its corresponding algorithm. Since the key needs to be looked up anyway in order to validate the JWT. In this manner, attackers are no longer able to control the manner in which a key is used for verification. (They can set the "alg" field, but implementations should not be looking at the "alg" field.) ====

The UserId Assertion JWT provided to the RP from the browser MUST be signed by a public key certified by the IDP. This is, the public key that was generated by the browser and certified by the IDP (or ID broker). This key is in the UserId certificate. This is typically a JWT. (However, if it is an X.509 certificate chain, then it could be stored in the UserId Assertion. In the "x5c" header parameter.)

If the UserId certificate is an X.509 certificate, then X.509 certificate contains a public key. The X.509 certificate proves that the IDP has approved that public key to have access to the specified identity.

If the "UserId certificate" is a JWT, then it contains a "pubkey" field in the JWT payload, that contains the public key.

navigator.id.auth.verify.extractPublicKeyFromX509Certificate(jwt_x5c_arg); navigator.id.auth.verify.extractPublicKeyFromJWT() navigator.id.auth.verify.verifyIdentityAssertionJWT(jwt_compact_form) navigator.id.auth.verify.verifyCertificateJWT(jwt_compact_form) Typical steps to verify a UserId certificate. (From "req_ic"). 1. Create an empty set of keys. 2. Decode the JWT header. 3. Get the "crit" value. Ensure that there are no parameters that are not understood. A JWT with unknown "crit" parameters MUST be rejected as invalid. 4. Get the "jwk" value. Add the key to the key set. 5. Get the "jku" value. Perform a url lookup, add the keys to the key set. Verify the domain name in the url and the issuer are the same. Retrieve the public key document from the issuer. 6. Get the "x5c" value. Perform a certificate chain verification. This will confirm that the public key in the certificate is used by the issuer. (Will check the certificate authority, CA, for the issuer's public key.) If valid, add the key in the certificate chain to the key set. 7. Get the "x5u" value. Perform a url lookup, validate it. If valid, add the key to the set. 8. Get the "csvm" field in the JWT PAYLOAD, or use the "req_uic.csvm" value. Use the "Certificate Signature Verification Protocol" to look up the keys. Add all keys to the key set. 9. Get the "kid" value. Use this value to filter the keys. If the "kid" value does not exist, then ignore. Don't filter anything. 10. Try the keys, one at a time. Filter out keys that do not have the same "kid". (If there is a "kid" value in the JWT.) 11. If the signature matches. Extract the "pubkey" value from the JWT PAYLOAD.
For the Certificate Signature Verification Protocol Use the "csvm" (or "req_uic.csvm") value to determine the appropriate steps. Combine the issuer url and the new path. Get the document at the "discovered" url. If the the "csvm" value is "OIDCD" (OpenID.Discovery), then perform "OpenID Connect Discovery 1.0".
  • Use the "well-known" path "/.well-known/openid-configuration".
  • Combine the issuer url and the new path.
    (Example: "https://issuer_url/.well-known/openid-configuration").
  • Get the document at the "discovered" url.
  • Extract the "jwks_uri" property.
  • Get the document from the specified JWK endpoint. (Ex: "https://issuer_url/keys.json")
  • Extract the JWK key set from the document.
  • Get the "kid" value (from the JWT header)
  • Extract the public key in the key set that matches the "kid". That is the JWK used to sign the JWT.
If the the "csvm" value is "BrowserID", then perform "BrowserID". Use the "well-known" path "/.well-known/browserid". Combine the issuer url and the new path. (Example: "https://issuer_url/.well-known/browserid"). Get the document at the "discovered" url. Extract the "public-key" property. This is the JWK used to sign the JWT.

Use Digital Signatures

In order to sign in, an RP must receive proof that the user is who he claims to be. The simple way to do this is for the user to provide to the website a username and shared secret (i.e. a password). The upgrade to this is to provide a username and a digital signature. The website stores a public key for each user. Instead of storing a password. (Or it could store a "thumbprint" for a public key instead of the full public key.) When logging in, the website compares the digital signature to the public key stored for the specified user, in order to determine if the user is who they claim to be.

"Proof of identity" is sent to RPs instead of passwords because it's more secure and more capable. First, using this type of proof eliminates the storing of secrets by RPs. The proof consists of a UserId and one or more digital signatures. The RP typically stores some sort of "verification codes" (e.g. usernames and passwords) to verify its users, but with digital signatures all the stored items are not secret. Things stored by the RP include the UserId, any cached public keys (of an IDP, broker, etc.), and any public keys stored for users. (An RP can choose to store public keys for users in addition to an identity as a second means of authentication.) It almost doesn't matter if the website gets hacked, because there is nothing to steal. The public keys are already public.

This is a huge benefit, as it eliminates the problem of RPs having to securely store anything (i.e. passwords). The prevention and elimination of security leaks is the primary reason for using "proof" instead of passwords.

Second and third, the "proof" sent to RPs is customizable, and can be made to expire. To login, each RP is given a "proof of identity" customized just for them, and the proof expires after a few minutes. Because of the customization, the "proof of identity" given to one RP cannot be used on any other RP, even if the different proofs all use the same signing key.

The downside to using "proof" with the RPs is that the "proof of identity" is more difficult for people to type into a web form. Hence, the need for this proposal.

FiskCatalog Document: retrieval_considerations

The FiskCatalog document must be retrieved using an anonymous fetch. That is, the HTTP GET used to retrieve the document MUST not have any cookies, authentication, Origin or Referrer headers, and must not use any TLS certificates or other credentials. The response delivering the document MUST have a MIME Content-Type of "application/json". (This is for the appId.)

Some points to consider. Copied from the FIDO AppID authorization.

Remere Login UA Method - Value: "without_MSUID" Detail

When using "without_MSUID" during login, if a Locked Id is used in the RML Request, then the "sigInfo.vkm" value for the certPK signatures should be "site_stored_vks".

This method can only be used if the RP stores some kind of "Credential Verification Rule" (e.g. a public key, password, etc.) for the user. This method also requires the user to store a Credential Source for the RP in his Login Manager. (A password or private key, etc.)

Example FiskCatalog:
{ "serviceProfileList": [ { "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": {"id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID", "without_MSUID"]} }, "requirement_section": {} } ] }
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login" }'>Login</a>
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "login", "sub": "remere/login", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "abcdefg", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "encReqValue": "xxxxxxxx", // "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId "encForLogin": "q3jC9-h" // The UserId is necessary, unless the service request uses a "UserId certificate". }, "user_auth_method": "without_MSUID", "redirectUri": "/some/path/page.html", /* The user_id_certificate_chain is absent when using the "stored_cred" uauth_method. // user_id_certificate_chain "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], */ "useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "JWT", // "format": "JWT" (not necessary. The default is "JWT".) "val_p": "mmmm" // This is the encoded JWT, see below. } ] }, /* "credRegSec" credentialRegSection. Not required for "login" opId. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "bupk": "qis:A:H:2:mK7HnbndYb746GvnYkrU", // The Backup User Public Key ("backup_upk") "bupk2": "none" } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "alg": "ES256", "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } */ };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256"} "header": { // "kid": "userIdPK" // "userIdPK" means the public key is in "userId". "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", "vkm": "site_stored_vks" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] } };

Remere Login UA Method - Value: "only_MSUID" Detail

The RP will do user authentication without using the Credential Verification Rules that it stores for the user. If the UserId is a Federated UserId then it can only perform user authentication if it receives a UserId certificate. (This process will prove that the user "owns" the UserId.)

In this case, the UserId Assertion in the RML Request should use a "vkm" value of "certificateF".

Example FiskCatalog:
{ "serviceProfileList": [ { "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": {"id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]} }, "requirement_section": {} } ] }
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "login", "sub": "remere/login", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "abcdefg", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "encReqValue": "xxxxxxxx", "rawForLogin": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId "encForLogin": "q3jC9-h" }, "user_auth_method": "only_MSUID", "redirectUri": "/some/path/page.html", // user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], "useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "JWT", // "format": "JWT" (not necessary. The default is "JWT".) "val_p": "mmmm" // This is the encoded JWT, see below. } ] }, /* "credRegSec" credentialRegSection. Not required for "login" opId. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "bupk": "qis:A:H:2:mK7HnbndYb746GvnYkrU", // The Backup User Public Key ("backup_upk") "bupk2": "none" } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "alg": "ES256", "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } */ };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256"} "header": { // "kid": "userIdPK" // "userIdPK" means the public key is in "userId". "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", "vkm": "certificateF" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISApmWQxfKTUJqPP3-Kg6NU1Q" }] } };
Example Certificate:

The "UserId certificate" JWT. header and claims: (uic_chain[0])

let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, "exp": 1533064012, "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "cert_pubKeyList": [ { "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b3c34...", "e" : "93bc32..." } ] };
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. "csvm": "idr_keystore" // Get the sig verification key from an "identity registrar keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

Login Creation Method: MSUID_acm

A simple way for a user to create a User Login at an RP, where the user provides a Locked Id and proof of ownership of the identifier. This skips the hardest part of traditional account creation, having the user find (and remember) an unused Single-site UserId.

With the traditional means of a username and password, creating an account at an RP requires the user to find an unused username at the RP. When using a Multi-site UserId, the user does not have this problem as the "UserId" is reserved for his exclusive use, at every RP.

When using a Multi-site UserId, an account can be created automatically, without user intervention or negotiation with the RP. This is because an account username does not need to be negotiated with the RP in order to create an account. Unlike when using a Single-site UserId. (Such as the username in a regular username and password.) All RPs restrict use of a Multi-site UserId to only the party that can provide proof of ownership. Thus, a Multi-site UserId functions as a "reserved username" at all RPs. Before the RP will register the Locked Id to the user, it requires that the user unlock the ID and create a "proof of identity". (i.e. No "negotiation" with the RP is required to obtain a username. "Negotiation" being the opId of repeatedly querying an RP to find out if a desired username is available at the RP, or if it is already used.)

The Multi-site UserId MUST BE VERIFIED before account creation. An RP MUST NOT create an account for a Multi-site UserId without a verified proof of identity. An RP MUST NOT allow someone who does not own the identifier to create an account using it. (It is a security risk)

Note that an RP SHOULD NOT automatically create an account for identities in response to a login RML Request. (i.e. When a user tries to login with a Locked Id, the UserId is VERIFIED, but the RP does not have an account for that UserId.) Instead, confirmation from the user is required before account creation. (To prevent account creation due to user error. If the user accidentally selected the wrong User Login to log in with, etc.)

When creating an account, the value of the "RmlFiskProfile.userLoginCreationRules.userIdTypes" property limits the type of identifier that can be used to create an account. (e.g. "single_site_userId", "qis:A:H:9:ri", etc.) When using a Locked Id (using the "RmlFiskProfile.userLoginCreationRules" "createMethods" value of "claim_ms_ulc"), user authentication is done. This is the regular "User_id" authentication. Thus, the value of "RmlFiskProfile.userLoginCreationRules.userIdTypes" is used to verify the Multi-site UserId.

A Locked Id can be used to create an account with or without an additional Credential Verification Rule (i.e. a public key) being stored at the RP. (A CVR public key. Not a public key used in the UserId.) This option depends on what the RP supports. In order to create an account with an additional public key (i.e. CVR), the RP must declare that it can store public keys. It does this with the "credentialReg property. ("credentialReg.publicKeyTypes")

Example FiskCatalog:
{ "serviceProfileList": [ { "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": {"id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"] // "createMethods": ["assigned_ulc", "claim_ms_ulc"] // "assigned_ulc" is the default methods value. }, "credentialReg": { "ruleTypes": {"pubkey1":"1"}, "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}], "required_ruleTypes": {"pubkey1":"1"} }, "supported": { "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "uic.csvm": ["basic1", "basic2"] } }, "requirement_section": { } } ] }
Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/registerUserLogin" }'> <input type="text" name="firstname" placeholder="Enter your first name"> <input type="text" name="lastname" placeholder="Enter your last name"> <input type="text" name="user_email" placeholder="Enter your email"> <input type="submit" value="Submit"> </form>
Example JSON:
firstname=Mickey
lastname=Mouse
user_email=john@example.com
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "registerUserLogin", "sub": "remere/registerUserLogin", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "abcdefg", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k", // The QIF UserId "timestamp_uid": "q3jC9-h" }, // "user_auth_method": "combo_MSUID", "user_login_creation_method": "claim_ms_ulc", "redirectUri": "/some/path/page.html", // user_id_certificate_chain (Available when not using the "stored_cred" uauth_method.) "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], "useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "JWT", // "format": "JWT" (not necessary. The default is "JWT".) "val_p": "mmmm" // This is the encoded JWT, see below. } ], }, // "credRegSec" credentialRegSection. Required for opId="registerUserLogin" and "setActiveCredentials" actions. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "bupk": "qis:A:H:2:mK7HnbndYb746GvnYkrU", // The Backup User Public Key ("backup_upk") "bupk2": "none" } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "alg": "ES256", "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256"} "header": { "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d", // "kid": "userIdPK" // "userIdPK" means the public key is in "userId". "vkm": "certificateF" }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] } };
Example Certificate:

The "UserId certificate" JWT. header and claims: (uic_chain[0])

let payload_unencoded = { "sub2": "remere/certificate", "iss": "id_registrar.example.com", // The Backer "iat": 1503971280, // REQUIRED "exp": 1503971430, // REQUIRED "sub": "qis:A:F1:1:ACMA_org:M4j-q", // The QIF UserId. "uid": { "rawForLogin": "qis:A:H:2:K8h3_TfXr6ZtLq7d1m8k", // The QIF UserId }, "cert_pubKeyList": [ { "pkf": "JWK", "kty": "RSA", "kid": "f85762a7f52c8346", "alg": "RS256", "n" : "4b9e34...", "e" : "93bc32..." } ] };
let jws_certificate = { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [ { "protected": "eyJhbGciOiJSUzI1NiJ9", // protected_header = {"alg":"RS256", "sct":192} "header": { "alg": "RS256", "kid": "bf4a76332a6c8370f48", // "kid" is REQUIRED. ID of the issuer key. // "csvm": "OIDCD" // Get the sig verification key from the issuer via "OIDCD", etc. "csvm": "idr_keystore" // Get the sig verification key from an "identity registrar keystore". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] };

Login Creation Method: SSUID_acm

This is a way to create a User Login at an RP with a Single-site UserId. (As opposed to a Locked Id.) To do this, the browser must negotiate a "Single-site UserId" with the RP. It must find a Single-site UserId at the RP that is not already in use. After the UserId is decided upon, the browser will send the RP an account creation service request and include in it the desired UserId and some Credential Verification Rule (CVR) (e.g. a public key, password, etc.). The RP will then store the CVR (e.g. public key) in the user's account.

After account creation, the user will login with the stored_cred user authentication method. So the RP must include "single_site_userId" as a RmlFiskProfile.userLoginCreationRules.userIdTypes. The RP will receive a service request that includes a Single-site UserId and a "proof of identity". It will look up the account for the claimed UserId, and use the CVR associated with it to authenticate the proof of identity. (Note that the account creation service request would send a public key to the RP, not a key signature.)

When using the "assigned_ulc" account creation method, the browser must include the desired UserId in the service request to the RP. The RP can direct the browser how to choose a Single-site UserId by using one of the Configuration Settings. Probably one of the "username" settings. Either ulCreationCfg or triggerPropFieldNameList.

To use the "assigned_ulc" method, the RP must also support using a "single_site_userId" for authentication. (It must have "single_site_userId" as an option for the "RmlFiskProfile.userLoginCreationRules.userIdTypes" property.) There is no reason to create a "Single-site UserId" when it can't be used to login.

Note that the "stored_cred" User Authentication Method cannot be used to create an account. Creating a "assigned_ulc" account requires two different features. (1) Selection of a username. (2) Sending a CVR (e.g. a public key) to the RP. The RP must store the CVR.

The "SSUID User Login Creation Method" is not the default. In order for the browser to use it, the RP must declare support for it. The "RmlFiskProfile.userLoginCreationRules" "createMethods" property must include the "assigned_ulc" option. (Example: "RmlFiskProfile.userLoginCreationRules":{"createMethods":["assigned_ulc", "claim_ms_ulc"]} ).

When creating an account, the value of the "RmlFiskProfile.userLoginCreationRules.userIdTypes" property limits the type of identifiers that can be used to create an account.

When creating an account using the "claim_ms_ulc" option, the regular "User Identity" ownership authentication is done, and the value of "RmlFiskProfile.userLoginCreationRules.userIdTypes" is used. In this manner, the "RmlFiskProfile.userLoginCreationRules.userIdTypes" limits what type of identifier can be used to create an account. (e.g. "single_site_userId", "qis:A:H:9:ri", etc.) However, when creating an account using the "assigned_ulc" option, the RP does not do any user authentication. The value of "RmlFiskProfile.userLoginCreationRules.userIdTypes" is not used to verify ownership of a Locked Id. The JWT sent to the RP is unsigned. So, in this case, a special check has to be put in, to ensure the RP supports "single_site_userId". The "RmlFiskProfile.userLoginCreationRules.userIdTypes" must include "single_site_userId". As there is no reason to create a "Single-site UserId" when it can't be used to login.

Selecting a username at the RP requires one of:

  1. The suggestion of a username by the RP.
  2. Interactivity with the RP. Where the user can check on the status of a username, to see if it is already in use at the RP.
  3. Use of a reserved username. Like a Locked Id. Multi-site UserIda are reserved at all RPs. RPs should ONLY allow the use of such an identifier by a person that can prove ownership of it. (See identity detail.)

Note that the RP declares what type of Credential Verification Rule (CVR) it can store for users by setting the "credentialReg" property. Also, the default value is "publicKeyTypes":[{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}], so even if the "credentialReg.ruleTypes" property is missing, creating an account will work. However, if the RP does NOT store any CVR (i.e. "credentialReg.ruleTypes":{}), then the "assigned_ulc" account creation method cannot be used. As the RP cannot store ANY "Credential Verification Rule" (e.g. public keys). In this case, one of the other opId="registerUserLogin" createMethods must be used.

Example RML FiskCatalog:
{ "serviceProfileList": [ { "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": {"id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json"}, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "userLoginCreationRules": { "userIdTypes": ["single_site_userId", "qis:A:H:9:ri"], "createMethods": ["assigned_ulc", "claim_ms_ulc"] }, "credentialReg": { "passwordTypes": [{"name":"password_s1", "maxLength":14, "storageQuantity":"1"}], "publicKeyTypes": [{"name":"pubkey_s1", "JWA_alg":"ES256", "storageQuantity":"1"}] }, "supported": { "userAuthScheme": {"methods":["stored_cred", "only_MSUID"]}, "factor2": ["pubkey"] }, "requirement_section": {} } ] }
Example HTML Form:
<form ... rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/registerUserLogin", "triggerPropFieldNameList":["input27_userIdInfo"] }'> Please enter a username and click the "check" button to see if it is already taken. <!-- Javascript should be attached to the "check" button, to dynamically query if the username exists. --> <input id="input27_userIdInfo" type="text" name="ulCreationCfg" placeholder="{ Enter JSON. }"/> <input type="button" value="Check if username is taken"/> <br/>When you are finished selecting a username, press the "Create Account" button. Your browser will then prompt you to create and store a password. (Or a public key) <br/><input type="submit" value="Create Account"/> </form>
Example HTML Form:

Working example

Please enter a username and click the "check" button to see if it is already taken.

When you are finished selecting a username, press the "Create Account" button. Your browser will then prompt you to create and store a password. (Or a public key)

When the opId="registerUserLogin" form is submitted, the "auth" click event listener catches the submission and asks the user to generate a public key to use with the website. auth.invokeServiceFromTriggerHtmlElement(formElement, event) --

Example HTTP values:
username_choice="qis:A:A:2:M4j-q:XSAErLq"
capis_request=
Example RML Request payload:
let payload_unencoded = { "requestFormat": "remereRequestFormat 0.1", "protocolId: "remere", "opIdReq": "registerUserLogin", "sub": "remere/registerUserLogin", "iat": 1503971280, // issuedAt timestamp "exp": 1533064012, // expiresAt timestamp "aud": "RP_example.com", // the audience "challengeKey": "abcdefg", // nonce value "cht": 1533064012, // challengeTimestamp "uid": { "rawForLogin": "qis:A:A:2:M4j-q:XSAErLq", // The QIF UserId "timestamp_uid": "q3jC9-h" }, // "user_auth_method": "stored_cred", "user_login_creation_method": "assigned_ulc", "redirectUri": "/some/path/page.html", /* The user_id_certificate_chain is absent when using the "none" uauth_method. // user_id_certificate_chain "uic_chain": [ {"uic.format":"JWS", "uic.csvm":"YY", "ucv": { "payload":"82h7vb...", "signatures": [...] }} ], */ "useCount": 42, // crProofSection. "crProofSection": { "uid": { "raw2": "qis:A:H:2:xxxxxxxx", // The QIF UserId }, "uapCounter": 42, "cpl": [ { "cid_p": "cd2", "typ_p": "pubkey", "fmt_p": "JWT", // "format": "JWT" (not necessary. The default is "JWT".) "val_p": "mmmm" // This is the encoded JWT, see below. }, { "cid_p": "cd3", "typ_p": "pubkey", "fmt_p": "JWT", "val_p": "mmmm" } ], }, // "credRegSec" credentialRegSection. Required for opId="registerUserLogin" and "setActiveCredentials" actions. "credRegSec": { "backup_credentials": [ { "cid_r": "cd-3", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "bupk": "qis:A:H:2:mK7HnbndYb746GvnYkrU", // The Backup User Public Key ("backup_upk") "bupk2": "none" } ], "crList": [ { "cid_r": "cd10", "typ_r": "pubkey", "fmt_r": "JWK", "coseAlgId": -7, "val_r": { "kty": "EC", "kid": "cd10", // "kid" is REQUIRED. Same as the "cid_r". "alg": "ES256", "crv": "P-256", "ext":true, "key_ops": ["verify"], "x": "5sy3ogiqTrlC_FF7aOdCq7cNFsN2nbrH4xQmNCCBtso", "y": "ctK7P_LU-SQPf-ejkmC1JYzm7S5N0B6ZX5rwOQWzuv8" } } ] } };
Example RML Request:
let rml_request = { "JWS": { "payload": BASE64URL(UTF8(payload_unencoded)), "signatures": [{ "protected": "eyJhbGciOiJFUzI1NiJ9", // protected_header = {"alg":"ES256"} "header": { "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d" // "kid": "userIdPK" // "userIdPK" means the public key is in "userId". }, "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4Ga" }] } };

APPENDICES

========

APPENDIX A - Attribution

This specification is largely based upon BrowserID, and Mozilla Persona, which used it. However, those proposals had a number of problems. This proposal differs from those in a few areas.

First, this specification is more limited in scope, yet deeper in its implementation. It only defines the communication between the browser and the RP, but it does so in three ways. It defines a declarative HTML API. (i.e. How the RP can advertise to the browser what types of identities and protocols it supports.) It defines a method of automatically adding items to a browser HTTP request. (i.e. How the browser can send the "proof of identity" to the RP.) And it defines a JavaScript API for the RP to use. (i.e. How the browser is supposed to respond to the declarative HTML API.)

The process by which the RP must verify the proof. Including both the UserId Assertion and the "UserId certificate". And it defines a set of JavaScript API functions that the RP web page can call.

Second, this specification allows for different levels of abilities in RPs. It defines a way for browsers and RPs to negotiate between themselves. RPs can publish their capabilities and requirements in HTML attributes, and browsers can adjust their behavior accordingly. Some RPs may be more capable than others, and may define additional features.

Third, this specification doesn't attempt to specify what protocol the IDP uses, any more than absolutely necessary. In fact, the IDP and its communications are intentionally as loosely defined as possible. The format of the UserId certificate, and the communication between the IDP and the browser are undefined. There are multiple ways for the RP to get the IDP list of signature verification keys. And the way the verification key discovery protocol is communicated to the RP can be outside the IDP.

This specification does not define how the browser obtains or creates an "UserId certificate". It does not define how the browser communicates with the IDP. (It intentionally does not define exactly what form an UserId Certificate must be.)

Fourth, this specification is extensible. There is a mechanism for adding additional IDP protocols, additional "RML Request" Properties and HTML attributes. The additional fields to hold type information are already a part of the specification (uic.format, uic.csvm, "supported.uic.csvm", etc.) Additional type values can be added.

Third, this specification offers a wider variety of identities.

========

APPENDIX B - What is an identity?

An "identity" is the combination of a unique user identifier (i.e. a UserId) and some method of proving ownership of the identifier. Authorized use authorized user. (Some sort of private and public key, verification by an IDP, etc.) In order to prove ownership, the UserId must have a set of Credential Verification Rule bound to it in some manner.

In order to use the user identifier at more than one RP, the identifier must be universally unique. This likely means that the identifier include the ID of the issuing authority. (i.e. the Backer Organization) Also, there must be a way for RP to verify that a user can prove ownership of the identifier. This requires either that the Credential Verification Rules be public, so that the RP can use them, or that the RP use some sort of trusted "service" to verify user ownership. This is a Locked Id. The user must prove that they can Unlock the ID.

It turns out that an "identity" is almost the same thing as a "User Login".

The QIF specification defines a format for writing an identity. Using an identity "namespace" gives more options, as each namespace can define its own authentication protocols.

Format qis:NID:idClass:subClass:payload
Examples:
  • "qis:A:A:1:M4j-q" // Type "A:A:1" contains a base64url userNum.
  • "qis:A:A:2:M4j-q:XSAErLq" // Type "A:A:2" contains a base64url timestamp-id.
  • "qis:A:F1:1:ACMA_org:M4j-q" // Type "A:F1:1" contains a type-C PermanentBackerId and a base64url userNum.
  • "qis:A:F1:2:ACMA_org:M4j-q:XSAErLq" // Type "A:F1:2" contains a type-C PermanentBackerId, a base64url userNum and a timestamp-id.
  • "qis:A:F2:1:example.com:M4j-q" // Type "A:F2:1" contains a domain name and a base64url userNum.
  • "qis:A:F2:2:example.com:M4j-q:XSAErLq" // Type "A:F2:2" contains a domain name, a base64url userNum, and a timestamp-id.
  • "qis:Z9:uri:mydomain.com/somepage.html" // Type "uri". Payload is a URI. (uses https)
  • "qis:Z9:device:gno.mn~cccccwhgyes" // Type "device". "payload" is an IDP uri and a device id. The IDP requires signed input from a key-fob One-time password generator with id "cccccwhgyes".

The "Z9" namespace is reserved for examples. (Like "example.com".)

A namespace gives extra options. For example, an identity with namespace:"D2" idClass:"email" can be verified as an account at the issuer (the IDP), through the actual email protocol (Sending a passcode to the email address), through Oauth, BrowserID, etc.

Note that a simple "username" at an RP is not a true identity, for 2 reasons. (1) Because the UserId is not a universally unique identifier. And (2) Because there is no "associated" public means of proving ownership of the identifier. (i.e. A LIAV Method.)

The identifier (UserId) used in the QIF specification consist of four parts, a "format-prefix" (i.e. "qis:"), a "namespace_id", "idClass", and a "payload". These are combined together with the colon character ": " . The "payload" may contain whatever characters are allowed by the namespace and type combination. Certain namespace and idClass combinations have the payload contain an identity provider domain name. (Like an email address. The part after the '@' symbol.) The "format-prefix" clearly labels the identity, and denotes which conventions it follows. The "namespace" defines the standards body that governs that QIF identity. (It is the organization that controls the rules and formats of QIF identifiers that use that namespace. It also identifies the format of the rest of the identifier (the payload), and the means how ownership can be verified. The "namespace" may ONLY contain alphanumeric characters and hyphens "-". The "payload" is the contents of the identifier. Note that the payload MAY contain additional colon characters. The idClass must follow the rules of the namespace. The payload must follow the rules of the idClass. All payload must be assigned in a manner such that there can be no duplicates.

// a "Facebook Connect 1.0" identity.

Note: The entire QIF identifier (namespace, idClass, subClass and payload) may NOT contain double quotes. The namespace may NOT contain the special character for concatenation, the colon character ":", or the tilde character "~".

The identity "namespace" defines the category of the identity. It should also define at least one protocol. i.e. How the browser can contact the IDP to obtain the UserId certificate. However, alternate protocols and implementations are allowed. A browser can innovate. For example, there can be multiple protocols to contact an "email address" IDP to obtain an UserId Certificate. A browser can attempt to contact the email provider and use it as the IDP (via BrowserID, openAuth, etc.), or it could use an ID broker instead, or it could send a passcode in an email to an email address. It may depend on what the email provider supports. The verification the RP must perform is determined by what protocol the IDP uses. (OpenID.Discovery, BrowserID, etc.)

The identity namespace should be a short string, as it has to be used as a switch in many other places.

When stored in a browser, an identity can have several additional properties. Besides namespace, idClass and payload, it may possibly contain: a user created "display_name", a list of IDPs, an IDP connection protocol. The display name is so that users can give their ids more descriptive names.

Written as JSON, it might look like this: {"format":"QIF", namespace:"A1", idClass:"ri", "payload":"c0024d93", "IDP_protocol":"Facebook Connect 1.0", "display_name":"Roy's super Facebook Login Book", "identity-change-publickey":"jhdfskhf983y4weurhdsf98hsfs"}

The JSON can include a list of the "Credential Verification Rule". {"format":"QIF", namespace:"A1", idClass:"ri", payload:"c0024d93", auth_criteria: [{"idClass":"public-key", "value":"bf3a8c49178de274f8a61c"}, {"idClass":"potato", "value":"hjghkjdfhu"}] }

* The "Credential Verification Rule" of the user identity can be changed by the id owner. The IDP can issue a new "UserId certificate". etc. The IDP can also revoke the identity. (list it as compromised.) * An RP can publish HOW MANY, and what type of Credential Verification Rule it requires. In its "credentialReg" property. The identity "namespace" defines how the Credential Verification Rule (CVR) are bound to the user identifier (UserId). It defines how to verify the "UserId certificate", and the types and number of CVR the identity can contain. The protocol has two parts, how to connect to the IDP and get the UserId certificate, and how the UserId Certificate can be verified. The browser can have a few customizations for each identity. 1. A display name. 2. Which IDP to connect to. 3. What protocol to use with the IDP, How to acquire a UserId certificate. 4. What protocol to use when verifying the UserId certificate. (limited set. The RP must support it.) The identity namespace specifies: 4. The default set of protocols for verifying an UserId certificate. That is, the way the RP website has of checking the validity of an UserId Certificate. Note that the user will likely never see the combined identifier as in the examples. The browser will likely have three separate boxes for entering identifiers. A text box to enter the unique identifier, a drop down listing various identifier namespace strings (user-identifier-types) supported by the browser, and a second text box to give the identity a name. (Also, the browser can import an identity directly in the url via a JavaScript directive. So after creating an identity, the user can be automatically redirected and the new identity will be added to their browser.) The browser implementation must know the names for the protocol parts. For Example: { "protocol":"Facebook Connect 1.0", "token-creation":"Oauth Facebook.com", "token-validation_icvp":"X.509 certificate"} Note that, all RP websites MUST understand at least JWT. Both JWS and JWE. This is so that the RP is capable of validating all the types of UserId Certificate that those standards support. "alg" - Algorithm "jku" - A URL with a set of keys "jwk" - JSON Web Key "kid" - Key ID "x5u" - X.509 URL "x5c" - X.509 Certificate Chain "x5t" - X.509 Certificate SHA-1 Thumbprint "x5c#S256" - X.509 Certificate SHA-256 Thumbprint This specification splits the proof of identity into two parts. The UserId certificate, and the UserId Assertion. That is, the communication from the IDP to the browser, and the communication from the browser to RP websites. The UserId Certificate is not specified. The UserId Assertion is a JWT. This allows the specification to work for almost any protocol. As long as the ID certificate can authorize a public key, and the ID certificate can be verified. It will work. The browser visits the IDP in some manner and the IDP gives it an ID certificate. This authorizes the browser. (It actually authorizes a public key, of which the browser has the related private key.) Then the browser uses the private key to authorize new UserId Assertions and send them to RP websites. The RP websites need a way to check the validity of the ID certificate. The RP websites state which IDP verification protocols they support inside the "RmlFiskProfile.userLoginCreationRules.userIdTypes" and "supported.uic.csvm" properties. The internal browser implementation will need to be updated to add support for different protocols. (The function user_auth.acquireNewUserIdCertificate() will need to be updated.) Such support could be implemented by simply making a REST API call. Note that a "UserId certificate" does not have to come from an online IDP. An ID certificate can be supplied via a USB key, etc. Or a token or private key can be stored internally to the browser.
========

Appendix C - Original Mozilla Persona discussion

The following is a (heavily edited) excerpt from a Mozilla Persona discussion board, giving the basis for a Declarative HTML API. Mozilla Persona Goldilocks: Declarative HTML API
... it seems like a small skip to support an fully HTML declarative API, as suggested in this issue[2] by @eevee. ## How There becomes potentially 3 ways to integrate with Persona this way. 1. HTML only 2. HTML + shim 3. call through JavaScript ### HTML only (when JavaScript is disabled) An RP can express Persona support in HTML by including an anchor tag with a rel="userauth4" attribute. Additional options can be included as data-* attributes. The href is considered the returnTo (and postBack, we'll come back to that). Example:
<a href="/_capis/remere/requestOp.json" rel="userauth4" data-siteLogo="/images/logo.png" data-siteName="123done">Login</a>
COMMENT: For browsers that DO support "userauth4" anchors, when a "userauth4" link is clicked, the browser will capture the click, read the anchor's attributes (i.e. "data-siteName") and do some sort of custom login for the user. The browser will generate an assertion, and then send the assertion in a POST to the href of the anchor. The RP will verify the assertion, and then redirect the user's browser to another page on the RP website.
COMMENT: For browsers that do NOT support "userauth4" anchors, when a "userauth4" anchor is clicked, the browser will send a GET request to the href of the anchor. (This is a GET, not a POST. And it will not contain an assertion.) The RP will receive the GET request, and then have to do "something else" to log the user in. The easiest solution is for the RP to "redirect" the request to the Persona login page. The "redirect URI" should include some parameters, such as a "returnTo" address. The Persona login page will generate an assertion, then send the assertion in a POST to the "returnTo" address.
For browsers that do NOT have support for "userauth4" anchors, This will also require a bit of server-side scripting to make it work. Specifically, if a user clicks the link, the browser will send a GET request to the anchor's href. The RP would do a redirect to: login.persona.org/sign_in?origin=123done.org &returnTo=/_capis/remere/requestOp.json&siteName=123done The Persona login page would handle the request, and upon completion of authentication, it would POST the assertion to the returnTo address. This means the RP would need to also handle a POST to "/_capis/remere/requestOp.json" (in this case) to accept an assertion and verify it, and then redirect the browser to wherever else they want on their site.
COMMENT: When the RP creates a URI to the Persona login page, it should include several parameters in the URI, including "origin", "siteName" and "returnTo". These parameters tell the Persona login page what to do and what to display. The "returnTo" URI value specifies where to send the the assertion (as a POST request). The easiest URI to use for the "returnTo" value is the same address as in the "userauth4" anchor's href value.
COMMENT: The RP will receive both GET and POST requests at the same URI. GET requests from browsers that do not support "userauth4" anchors, POST requests from browsers that do. And POST requests from the Persona login page. (If the RP chooses to use the same URI as the "returnTo" value.)
COMMENT: The values in the HTML attributes (i.e. "data-siteName") are only used in browsers that DO support "userauth4" anchors. For browsers that do NOT have support for "userauth4" anchors, the values in the HTML attributes are NOT sent to the RP in the GET request. (The HTML attribute values are not contained in the href URI of the anchor, and the RP doesn't need them anyway. Since it created them.)
The POST part shouldn't actually be more work for the RP, since they already would need an endpoint to verify the assertion. However, whereas when using the Javascript API the POST would come from the RP's own AJAX, they could protect their endpoint from CSRF. To stay protected, they'd need to pass a csrf token in the returnTo if redirecting from their auth URL to the Persona login page (The Persona dialog), and the Persona login would simply POST it back without doing anything, since Persona would POST to their returnTo.
COMMENT: CSRF is (Cross-Site-Request-Forgery). A csrf token is a "nonce" value. It is implemented as the "challengeKey" value.
If the browser natively supports Persona, it can look for these kinds of anchors (i.e. rel="servicetrigger"), and attach to them. Even without JavaScript enabled, the browser can still do whatever it wants, and can open a door hanger or trusted UI, do Persona, and then automatically POST to the href of the anchor. #### Benefits This means Persona works without JavaScript! Well, sort of. But, again as @eevee pointed out, the redirect would take place, and then Persona's page could rightfully show a noscript message. Users could easily whitelist Persona, without whitelisting each site individually. Mobile apps could exclude the include.js entirely, which would save users bandwidth. The communication_iframe is no longer needed in this instance! Performance win! Also means we aren't rely on any 3rd party mechanism, so we'll work in Safari / 3rd-party-cookies-disabled.
COMMENT: This almost makes Persona work without JavaScript. But the Persona login page still needs JavaScript enabled. The RP endpoint receives a GET request, and does a redirect to the Persona login page (The Persona dialog). The Persona login page attempts to do JavaScript. If the user has JavaScript disabled, the page will display a "noscript" message, and ask the user to whitelist it. ("Whitelist" is a browser setting to allow JavaScript to run on a page, even when the browser has a setting "JavaScript is disabled" for all web pages. Whitelisted pages can ignore that restriction and execute JavaScript anyway.) The "whitelist" of the Persona login page only needs to happen once for a browser, after that the browser will remember the setting. COMMENT: The Declarative HTML API provides a more graceful fallback for people with JS disabled.
### HTML + shim Including all of the above, the RP could optionally include the shim, and use it with the declarative HTML. If there's no JavaScript, it works as above. If there is, the shim would look for "a[rel=servicetrigger]", and if found, automatically setup the click event handlers for those elements. This would give the user a slightly better experience, as they use a popup and don't lose context of the main window. WeThe popup would use the onlogin callback of watch() to then notify the shim running in the main window. The shim would then POST to the href of the anchor.
COMMENT: In this case, the RP includes the shim (JavaScript) in web pages that it serves. When a user clicks a "userauth4" anchor, the shim (JavaScript running in the page) displays a popup dialog window. This gives the user a better experience, because it does not change the page the user interacted with. (For comparison, in the "HTML only" case, the web page the user interacted with is redirected to a Persona login page.)
COMMENT: The popup does not do a POST itself. (To the RP. To the "returnTo". To the "href" of the anchor.) Instead, the popup does a callback into the shim, in order to let the shim do the POST. This is in order to make the POST (to the RP) originate from the user's browser, from the same browser window that contains the HTML page that the RP served. This is because the popup dialog displays a website (probably a Persona login page) that has a different domain than the RP. If the popup did a POST directly to the returnTo, then the RP would receive a POST with an origin different from its own domain name. In this case, the RP would have to guard against a Cross Site Scripting attack. Likely requiring the use of a csrf token (A challengeKey value) (A nonce value).
### JavaScript only If people don't want to use the HTML, they can use the JavaScript API. continue to interact with navigator.id using JavaScript. We would still recommend that all properties be put into watch() instead of request(), and my Pull Request[1] outputs deprecated warnings for all options passed to request(). We may want to recommend setting up the GET and POST endpoints on their returnTo anyway, to support both no JavaScript, and returning users from email verification who closed the original tab.
COMMENT: If an RP wants to use the JavaScript API exclusively, then it does not need to use the new style ServiceTrigger HTML Elements in its HTML web pages.
COMMENT: Even when using Javascript exclusively, it is a good idea for RP websites to set up a POST endpoint. All the navigator.id functions will do a POST to the endpoint provided to them.
Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images2/logo.png" }, "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "supported": { "supported.uic.csvm": ["basic1", "basic2"] } }
] }
Example JavaScript:
// Example JavaScript in an RP web page.
let login_promise = remereLogin_api.performServiceOp("login", {FiskProfile: fiskProfileCts}, fiskProfilePath);
========

APPENDIX D - Browser does not support Capis HTML Attributes

It is easy to detect when a browser does not support the Capis specification. (i.e. ServiceTrigger HTML Elements)

If the RP receives a request without "capis_request" (or to the href of the anchor), then the RP CAN do a redirect to a Persona login page. And add appropriate parameters to the new URI.

Example FiskProfile: On the website 123done.org The following FiskCatalog is at 123done.org.
Example FiskCatalog:
{ "id": "main_2", "rpInfo": { "siteName": "ACME Corporation", "smallIcon": "/images2/logo.png" }, "serviceProfileList": [
{ "id": "remere_1", "protocolUsed": "remere", "sdSpec": "remereSdSpec 0.1", "endpointBlock": { "id":"endpoint_1", "sUri": "/_capis/remere/requestOp.json", "supportedFormats": ["JWS"] }, "fallbackProtocol": {"uriPattern": "https://$domainName/_capis/fallback/$protocolId/$opId.html"}, "supported": { "supported.uic.csvm": ["basic1", "basic2"] } }
] }
Example HTML Anchor:
<a href="remere://example.com/remereServiceOp?op=login" rel="servicetrigger" servicetriggerdata='{ "protocolOp":"remere/login", "onSuccess":{"actions":[{"redirectUri":"/some/path/page.html"}]} }'>Login</a> // Redirect to: login.persona.org/sign_in?origin=123done.org&returnTo=/_capis/remere/requestOp.json&siteName=123done

When login.persona.org receives the redirect, it would display a dialog to the user, of the list of available identities. (In a popup window or in the browser tab?) The user picks the identity to use, enters the identity password (if necessary). Once the identity authentication has been completed, login.persona.com redirects the browser to the returnTo address. It makes this redirect a POST request, and includes the proof of identity (i.e. the UserId Assertion). The RP at the returnTo address receives the POST, verifies the proof of identity (the UserId Assertion) and then redirects the browser to wherever else they want the user to go to on their site.

In this case, the href can be considered the returnTo (and postBack, we'll come back to that).
========

APPENDIX E - OLD Mozilla Persona (Browser ID) JavaScript API

Note the similarities to the previous Mozilla Persona (Browser ID) API. navigator.id.get(object callback, object options); navigator.id.request(object options) ~ remereLogin_api.performServiceOp("login", opArgs, fiskProfilePath, triggerView) navigator.id.logout() ~ remereLogin_api.performServiceOp("logout", opArgs, fiskProfilePath, triggerView) navigator.id.watch(object options)  ~ no equivalent. Was used to automatically login users. This proposal does not cover auto-login. Example options to request(). navigator.id.request({backgroundColor:"#rrggbb", siteName: 'Example Site', siteLogo: '/logo.png', termsOfService: '/tos.html', privacyPolicy: '/privacy.html'}); The functions navigator.id.get and navigator.id.request are almost the same. id.request will pass the assertion to the onlogin callback registered with navigator.id.watch(). id.get requires the onlogin callback in the list of arguments. --- Arguments to id.watch loggedInUser - The website provides the id the user is logged in as. onlogin - function called during login, with the created assertion as an argument. Is called BEFORE the assertion is sent as a POST to the site. onlogout - function called during logout, with no arguments. Is called BEFORE the POST is sent to the website. This should tear down the user's session. By making a call to the site's backend, or by redirecting the user. onready - function called when the user agent is initialized. Is called immediately AFTER any automatic invocations of onlogin, onlogout, or onmatch. We would still recommend that all properties be put into watch() instead of request(), and my Pull Request[1] outputs deprecated warnings for all options passed to request(). This Declarative HTML API works if you have native support in your browser -- or an extension installed. It also works if the RP includes the shim. How does this work with login/logout session tracking which requires JS callbacks to be useful?? navigator.id.beginProvisioning(object callback); navigator.id.genKeyPair(object callback); navigator.id.registerCertificate(string certificate); navigator.id.raiseProvisioningFailure(string reason); ==== navigator.id.beginAuthentication(object callback); navigator.id.completeAuthentication(); navigator.id.raiseAuthenticationFailure(string reason);
========

APPENDIX F - Option to send an HTTP GET request

By default, an ServiceTrigger HTML Element will result in an HTTP POST request to the RP website. However, there may be some reason to change that to an GET request.

If the browser sends a GET request in response to a ServiceTrigger HTML Element being activated, then the proof of identity may not fit in the url.

========

APPENDIX G - Old Persona login behavior

YouTube Pesona login When logging in at a site where the user had logged in before, the user was faced with a LIST of login options. 1. Click "Sign in with Persona" button on 123done.org 2. Browser does a check if it has any login email addresses for that website. 3. If the browser does not have any logins for that website, the user is presented with a FORM to fill out. 3. Browser displays https://login.persona.org/sign_in (If the user had not signed in to that website before,) A popup opens. It says: 123done uses Persona instead of usernames to sign you in. To sign in with Persona, please enter your email address. Text entry box. He typed in: foobar@eyedee.me 4. Browser extracts the domain name from email. Looks up domain/.well-known/browserid If there is a browserid document, then present that UI. 5. Browser pupup is refreshed. (Still at https://login.persona.org/sign_in) eyedee.me makes this easy. Persona lets you use your eyedee.me account to sign into sites like 123done. Once you verify your account there, you will be signed in to 123done. [sign in with eyedee.me] [Use a different email address] 6. Browser is redirected to "https://eyedee.me/browserid/sign_in.html?email=foobar%40eyedee.me" REDIRECT TO EYEDEE.ME and asked to sign in there. 7. Browser is redirected to "https://login/persona.org/sign_in#AUTH_RETURN" Next, go to a DIFFERENT website and click sign in there. Persona presents a LIST of login email addresses. There is only one item in the list. It is NOT selected. (There is no selection) Persona remembered and listed the last email address used. (It was foobar@eyedee.me used at 123done.org.) 2. If the user HAD signed in before, the user was presented with a LIST. (radio buttons) Sign in as... dan.callahan@gmail.com dcallahan@mozilla.com [Add another email address.] [This is not me.] 123done [Sign In} 123done.org * One is selected by default. It's preselected, the last one I used on this site. * password reset

APPENDIX G - Changing a user's identity

Password Manager Studies

good article other ==== Two-factor authentication or two-step verification? A lot of people think they’re the same thing, but that’s not really accurate. ==== Some providers use a zero-knowledge model, where they only store an encrypted copy of the password vault on their servers. Then, contents of the vault get synchronized with the client applications or are sent in encrypted form to the user’s browser during online access. The decryption process is always done locally, based on the user’s master password, which is never shared with the service provider or sent over the Internet. However, this model does not protect against client-side attacks. For example, attackers could still obtain users’ master passwords if they infect their computers with keylogging malware. That’s why it is also important to choose a password manager application that offers two-factor authentication. Two-factor authentication prevents attackers from accessing a user’s password vault from a different computer or device by using a stolen master password. However, they could still use an existing malware infection to piggyback on a user’s active password manager session and access their online accounts via the local browser, especially if the auto-login option is turned on. Auto-login features may be convenient, but can also be fraught with peril. Users should think carefully about whether they want to activate them. It’s also best to use password management applications that can automatically log off the user after some time of inactivity, especially if the browser is kept open for long periods of time or if someone else might have access to the computer while the user is away. This might not always protect against active malware on the computer, but it does add another layer of security. Users may also be tempted to flag a device as trusted. Many password management applications offer an option of skipping the second authentication step in the future on a given device once they’ve completed a two-factor authentication with it. While convenient, this method assumes an attacker will never gain control over that device, which is not always the case, so users should carefully consider whether they can live with inputting the second factor every time. Don’t rely just on a password manager One of the primary benefits of using a password management application is that it allows the use of different complex passwords for every account without having to remember them all. However, it’s equally important for the user’s master password to be strong so that it can resist brute-force attacks. Users who find it hard to remember complex passwords that include digits, lower-case and upper-case letters and even special characters, should try using long pass phrases as their master passwords instead. These are sequences of random real words that make up hard-to-guess phrases and provide the same or even better level of protection against brute-force attacks as a strong password, but are easier to remember. Pass phrases can also be used for critical accounts that need to be accessible even if when the password management application or server is unavailable for some reason. Finally, many of the largest online services, such as Facebook and Gmail, are now offering two-factor authentication themselves, so even if you’re using a password manager and follow best security practices in general, turn on two-factor authentication whenever it’s available. It can make a really big difference, especially if your password manager does get compromised.

Password and account reset

3. If you still receive an "invalid password" error, try your Password Hint that you set up when you created your account. This is a reminder hint NOT your Master Password. 4. If the hint doesn't help, go to the Account Recovery page to activate your local One Time Password. This allows you to change your Master Password if you've logged into LastPass previously on that computer, and is the only way to 'reset your password'. You should try this on all browsers and on all computers where you've used the LastPass plugin to access your account. This method does not work on mobile devices. Account recovery is not supported on mobile devices or apps. Changing the master password, reverting the account, or clearing the browser cache for LastPass can destroy these files. 5. If you have updated your Master Password within the last 30 days, you can go through these steps to Revert to change to your previous Master Password. Please note, we do NOT recommend selecting the "restore" option unless you have confirmed with LastPass Support that this is the best course of action. Availability 1. Have both a primary and a backup IDP (or publicKeyManager) in production service. 2. Store your data on your local PC, in encrypted form, when you login. so that if both the primary and secondary can't be reached, you can still login to the passKeyStore and get to your accounts. Features * Password generator * Auto-password-change * passKey Manager should store a list of IP addresses that accessed the account for at least 60 days. * temporarily disable Yubikey authentication. (lost device) ( BYOI (Bring your own identity)

Notes, ideas

Storing the private key in session storage for an origin. Can we store it in a session storage for a domain that doesn't exist? To keep the private key private? How does password manager work? ======== github.com/portier/demo-rp/blob/master/server.py @app.post('/login') def login_post(): // The RP should discover what the user's IDP is. # FIXME: The Authentication Service Endpoint should be discovered, not hardcoded url = SETTINGS['BrokerURL'] + '/auth?' + query_args return redirect(url) ======== The most common types of records stored in the DNS database are for Start of Authority (SOA), IP addresses (A and AAAA), SMTP mail exchangers (MX), name servers (NS), pointers for reverse DNS lookups (PTR), and domain name aliases (CNAME) ======== Q: When submitting the form, the page gets reloaded... I don't want that... Is there any way to make it not reload and still send the mail? Preferrably without ajax or jquery... A: If you put an Iframe in the page, you can redirect the exit of the action there and make it show up. You can do nothing, of course. In that case, you can set the iframe display to none.
<iframe name="votar" style="display:none;"></iframe> <form action="tip.php" method="post" target="votar"> <input type="submit" value="Skicka Tips"> <input type="hidden" name="ad_id" value="2"> </form>
======== Please clarify the "about:preferences" categories. It isn't clear what "privacy" vs "security" is. I had to look it up. Can you add a short explanation of each under the section title? Under "Browser Privacy" add a sub-title like "How your browsing behavior is tracked and used. And what suggestions are made. (History, search, cookie, cache, login, form data, passwords, and tracking settings.)" Under "Website Permissions", add a sub-title like: "Special privileges certain websites may be allowed." Under "Security", add a sub-title like "Guards against online threats, such as fraud, impersonation and dangerous content. (certificates, network, content and software blocking.)" ======== The parameters for the signature creation function call are the Key Handle and the challengeKey (the nonce).

U2F - Universal 2nd Factor Study

There is an error in the sentence "An error can be detected by testing for a non-zero errorCode key". The use of "non-zero" is incorrect. The function needs to check the response object for "the existence" of the errorCode property. (i.e. if it is not the "undefined" value.) Zero and non-zero values have nothing to do with it. Example 1 is needlessly confusing. u2f.sign(reqs, callback_function); The u2f.sign function takes 4 or 5 arguments, (The 5th is optional), and none of the arguments is named "reqs". It is my assumption that somebody got sloppy and used "reqs" as a placeholder meaning "the other stuff", as the example was meant to show the use of the callback_function. However, this makes the example very confusing. And your document DOES NOT HAVE ANY CORRECT EXAMPLES OF THE u2f.sign function for the JAVASCRIPT API that you are trying to define. Probably, a more correct example would be something like... // High-level API u2f.sign(<Application id>, // The Application Id [<RegisterRequest instance>, ...], // Array of RegisterRequest objects [<RegisteredKey for known token 1>, ...], // Array of RegisteredKey objects signResponseHandler); // The callback function function signResponseHandler(response) { if (response.errorCode) { // Check for a non-false value. Non-zero, non-null, non-empty string, not undefined. // if (response.hasOwnProperty("errorCode") // Check for the existence of an "errorCode" property. // response is an Error ... } else { // response is a SignResponse ... } } The U2F device registration and authentication operations are exposed through JavaScript APIs built into the browser and, in following phases, native APIs in mobile OSes. A Relying Party (RP) consumes UserId Assertions from U2F tokens. During registration, the browser sends the U2F device a hash of the origin (combination of protocol, hostname and port). In an authentication request, the browser sends 3 hashes to the U2F device. The browser sends a hash of the 'client data', the hash of the Key Handle, and the hash of the origin which is requesting the authentication. The U2F device checks the Key Handle against the origin, to see if it issued that Key Handle for that origin hash. If there is a mismatch no signature is returned. the U2F device proceeds to issue a signature across the hashed 'client data' which were sent to it. This signature is returned back as another return value of the U2F 'sign' call. The site's web page which made the U2F 'sign' call sends the return values, both the 'client data' and the signature, back to the origin site (or equivalently, relying party). The origin check is a critical privacy property. It ensures that the public keys and Key Handles cannot be used by a different RP (i.e., a site with a different name on a valid SSL certificate). (That the same public key or KeyHandle is not used with multiple RP.) If the origin check was not present, a public key and Key Handle issued by a U2F device could be used as a 'supercookie' which allows multiple colluding sites to strongly verify and correlate a particular user's identity. ** Can Register Multiple U2F Devices to the Same Account If a user has registered multiple U2F devices to a particular account, then during authentication all the Key Handles are sent by the origin to the intermediate page. The FIDO APIs use an alternative identifier for relying parties called an AppID [FIDO-APPID], and any credentials created using those APIs will be bound to that identifier. Object.keys(obj) -- returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well) The for...in statement iterates a specified variable over all the enumerable properties of an object. The for...of statement loops (iterates) over properties of an iterable object. ?? Object.prototype.getOwnProperty() ?? Object.getOwnPropertyDescriptor(obj, propName) Object.defineProperty(obj, propName, options) Object.getPrototypeOf(obj)
========

Changes to the HTML specification

  1. Defined 2 attributes for ServiceGuide HTML Element. name="serviceguidedata" and content='{}'. (Alternately, "serviceguidedata" could instead be defined as a "data" attribute, "data-serviceguidedata".)
  2. Defined 1 attribute for all triggers. "servicetriggerdata".
  3. Defined a special behavior for ServiceTrigger HTML Element HTML Elements. (Calls CapisControl.invokeServiceFromTriggerHtmlElement(triggerHtmlElement)
  4. Defined 2 attributes for Anchor HTML Element. rel="servicetrigger". And "servicetriggerdata".
  5. Defined 2 attributes for Form HTML Element. rel="servicetrigger". And "servicetriggerdata".

Note: During a Capis Form submission, Capis will also send an extra, special HTTP parameter, in addition to any regular form data. The default name of the HTTP parameter is capis_request. See Capis Default Values.

========

Attestation Notes

How should attestation be done?

Should RPs require attestation? And exactly how should the Authenticator devices provide it?

Attestation change - devices should not provide a MANUFACTURER's attestation certificate. This would mean that RPs get to know who created a device. Then the RP can allow or deny or track usage based on a manufacturer Users would not know if an RP allows their device or not. RPs would only support the top 3 manufacturers. Making it impossible for a newcomer to break into the product space. Instead, a device should provide a CERTIFICATION certificate. From an industry group. The manufacturer can pay the industry group (certification group) to get their device certified. RPs can accept or deny a device based on its ABILITIES. Not it's manufacturer. Based on a certification by an industry group. The certification group must number (increment) their certification certificate numbers. So that if a problem occurs, it will only that batch of devices. (The devices may have an error, the master key is hacked, etc.) Perhaps 100 thousand at a time, then the certification certificate number must be incremented. If there was an online listing of which manufacturer created which certification group, then the RP *could* still limit by manufacturer. However, it is not the default. There is another, easier way for RPs to limit devices, and that's by the certification rating.
Quote from Adam Langley's Weblog
Example WebAuthn:
    user: {
        // id is a opaque user-id for this account.
        id: new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]),
        // name is an account identifier. Doesn't have to be an
        // email-like string, just whatever your site uses.
        name: 'jsmith@example.com',
        // userDisplayName is a more friendly version of |name|.
        userDisplayName: 'Joe Smith',
    },

All these fields are required. However, like the previous chunk, they’re currently discarded and intended for forthcoming CTAP2 tokens.

The id identifies an account. A given CTAP2 token should not store two keys for the same RP ID and user ID. However, it's possible that CTAP2 keys will allow blind enumeration of user IDs given physical possession of the token. (We have to see how that part of the CTAP2 spec is implemented in practice.) Therefore, you don't want to store a username in the id field. Instead you could do something like HMAC-SHA256(key = server-side-secret, input = username)[:16]. (Although you'll need a reverse index in the future if you want to use CTAP2 tokens in 1st-factor mode.)

The name and userDisplayName are again strings intended for a future account chooser UI that doesn’t currently exist.

Example WebAuthn:
    // challengeKey is an ArrayBuffer containing the nonce.
    challengeKey,
.........

... it’s far from clear how well attestation will work outside of a controlled environment and I recommend that you ignore it in the general case. ... the utility of the challengeKey when creating a key is questionable. Generated U2F keys don’t sign over it and, while CTAP2 keys have the option of covering it with a self-signature, it remains to be seen whether any will actually do that.

...

Thus since it's easy and you'll need it for assertions anyway, I still recommend that you generate a 16- or 32-byte nonce on the server as the spec suggests. I just thought that you should be aware that the benefit is a little fuzzier than you might imagine.

Example WebAuthn:
    pubKeyCredParams: [ // Each item is a pubKeyCred.
        { type: 'public-key', alg: -7 },
    ],

This enumerates the types of public keys that the server can process. The type field is always public-key and the alg comes from the IANA COSE list. The value -7 means ECDSA with P-256, which is effectively mandatory since it's what all U2F tokens implement. At the moment, that's the only value that makes sense, although there might be some TPM-based implementations in the future that use RSA.

Example WebAuthn:
user: { id: "yyy" }
Don't store a username in the id field. Instead you could do something like HMAC-SHA256(key = server-side-secret, input = username)[:16]. (Although you'll need a reverse index in the future if you want to use CTAP2 tokens in 1st-factor mode.)
Quote from Yubico

FIDO key-pair generation. yubico.com

When a user registers one of our U2F devices with a new service, the service provides an AppID (this is tied to the URL of the site and prevents phishing).

The U2F device generates a nonce, N. We then take the AppID and the Nonce and run them through HMAC-SHA256 (a one-way keyed function), using a device-specific secret as the key. This device-specific key is generated on-chip at the time of manufacturing (just like the master key would be, if we were using regular key wrapping). The output of the hash function becomes the private key, and the nonce, together with a MAC, becomes our key handle. During authentication, the MAC helps to ensure that a key handle is only valid for the particular combination of device and AppID that it was created for during registration.

Quote from techbeacon.com
See also: this, from blakesterz:

See also: that SQRL thing Steve Gibson has been working on for years?

To which Ajedi32 forces an opinion: [You're fired--Ed.] SQRL's biggest advantage over other proposals for a password replacement was that it didn't need buy-in from browser vendors to work.

At this point though it seems pretty likely that the Web Authentication Standard has successfully overcome that barrier. It's already partially implemented in multiple browsers (Chrome, Firefox) and backed by a W3C Candidate Recommendation. In the face of that, I don't believe SQRL can compete.

Scratchpad - unfinished thoughts, random ideas

Capis - Common Architecture for Publishing Internet Services Capis - Common Authentication Protocol Identity System Capis - Canonical Authentication (and) Permanent Identity System Camua - Common Architecture for Managing User Authentication (i.e. Remere) BISHOD - Brookstone Internet Service in HTML Discovery Protocol JSDS - JSON FiskProfile Specification. BICDAS - Browser Identity Credential Delivery and Authentication System Capis - Common Authentication Protocol Identity System ICDAP CADAR - Identity Credential Delivery via Post ICEDAP - Identity Credentials Etc. Delivered via Auth Parameters ECDS - Easy Credential Delivery Specification GEXIS - Groovy Extendable Identity System. CAGIS - Category G Identity System Capis - Category P Identity System COPIS - Common Online Pervasive Identity System, CIPA - Common Identity Authentication Protocol COVIS - Common Online Very Identity System. CIURP - Common Identity Used by Rps. CIAS - CIVS User Identity Verification System Capis - Common Architecture Priority Identity System CARPIS - Common Architecture Relying Party Identification System CURTIS - Common Identity Authentication System CRIBAS - Common Registry Identity Broker Authentication System The IDP knows the current public key of the user. The IDP can invalidate the public key of the user. The user contacts the IDP to change their public key. This request must be signed by the CU private key. The user can change IDPs at any time. IDP ISR - key revocation. OCSP stapeling. * Contains the UserId of the most recent record involving the identity. * Contains the timestamp of the most recent record involving the identity. The contents of an "UserId certificate" are constantly checked. (Through an IdP "UserId Status Report".) Note that the identity is permanent. Even if the certificate expires or is later compromised or revoked, the identity is still valid. node.js > const base64url = require('base64url'); * Give users a trust-worthy way to identity themselves. * Examples: OpenID, OAuth, SAML, Kerberos, and so on * Create a simple and consistent way to sign in while preserving user choice and preventing vendor lock-in. (that's the hard part.) Problem - lets all IDP log in as the user to all websites. Problem - limits the user. The IDP must be online. Solution - use two keys. IDP has one key and a partial second key. Maybe the partial second key is derived from a master + pass phrase. How to store a password hint? How to do account recovery. Maybe the partial second is the master, the login key is derived from it + a secret pass phrase. Idea - an IdP that gives the user a menu of authentication methods per identity. Universal 2nd Factor (U2F) protocol from the FIDO Alliance - The RP stores a second public key for each user.!!!! IDP - I know you own this identity, because I issued it to you. The certificate that Persona.org gave the user's browser is stored in the LocalStorage -- for login.persona.org ERRORS * typing passwords into a popup. * fake logout. Even if the user closes the browser, if someone else opens it, they can log in again. * anything in the client is insecure. The token can be stolen. Vulnerable to XSS. Can we encrypt the JWT with the RP public key? Make it readable only to the RP? Need to make the automatic POST https. (secure) Amazon -- To simplify sign-in on computers and devices that you routinely use, you can tell us not to ask for a security code so that you won't need to enter a code each time you sign in. Afterward, that computer or device will only ask for your password when you sign in. Amazon -- sign-in using a registered backup method or a trusted device before attempting an account recovery. * users must have TWO methods for token delivery. One primary and one backup. phone number or an authentication app * Amazon requires a backup. * can get your two-factor authentication codes from an authenticator app, like Google Authenticator or Authy * Most websites that support two-factor authentication allow users to mark devices as trusted * users can remove any of their previously trusted devices in case they are lost or compromised * Allow users to specify a backup phone number that can be used for account recovery. Or, provide backup codes when turning on two-factor authentication that can be printed on paper and kept in a safe place. * If these options fail, you will most likely have to call or email the company’s technical support department and prove the account is yours, for example by providing information about the account that only you would know. grantAccess Name: Managing Account Access Name: Granting Account Access, (GUA) Name: Allowing Account Access Name: 3rd part Account Access Name: Secondary User Access, Name: Additional User Access, (AUA) Section title, "Managing Additional User Access (AUA) to your account" Name: Account Sharing, Managing Additional Users, Allowing 3rd party Account Access additional User Logins * A UserId must ALREADY be different from the account_id, because a user can change his UserId. He can use the Identity Lock feature to disable his identity. And to change his UserId. * Because the UserId of the owner can CHANGE, * How does a user "disable" their account? (If he suspects his key has been compromised?) Does it lock the account, or the identity? Can a *guest* disable their login too? If the user that disabled their login is the owner of the account, does that disable the *account* and not just the UserId? Are all other users locked out as well? * How does a user *re-enable* their account? (Does it require a Rescue Code or not?) Does it re-enable all other users? * What if the "owner" needs to change their UserId? He/she loses their private key, or the private key is compromised. He/she sends the Rescue Code to the server. The server does what to the account? Is the "owner" set to nothing? How will the new owner be set? * How would a potential "Guest" request access to an account? He would have to know either (1) the account id, or (2) the UserId of the person that owns the account. How would he get that UserId? He would have to ask the owner. How would the owner get it? He would have to retrieve the UserId that SQRL uses to login to that website. (The UserId is different for every website.) Is it easy for a user to get the UserId for a website from SQRL? Is that possible? * Would it be easier for the owner to login to the account and have the website send an "invite" to their friend/wife/child? When an invite is created, the owner must assign the new person an "account_permission_role" (restrictions). By using an "invite", the "invite" would be sent to an email address, the UserId of the invited person will not be known when the email is sent. The UserId will be discovered when the person accepts the invite. The invite should probably expire after 24 hours. Also, doing it this way will make the owner do it through the website. So the website gets to define what security restrictions the owner must assign the new user. * Extra Security when using invitations. When an invitation is used, all that does is ADD the UserId to the account's list of *potential* users seeking access. It does not give access immediately. The owner of the account has to do an additional step to activate the additional user access. For example: the owner can be provided with a list of people seeking account access. (i.e. people that he gave out invitations to.) For each of these users, he can click "view", "allow", or "more". ("more" can include "report", "delete", "delay", "flag as suspicious".) "view" shows the invitation send date, the response date, and the UserId of the new user. The page can have a warning. Additional Users Confirmation Page. WARNING. This is a list of users that have responded to an invitation sent from this account. These users have NOT BEEN GRANTED ACCESS TO THE ACCOUNT YET. It is possible for this invitation system to be abused or circumvented. It is possible for uninvited persons to use invitation codes. YOU MUST CONTACT each person that was sent an invitation, and CONFIRM that they are the one that successfully used the invitation, that they provided the system their UserId, and are waiting to be confirmed. (Contact them through phone, text or in person.) If you are not sure about a user, or cannot contact a user, DO NOT GIVE THEM ACCESS. A word about "none" account access. It is possible to change a user's "account_permission_role" to "none". However, that SHOULD NOT BE DONE with users that are entirely unknown (i.e. to users on this page that are requesting access). "none" is for temporarily removing account privileges, it does not grant sufficient protection from hackers. * As a special feature, if you do not want to "delete" or "report" the user, you can give the user a special "account_permission_role" of "none". This will remove the user from this page, and allow you to manage the user later. It will not give the user any account access. The idea is, this would allow the owner of the account to verbally (or through email or text) confirm that the intended user actually was the one that clicked on, and used the invitation. * Suggestions. An account must have at least one owner. (An implementation can optionally choose to limit the number of owners to one, but this is not a requirement.) Every time a request is received to "downgrade" the access of a user, it must be checked that doing such an action would not leave the account without an owner. * It will be possible for a single UserId to access multiple accounts at a website. The UserId could "own" one account an be allowed to access several others. * How to switch between accounts? When this user logs in, they must be able to either (1) be able to dynamically switch accounts. Or (2) Not be able to dynamically switch (after having logged into an account). In this case, the user must be presented with a selection screen during login. * How would a recognized UserId (a user with an account) be able to *ask* the website to access a different account? He wants to be able to switch accounts. To be able to see the account of his wife/child/friend at the same website. How does it provide authentication to applications? replace #user-identity-criteria with #user-identity-verification-def replace "Credential Verification Key" with "Credential Verification Rule" replace single quoted, "credRegSec" credentialRegSection search for lopsided quote Credential Verification Rule" TOI Different login. 2018-09-06 convert the public key to a text user id, encoded in Base64. The login page from the server embeds a nonce (what SQRL calls a nut) and it uses the public key to sign a hash of the nonce and send that as the password. So a sample login might look like (this is all just random typing, I'm not going for authenticity here) (I'm also not even trying for the correct string lengths): Username: TOIUv1:H_D9uhd9DHa98dh98d9d9797-798goiGUiG9 Password: TOIPv1:hLKJhbhojoojb798uGG9ug9ug_g7g87g-7687f8f8f8F5xgXg8ff As you can see the server can recognize this different type of login from the prefix, including a version indication. The users that still use username/password can still login as they always have. This allows for a nice gradual transition from the old to the newer. There are other details that SQRL insiders will want to know about. The login page has the server's public key embedded into it, and the nonce is signed by it. The client pins the site to that public key... so it helps to prevent phishing in the future. (The first login obviously is a different case... but that is another topic for another time.) Additionally, the server will use the authentication credentials to encrypt a cookie in its response back from the login POST. This allows the server to securely send back a cookie, as well as a URL for the next page. (This is possibly still optional as it's not really necessary if TLS works.) ==== Sharing SQRL site keys - not sharing the master identity. Q: For example, you break up with your partner and then you want to revoke that sharing permission. Or say somebody starts sending the pair around to a bunch of people you don't want to have access. A: If you strictly follow Steve's approach then you would have to rekey your identity. I have an additional site specific nonce that can be changed independantly, which thus means you can rekey just one site at a time.

TODO

Suggested changes to WebAuthn. Note: Remere Login is an example of how to do these. 1. Remove all JavaScript code from web pages. Consolidate all the Webauthn "requirement information" into a single JSON configuration object. (The PublicKeyCredentialCreationOptions and PublicKeyCredentialRequestOptions, and all sub objects.) The JSON object should be in the HTML as an element attribute (on the Meta element). Or it can be in a separate JSON file, published by the RP, and referenced by an HTML Meta element. This JSON configuration object will define what formats and methods the RP supports, and what requirements it has for "user login". (For example, it will contain what public key COSE algorithms the RP supports.) 2. Extend the JSON configuration file to contain some additional information. It should contain a description of one or more RP "endpoints". That receive requests from browsers. It should contain the equivalent of the "PublicKeyCredentialCreationOptions", etc. An endpoint URL will accept User Authentication requests. Which contain login commands and credentials. (And logout commands, etc.) Example JSON config object/file: { "userAuthentication": { "endpointBlock": { "name": "endpoint_1", "http_method": "POST", // Default http_method is "POST" "sUri": "/_capis/remere/requestOp.json", // REQUIRED. Define the endpoint URI. "supportedFormats": ["JWS"] }, "rp": { "id": "login.example.com", // The capId "altRapSourceList": [ {"authAppId":"example.com/remereApp/2022010100", "colMode":"SSO"}, {"authAppId":"example.com/remereApp/0000000007", "colMode":"SSO"} ], "capAccessList": [{"capId":"example2.com"}, {"capId":"example3.com"}] }, "userLoginCreationRules": { "allowedRpIdRules": {"rpMatch": [{"id":"example2.com", "all-subdomains":"yes"}]} }, "mediation": "silent", "credentialInfoModules": [ { "id": "passwordCred2", "allow": "always", "credentialModuleInfoList": [ { "type":"password", "transport":"raw" } ] }, { "id": "pubKeyFirst": "allow": "always", "credentialModuleInfoList": [ { "type":"public-key", "coseAlgId": -7 }, { "type":"public-key", "coseAlgId": -257 } ] } } } } Example of a JSON configuration file: FiskCatalog Detail 3. Create a standard "User Login Request Format". This is a way to deliver Login Data from the browser to the RP endpoint. It will contain User Login assertion data (the credential(s), etc.) to the RP. It will also include the requested opId. (i.e. "login" or "logout".) To make it very easy, this format can be a JSON Web Signature. 4. The browser should keep track of ALL web pages that are currently logged in. This allows the browser to have a "logoutAll" option. A.K.A. "logout of the browser". (pressing this button will send a logout command to all the logged in web pages.) 5. The browser MUST receive a response from the RP for every "User Authentication Request". In order to keep track of the login status for all web pages, the RP MUST issue a JSON response to the "User Authentication Request", telling if the request was successful or not. And the browser must receive and parse the response. The response cannot be preempetively received by JavaScript code in the web page and then not delivered to the browser. 6. Create an HTML element that will "activate" or "trigger" the login process in the browser. When the user clicks on this special HTML anchor (etc.) it will trigger the browser's login (or logout) behavior. The HTML element will have the href of the JSON file containing the login requirements. The browser will get the JSON file, and then perform the login. (Or logout) 7. Expand the concept of an "Authenticator" so that users do not have to buy a USB key, etc. The "Authenticator API" can be implemented by browsers or by the operating system etc. Existing password managers can implement the API, and users can access their "Authenticator" online. 8. Focus on the concept of a "UserLogin" and NOT on a "Credential". Using a "Credential" is insufficient. And confusing. Redo the "Authenticator API" to focus on a "UserLogin" and not be limited to a "Credential". This allows a User Login to have multiple credentials. Some credentials may be inactive or a backup. A User Login can have backup credentials. (In case of loss, compromise, hacking, etc.) Have a method where the user's "UserId" can be changed. (In case of compromise, etc.) A User Login is denoted by a "userHandle". Example: capis_login_spec.html#API-LoginManager Having multiple Credentials per User Login will allow Multi-Factor authentication, etc. For example: The "CredentialCreationOptions" JSON property should not have the "user" entity in it. The "user" entity should be part of the "UserLoginCreationOptions". 9. How to implement this API in RP web pages. The basic system will work in HTML, with NO Javascript. (A special HTML anchor, etc.) There can be a Javascript API that will give more control to websites. The Javascrit API will not use "navigator.credentials" Use something like "navigator.userLogins" or "navigator.remereLogin" Example: let ch_val = new Uint8Array([21,31,105 etc...]); let userEntity = { id: "ABBA", userDisplayName: "Alex Muller"}; let userLoginCreationOptions = {userCreation: userEntity, challengeKey: ch_val, credentials: [publicKeyCredentialCreationOptions] }; navigator.logins.create(userLoginCreationOptions); Example 2: let ch_val = new Uint8Array([21,31,105 etc...]); let userLoginRequestOptions = { userId: "ABBA", challengeKey: ch_val, publicKey: {...} } navigator.logins.getAssertion( userLoginRequestOptions); // In the "getAssertion" request use "userId" to identify the userLogin. // Instead of allowCredentials: [{ type: "public-key", id: credentialId }]. // The "allowCredentials" is detailed in the RP's JSON document. And does not include the id property. // "userId" is the same thing as "userHandle". Except it's base64url encoded. Example 3: navigator.logins.update({ userId: "ABBA", publicKey: publicKeyCredentialCreationOptions}); JavaScript: if (!window.remereLogin_api) { //error code } Discover client device Discover From ExternalSource. CollectQualifiedUserLogins(filter) -- GetQualifiedUserLogins Add to the FiskCatalog, the RemereLogin API. RP.name and RP.icon. // Relying Party: rp: { name: "ACME Corporation" }, // User: user: { id: Uint8Array.from( window.atob("MIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGTMII="), (c) => c.charCodeAt(0) ), name: "alex.p.mueller@example.com", userDisplayName: "Alex P. Müller", icon: "https://pics.example.com/images/00/p/aBjjjpqPb.png" }, login logout createUserLogin proveUserPresence setCredentials signData grantAccess placeHold releaseHold