What follows is an updated RFC for Tokens. Separated out from the previous Label/Token/RFC for clarity. (Labels will get its own updated RFC shortly).
The only practical change from the prior RFC beyond the increased clarity is a need to determine what is the ‘current’ token (due to UX updates around permissions), and so the inclusion of a TokenHashes storage for ClientHandler access.
Tokens
- Status: proposed
- Type: new authentication mechanism
- Related components: SCL, safe-api, authenticator, safe-nd
- Start Date: 28/01/2020
- Supersedes: Tokens/Labels/Auth
Summary
Changes to authentication mechanisms needed for Labels implementation.
This is a macaroon-like, ‘bearer token’ system, 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 in the Client Handling code, against generic permissions, but also at the Data Handling layer, validating tokens against specifics of that data.
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.
Motivation
Enables checking of arbitrary App’s permissions / caveats based upon a token which can be passed through to vaults, and therefore checked against the data itself.
Simplifies adding/updating permissions to the token struct, and checking those becomes only a matter of (optionally) verifying the desired caveat.
Detailed design
Data operations
To access any non public data, a (non revoked) application MUST present a token which is
- Valid
- Current (as determined by token sha3-256 hashing) against
AppTokenHashesstruct - Be valid for relevant caveats at
ClientHandling(this could be expiry, or permission checks against the current request for mutations etc)
Or:
- Have an appropriate
SignedLabelSharecaveat to be validated atDataHandling.
Data storage
There are three key locations relevant for token storage.
AccessContainer(suggested to rename toAppDataStorage). Writeable byAuthenticator, and readable by any application (so each app’s entry needs to be encrypted with the application’s sign keys)AuthKeysdata struct (to becomeAppTokenHashes, and referred to as such). Stored atClientHandlernodes, and accessible byAuthenticatorAppsthemselves.AuthConfig: AnAuthenticatoronly accessible location for private data.
Token Creation
Upon authorising an application the Authenticator will:
- Create App signing key pair,
AppSignKeyPair, and store this (as currently, inAuthConfig) - Generate and sign the application’s token with the
Client SecretKey, with all permissions and caveats stored therein. - Pass this signed token to the
App(along with apps own sign-keys) - Store this token, encrypted with the
AppSignKeyPairin theAccessContainer. (This can be updated later if app permissions change) - Store a hash of the token to check its expiration, at
AppTokenHashes// Token structure: #[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)] pub struct AuthToken { /// Auth token version pub version: usize, /// Caveats for verifying for token to be considered valid. pub caveats: Vec<Caveat>, /// Signature of the serialized token for validation against tampering. pub signature: Option<Signature>, } pub type CaveatName = String; pub type CaveatContents = String; //| LabelCaveatContents; pub type Caveat = (CaveatName, CaveatContents);
This application token will hold all permissions granted (eg, balance, transfer etc, currently managed in the ClientHandler).
Caveats
Caveats are arbitrary string entries to be optionally verified at various stages of the request.
A suggested implementation would be to follow macroon style verifiers .
In practice this would be check, PUT requests for example, have a valid signed token, which contains a caveat allowing PUTs.
Token Validation
At Client Handling, we need to verify:
- That the app itself is not revoked (as current)
- the token itself, by checking the signature against the tokens contents.
- That the token is current, by sha-256 hashing and comparing to latest stored
AppTokenHashesentry - All caveats of concern at that moment (ie,
ClientHandlingcode may verify different caveats toDataHandlingcode, which has access to different information)
Token Invalidation
Revoking an application’s permissions, happens as currently implemented: the authenticator removes the applications auth key.
Token Deprecation
It can happen that a user adjusts an applications permission, without immediately informing the app (and issuing a new token).
As Tokens are stored for re-issue, any updated Token will be saved in the AccessContainer, and a new token hash will be stored at AppTokenHashes.
ClientHandling code will check for token deprecation by comparing passed token signature against the latest AppTokenHashes entry. A failure to match will be result in a TokenNotCurrent error.
To avoid this, Clients should automatically check for new tokens on first failed requests with refresh_access_info() in Safe Client Libs.
Token Reissue
Similar to how we currently store app sign keys, Tokens will be stored by the authenticator in the AccessContainer. Updated tokens here can be retrieved by applications via refresh_access_info() (eventually this can be automated in the app client code).
Shared Data Tokens
With Labels on the horizon, the ability to share data via validation of caveats at DataHandling is opened up.
This could be done via signing the Token with a LabelShare and indicating that the token is a shared data token, to avoid certain ClientHandler checks, and passing to DataHandling, where the labels validity can be checked against LabelPublicKeyShares on the data to determine validity.
Drawbacks
- The need to send all label-ids to the
DataHandlercould lead to increased request size. - Passing tokens with requests can increase request size.
