The content of this page is a work in progress intended for review.
Please help improve the draft!
Ask questions or make suggestions in the discussion
or add your suggestions directly to this page.
This is an exploration of how ID-attached services might work. None of this should be considered a spec or an indication of what the final solution will look like.
- 1 "Card" server (used by the dialog)
- 2 Profile server (used by RPs directly)
- 3 Bookmark storage service
- 4 Related specs
"Card" server (used by the dialog)
This server stores similar information to the profile server described below. However, it is not used directly by RPs, but is rather called from the dialog.
An email can belong to more than one card, but there is only one email per card.
If the card server is available, the dialog will show a list of cards instead of a list of email addresses. Each card can have a name and a photo (both are optional).
Relying Parties will receive the contents of each "card" from BrowserID, as part of the assertion:
- the name will be a simple free-form string
- the photo will be a URL to where the full-size image is hosted (RPs will need images in different sizes, but they will need to resize images themselves. We will only provide the full-size image.)
Dialog will receive a JSON structure containing:
- photo thumbnail as a URL where it's hosted
The dialog can add new cards or modify existing ones.
Photos are sent to the card server as bytes in the usual form submission format.
Alternatively, a link to an image hosted elsewhere can be provided. In which case, the card server will download the image.
Uploaded images will be validated and optimized (lossless-ly) by us to reduce their size and strip out any metadata such as EXIF tags.
TODO: are there security concerns with the card server doing a GET to an arbitrary URL on the Internet?
Because it's only accessed by the Persona dialog (or a native Persona user agent), the card server can act like an RP and accept regular assertions directed to itself.
We will support PNG and JPG of any size, but the image will be cropped if necessary to make it square.
Also, we will resize images down to 2048x2048 pixels if they are larger than that.
We will store and server static images that were uploaded by users on a separate domain that is not a subdomain of persona.org for security reasons. This server will host:
- thumbnails of a certain size to be displayed by the dialog
- the full-size image for RPs
The card server itself will be backed by a SQL database with the following tables:
+----------+ +----------+ | card | | photo | +----------+ +----------+ | id | | id | | email | | filename | | name | | mimetype | | photo_id | +----------+ +----------+
For testing purposes, the card server will have a basic web UI where users will be able to login to update the contents of their cards or add new ones.
As proposed, there is nothing that primary IdP need to do with respect to the card server. The card server is just an RP that will accept any valid assertions.
In terms of federation / decentralization, it is desirable to allow third parties to run competing Card servers. There will therefore need to be a mechanism to change the card server that the Persona fallback dialog (or the native Persona user agent) will use.
TODO: determine how the Card server will be specified, maybe through a BCP
The above description refers to the dialog, but the same functionality should be available in native implementations too.
Like the shim's dialog, a user agent with native support for Persona could choose to offer a card selector instead of a list of email addresses. Just like the use of the card server is optional in the dialog (for example if the card server is down), the use of the card server in user agents should be optional (but recommended) to make native implementations easier.
Profile server (used by RPs directly)
For the purpose of this example, the profile server is a service which stores a user's name in a database, keyed on the user's identity (i.e. email address). That name is a simple string.
Relying parties will want to get a user's name when they log into their site for the first time.
- Users need to authenticate with BrowserID through the RP before that RP is allowed to query their name.
- Users need to consent to their name being given to the RP. Simply logging into an RP's site is not enough
To achieve this, the profile server needs to receive an assertion. However, compared to an RP getting an assertion to let users log into their site, there are two differences:
- Users will not visit the profile server directly so any assertion they get and present to the profile server will have the RP as the audience.
- The assertion is used to read information for a given identity, not to log into a service with that identity.
This means that the profile server will need to accept assertions with an audience of the RP because that's all the RP can provide. Therefore the audience check on the profile server side should be against the remote endpoint of the jschannel (as opposed to the address of the profile server).
There is however a problem if the RP shares an assertion with the profile server: the profile server can use that assertion to log into the RP.
TODO: should the IDP (e.g. BrowserID) interface with the profile server directly to prevent the profile server from knowing the RPs that a user visits?
One idea to solve this problem would be to create a new type of assertion: a non-login assertion.
If assertions had an extra "purpose" field, it would be easy to distinguish between a "get_info" purpose and a "login" purpose. The purpose would default to "login" and the verifier would reject non-login assertions unless it is told what purpose to check for.
The BrowserID dialog would know what type of assertion is being requested and could display a different consent message.
This spec suggests instead that these assertions have the profile server as the audience but with an extra client field that would be set to the RP. This would involve changing the verifier to take an extra "expected client" parameter.
TODO: this get_info assertion would allow the RP to query any ID-attached service on behalf of the user. is that a problem? should it be restricted to the profile server?
TODO: are we reinventing OAuth?
Obtaining (and caching?) consent
We need to obtain consent from users before passing on their personal information to RPs.
TODO: do we ask users for permission every time we want to pass their name to an RP or do we somehow cache that consent? or do we assume that if their BCP trusts a service then consent is automatic?
The profile server will need to have a write interface so that the name entered by a user can be saved.
- The name entered by a user while logging into an RP needs to be cached so that it doesn't need to be entered again in a different browser.
Controlling write access to the profile server is tricky and we run into the same assertion audience as in the reading case.
In order to use the hypothetical "set_name" operation, an RP will need to present a "set_info" assertion with an audience of the profile server. This is normally not possible, but the BrowserID API will be extended to allow this limited grant of an assertion which violates the regular origin rules.
When the profile server receives the set_info assertion from the RP, it will write the name in its database against the email address contained in the assertion.
Pre-filling name field
In most cases, the IDP knows the names of its users and could therefore provide it without the need for a third party service. However, we want to give users the ability to choose what name they give to an RP.
This doesn't however prevent us from pre-filling the name field with what the IDP knows.
Bookmark storage service
Let's imagine another type of potential ID-attached service: a bookmark storage service.
To view a user's bookmarks, an RP must acquire an assertion from the IDP specific to that purpose. Then it uses that to access data for that user from the bookmark server.
To add a bookmark to the service on behalf of that user, it needs to get a different type of assertion from BrowserID. Then it can instruct the bookmark server to store bookmarks for that user.
Should we want an audit trail to catch abusive RPs, we could store the name of the RP against each bookmark, but this may have privacy implications since the profile server would then have a record of sites that a user has visited.
While this doesn't appear to be necessary for the profile server, a persistent session between an RP and the bookmark service might be desirable.
It would initially take an assertion, then return some kind of token. That token could be a browser cookie or we could use "MACauth".