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
AppTokenHashes
struct - 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
SignedLabelShare
caveat 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) -
AuthKeys
data struct (to becomeAppTokenHashes
, and referred to as such). Stored atClientHandler
nodes, and accessible byAuthenticator
-
Apps
themselves. -
AuthConfig
: AnAuthenticator
only 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
AppSignKeyPair
in 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 PUT
s.
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
AppTokenHashes
entry - All caveats of concern at that moment (ie,
ClientHandling
code may verify different caveats toDataHandling
code, 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
DataHandler
could lead to increased request size. - Passing tokens with requests can increase request size.