tags: rfc
labels
tokens
- Status: proposed
- Type: new front-end data structures and permissions.
- Related components: SCL, safe-api, authenticator
- Start Date: 16/12/2019
- Discussion: (fill me in with link to RFC discussion - shepherd will complete this)
- Supersedes: (fill me in with a link to RFC this supersedes - if applicable)
- Superseded by: (fill me in with a link to RFC this is superseded by - if applicable)
Summary
Enable both automatic and optional indexing of data on the account level and prevent data silo-ing by applications, with a system of indexed groups, which increase permission flexibility.
Changes to authentication mechanisms will be needed. A macaroon-like, ābearer tokenā system is proposed, utilising BLS to manage permissions and advanced caveats for application permissions. (Limitations in the macaroon implementation make actual macaroon use non-viable for SAFE workflows.)
This enables us to validate data access not only at the client handler, against generic permissions, but also at the data handling layer, validating tokens against specifics of that data.
These changes also bring about the possibility of sharing private labelled data with other accounts, without the need to publish it.
Conventions
- The key words āMUSTā, āMUST NOTā, āREQUIREDā, āSHALLā, āSHALL NOTā, āSHOULDā, āSHOULD NOTā, āRECOMMENDEDā, āMAYā, and āOPTIONALā in this document are to be interpreted as described in RFC 2119.
Assumptions
Knowledge of BLS asymmetric encryption is assumed, as well as SAFE request validation, and ClientHandler
, DataHandler
and Authenticator
flows.
Also, a basic understanding of app ācontainersā is desirable.
RDF data schemes are not specified here.
Motivation
Ensure that applications can access āgroupsā of data, as opposed to only data they PUT/manage in a specific container.
Ensure that, by default, all data PUT by any application can be located by the user (automatic indexing).
Enable sharing of private data via these labels.
Enable better data access and discovery in the account.
Detailed design
Requests
PUTting data
Any data type, published or unpublished CAN have extra labels applied.
Any data type PUT by an application MUST have an app(<appId>)
label applied, (UNLESS it has permission for no-index
, and passes a no-index
flag).
The ClientApis
MUST apply labels passed to the data, MUST create any index that is missing AND add the dataās XorUrl to this index. If the data has a human-readable name, this may be used as the key.
eg:
- App requests the authenticator to create a label.
- Auth creates a
Token
for the app, as above. - Auth creates an index (MData) and PUTs it to the network giving the app the requested access.
- App receives updated Authentication
Token
.
- When PUTing the data, the common public key is added to the labels field and the data is PUT to the network. The address of the data is also added in the respective data index.
Note: This burden of indexing can/possible should be moved to the network later on.
GETting data
To access any data, an application MUST be either a) A data owner (ie creator of the data) or B) have appropriate permission on that data via a label.
(See Application Permissions, below).
eg:
In order to perform any network action, the app will:
- Present the
Token
alongside any request.
The ClientHandler
will then:
- Validate the
Token
ās signature against thePublicId
public key, to determine that the app is still authorised. -
ClientHandler
can then pass on relevant information (LabelPublicKey
s) to theDataHandler
for verification there. -
DataHandler
will then enforce permissions based upon thePublicKey
s presented, (the Appās own, or the labelsās granted to the app.)
Labels
Each label created will have a corresponding BLS SecretKey
to be stored at the account level. the labelās PublicKey
will be stored in the dataās permission sets, as any other key.
This allows the DataHandler
to verify permission just like any other key, and so labelās is an extension of this functionality. This also means there is scope for groups of labelled data to be shared between accounts (more on this later).
Thus a map of LabelName: String
will need to be maintained in a LabelStore
along with the relevant keys and some metadata (RDF) describing the label use.
The LabelStore
is part of the AccessContainer
(not accessible to apps).
`rust
LabelId : UniqueId;
// RDF info about the label, includes sharing info, human-readable name, etc.
LabelMetaData: String;
LabelStore : {
LabelId : (LabelMetaData, SecretKey, PublicKey)
}
Thus
KeySharescan then be assigned to apps or indeed other accounts in order to sign requests for a given label (to be validated by the
DataHandler`)
This also allows updating the label name without impacting the labelled data. Or indeed having pseudonymous labels.
Adding / Removing labels
To Data
An API will be needed to add labels to data (and check that this app has that label permission).
- The update
function of our data APIs should allow for --addLabels <label>
and --removeLabels <label>
additions.
- An API will be needed to remove labels from data (validating that this app has that appropriate label permission).
In general
It is likely to be desirable to remove labels in general. This can be done by simply removing the label from the LabelIndex
struct. And appās permissions.
Sharing Labelled data
SharedTokens
can be generated by an account and shared to another user, giving permission for specific data / labels.
Indexing
Indexing should be an opt-out process, (requiring app permission to do so). Otherwise all data should be indexed.
No-index
A no-index
permission will need to be added. This permission will allow requests to have a no-index
flag set, to avoid indexing.
Without this permission, all data will be indexed.
Indices
Indices are MutableData
objects stored in the accountās root container. Owned by the account.
`rust
EntryName: String; // could be filename as located on computer, xorurl or other. Unique. Human friendlier the better.
EntryInfo: String; // RDF data about the entryā¦ timestamp / file info / link to data.
Index : MutableData< EntryName, EntryInfo >
RootContainer : {
IndexList : {
IndexMetaData // RDF, as per label (is this duplicated?),
LabelName
}
// individual indexes
<indexId> : MutableData< EntryName, EntryInfo >
}
`
MetaData
An index will be a BTreeMap<String, String>
With the key being either a human-readable entry (filename), or the xorurl. The Entry will be a string of RDF metadata including the link
to the data itself, and other metadata (timestamp/ extension/morethings?)
Removing a label from data MUST remove that data from the appropriate index.
Example Label Flow
- An app
X
wants to create labelL
and apply to some data. - App requests Authenticator for permission.
- Label
L
doesnāt exist, so a keypair is created for it in the account. - KeyShares are provided to app X once permission is granted.
- Auth permission is granted, within a timelimit and a Token is minted via a BLS key-pair sign. The token sez āHe who bears this, can manage label
L
, for two hours from < now >ā - Token is returned to app
X
-
PublicKey
is stored in the accountAccessContainer
for retrieval later. -
SecretKey
is stored by Authenticator
When X
wants to access data, it must:
- Make a GET request, and pass along the token.
-
ClientHandler
s validates that the token matches the request type (ie has PUT permission if a PUT requests -
ClientHandler
s validates that the token with a PublicKey which has been stored in theAccessContainer
. -
ClientHandler
s validates that things are still within the specified timeframe -
ClientHandler
s agree the request is valid and send toDataHandler
-
DataHandler
checks the tokenāslabels
against those on the data. If aPublicKey
stored in the dataāsPermissions
array validates the KeyShare stored in the token, the request is valid and the GET is done
Application Permissions
Macaroon inspired bearer tokens will be used as the application authentication system.
Removing Permissions from Client Handler
Token
Upon authorising an application the Authenticator
will:
- Create a BLS keypair for the application.
- Store the app, PK at the ClientHandler, for verification later.
- Store the SK in the Authenticator, linked to the appId
- Generate and sign the applicationās token, with all permissions and caveats stored therein.
- Pass this signed token to the app (along with apps own sign-keys)
`rust
// Token structure:
CaveatName: String;
PermissionRestriction = Enum< āreadā | āwriteā | āmanagepermissionā >
LabelCaveatContents = ( , , Option);
CaveatContents: String | LabelCaveatContents;
Caveat : ( CaveatName, CaveatContents )
// to locate correct ClientHandlers
SectionInfo: AccountPublicKey as Xorname;
ProtoToken = serde::serialize(( SectionInfo, Vec ));
Sig = secret_key.sign( ProtoToken );
Token = Token + Signature
`
This application token will hold all permissions granted (eg, balance
, transfer
etc, currently managed in the ClientHandler
)
Shared Tokens
Ownership tied token implementation idea:
SharedToken
s are signed by the Accountās SecretKey
, in order to be validated at the DataHandler
against both the ownership keys
and the appropriate permissions for LabelPublicKeys
in the Token
.
SharedToken
s are treated differently, and not validated at the ClientHandler
.
Shared data MUST be identified as such in the LabelMetaData
, along with an identifier for the recipient (inbox xorurl
or safe-id
).
ClientHandler skipping tokens:
SharedToken
s are signed by the Accountās SecretKey
, but they lack (or have) a caveat to indicate that ClientHandlers
to pass on this token directly to data handlers regardless of LabelKey
presence for that app.
The Token
is singed by a LabelKeyShare
, which can easily be verified at the DataHandler
against its PublicKeyShares
as normal. (ie, instead of verifying a SignedLabelCaveat
, the DataHandler
verifies the token itself). This means arbitrary tokens can be created/passed + validated, with other caveats.
Caveat Example
Using label identifiers (which could be PK for each label or a unique ID stringā¦)
`rust
//labelId is arbitrary msg to sign for validation at DataHandler
let label_caveat = (ālabelsā, vec![(, , Some<āreadā>), (, āsignatureForSandwhichSkShareā)])
let get_balance_caveat = (āget_balanceā, false)
`
Token Invalidation
To revoke an applicationās permissions, the authenticator needs simply to remove the applicationās PublicKey
from the appās ClientHandler
, and remove the SecretKey
from the authenticatorās data struct, meaning a ClientHandler
could no longer verify an already existing Token
.
Invalidating SharedTokens
will need removal of LabelPublicKey
from the data.
Drawbacks
The need to send all label-ids to the DataHandler
could lead to increased request size.
Alternatives
A Pure BLS implementation could use sign key shares, and pass a key share to an application. This however requires more management and maintenance of ClientHandler
data structs than the proposed macaroon-esque setup (which also can gain us the ability to verify specific ācaveatsā depending on whatās encoded into the token.)
SecretKeyShares could be used as a simple alternative to SharedTokens
, needing less validation, but having less potential than passing SharedTokens
as they can have caveats attached.
Unresolved questions
Index Metadata
What should be included in an indexās metadata, and the metadata for each entry?
Optimisations
How far to optimise, and what to target. (To be decided as bottle necks identified).
Options:
- Transfer, spend, other bools outwith of token to avoid deserialisation at CH?
- Client notifying of appropriate labels for known data
-
DataHandler
lettingClientHandler
know whatās relevant, allowingToken
pruning. -
ClientHandler
caching known dataās labels (as received fromDatahandler
)
Changelog
2020-01-29
- Added examples of label flow for token issuing / authorization of labels on data.
- Add a second SharedToken idea, so not tied to data controlled by issuer per-se.
- Improved Summary
- Added some thoughts on async token re-issue