Minka Ledger Docs
Explanations

About Authorization

DateResponsibleChanges
January 16, 2023@Luis FidelisInitial version
February 27, 2023@Luis Fidelis• Update access.signer schema and rename access.bearer.$key to access.bearer.$signer in order to reuse the same schema.
• Add sign access action.
• Rename meta.signatures[*].schema to meta.signatures[*].method.
March 7, 2023@Filip Herceg• Added digest as part of signature object.
• Refactored signer.schemasigner.format
• Added custom.moment to examples
April 17, 2023@Omar MonterreyAdded sign-intent action to enum
May 25, 2023@Luis Fidelis• Describe circle access constraint for signers.
• Add circle to the list of records.
• Add assign-signer and unassign-signer to the list of actions.
June 14, 2023@Luis FidelisDescribe policy referencing to access rules.
July 19, 2023@Luis FidelisAdd schema, $record and $ledger signer properties.
August 22, 2023@Omar MonterreyRemoved action sign-intent from enum
February 6, 2024@Omar MonterreyUpdated enums for AccessAction and AccessRecord

There are two ways in which clients can authenticate to the ledger, the first by signing the mutation bodies, and the second by sending a JWT token. See About Authentication for more details about it.

These two forms of authentication allow users to say who they are, but do not guarantee that they have privileges to access the ledger. To ensure that users have the necessary privileges to create, update and read ledger records, access control rules can be defined at three levels: server, ledger and record. The authorization layer checks permissions by following the hierarchy:

record → ledger → server.

An access rule can be either a simple rule or a reference to a policy. See About Security Policies for more details about policies.

Access permission rules follow the following format.

type Rule: {
  /**
   * Defines which action the access rule is assigned to
   */
  action: AccessAction
 
  /**
   * Defines which ledger record class the access rule is assigned to
   */
  record?: AccessRecord
 
  /**
   * Define conditions that the subscriber of the request body 
   * must follow to grant access.
   */
  signer?: AccessSigner
 
  /**
   * Define claims and metadata about the JWT token to grant access
   */
  bearer?: AccessBearer
}
 
type AccessPolicy: {
  /**
   * Attaches a policy to the access rule
   */
   policy: string  
}
 
type AccessRule = Rule | AccessPolicy

Where

type AccessAction: 
    'any'             | // Applies to all actions below
    'access'          | // Defines permissions for accessing child records of a record
    'create'          | // Defines permissions for creating a record
    'read'            | // Defines permissions for reading a record
    'drop'            | // Defines permissions for dropping a record
    'update'          | // Defines permissions for updating a record
    'lookup'          | // Defines permissions for looking up from a record
 
    'assign-signer'   | // Defines permissions for assigning a signer to a circle
    'remove-signer'   | // Defines permissions for unassigning a signer from a circle 
 
    'issue'           | // Defines permissions for issuing a symbol
    'destroy'         | // Defines permissions for destroying a symbol
 
    'spend'           | // Defines permissions for spending from a wallet
    'limit'           | // Defines permissions for limiting a wallet
 
    'commit'          | // Defines permission for comitting an intent with a proof
    'abort'             // Defines permission for aborting an intent with a proof

The create action is not valid when defining access to a specific record - record level rules - since the record already exists. Access rules with create action are defined on parent records, usually this is ledger or server.

access is also not valid for records without children, it should be defined either at server or ledger levels. See the section about child records access constraints

type AccessRecord: 
    'any'           | // Applies all records below
    'server'        | // Defines constraints to access the server
    'ledger'        | // Defines constraints to perform actions on ledgers
    'signer'        | // Defines constraints to perform actions on signers
    'symbol'        | // Defines constraints to perform actions on symbols
    'wallet'        | // Defines constraints to perform actions on wallets
    'intent'        | // Defines constraints to perform actions on intents
    'intent-proof'  | // Defines constraints to perform actions on intent proofs
    'effect'        | // Defines constraints to perform actions on effects
    'bridge'        | // Defines constraints to perform actions on bridges
    'circle'        | // Defines constraints to perform actions on circles
    'circle-signer' | // Defines constraints to perform actions on circle signers
    'policy'        | // Defines constraints to perform actions on policies
    'schema'        | // Defines constraints to perform actions on schemas
    'anchor'        | // Defines constraints to perform actions on anchors
    'domain'          // Defines constraints to perform actions on domains

Access constraints must be defined respecting the hierarchy of server > ledger > record which means:

  • The server record rule cannot be set on ledger or record level rules
  • The ledger record rule cannot be set on ledger or record level rules
  • The property record should me omitted or defined with the same value of the record's type when defining access control on record level i.e it's not allowed to define access rules to manage symbol within a wallet record.

The property record can be omitted when defining access rules, and in this case it indicates the access rule is intended to be applied to record which defines the access rule.

type CircleConstraint = string
type CircleAggregation = {
  $in: Array<CircleConstraint>
}
type AccessCircle = CircleConstraint | CircleAggregation
 
enum RecordOwnership {
   Creator = 'creator'
}
 
type AccessSigner =  SignerConstraint | SignerAggregation
type SignerConstraint = {
  handle?: string // defines constraints for signer handle
  format?: string // defines constraints for signer format
  public?: string // defines constraints for signer public key
  $circle?: AccessCircle // defines constraints for signer circles
  schema?: string // defines constraints for signer schema
  $record?: RecordOwnership // defines constraints for the relationship between
                            // the signer and the target record
  $ledger?: RecordOwnership // defines constraints for the relationship between
                            // the signer and the active ledger 
}
type SignerAggregation = {
  $in: Array<SignerConstraint> // signature must respect at least 
                           // one of the constraints defined in the list 
}
 
/** 
 * @example 
 *  
 * Defining a signer aggregation rules which
 * requires the signature of 'owner' signer or 
 * a key pair with` '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
 * public key.
 */
 
{ 
  ...,
  signer: {
     $in: [
	     { 
	       handle: 'owner', 
	     }, 
	     {
	       public: '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
	     }
	   ]
  }
}
 
/** 
 * @example 
 *  
 * Defining a signer rule which requires the signature 
 * of a signer from circle 'admin'
 */
 
{ 
  ...,
  signer: {
	  $circle: 'admin'
  }
}
 
/** 
 * @example 
 *  
 * Defining a signer rule which requires the signature 
 * of the record creator
 */
 
{ 
  ...,
  signer: {
	  $record: 'creator'
  }
}
 
/** 
 * @example 
 *  
 * Defining a signer rule which requires the signature 
 * of the ledger creator
 */
 
{ 
  ...,
  signer: {
	  $ledger: 'creator'
  }
}

signer property is only valid for mutations, since a GET Http request doesn't have a body.

Since a record can hold multiple signatures, these access rules defines that at least one of the signers must fulfill.

type AccessBearer = {
  /**
   * Defines access rule regarding the issuer of bearer token
   *
   * @example company.org
   */
	iss?: string
 
  /**
   * Defines access rule regarding the subject of bearer token
   *
   * @example admin
   */
  sub?: string
 
  /**
   * Defines access rule regarding the audience of bearer token
   *
   * @example ledger
   */
  aud?: string
 
  /**
   * Defines if the request hash is mandatory
   *
   * @example true
   */
  hsh?: boolean
 
  /**
   * Defines the key required to verify the signature of bearer token
   *
   * @example WAweF9PHlboQoW0z8NqhZXFmzUTaV74NRFAd/aILprE=
   */
  $signer?: SignerConstraint
   
} | BearerAggregation
 
/** 
 * @example 
 *  
 * Defining a bearer aggregation rule which
 * requires the token to be signed by 'owner' signer or 
 * a key pair with` '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
 * public key.
 */
{ 
  ...,
  bearer: {
     $in: [{
	     $signer: {
         handle: 'owner'
       }
     }, {
			 $signer: {
         public: '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
      }
     }]
  }
}
 
/** 
 * @example 
 *  
 * Defining a bearer rule which requires the token to be signed 
 * by a signer from circle 'admin'
 */
{ 
  ...,
  bearer: {
	  $signer: {
	    $circle: 'admin'
    }
  }
}

Both AccessSigner and AccessBearer are matcher objects. Please don’t confuse them with Referenced Records. Matcher objects are more flexible because the whole object can be matched instead of just handle.

Child record access constraints

Child record access constraints works as filters and describes minimum conditions required for accessing a child record or a record. They can be set on any record that has child records in the ledger. Most common records like that are ledger and server records.

Those access constraints are checked before validating the actual action the user wants to perform, and it follows the hierarchy

server → ledger → record (top down approach)

Examples:

  1. user A wants to read a symbol , so firstly it must fulfill requirements for accessing the server and then fulfill requirements to access the ledger. Only after those requirements are satisfied, the ledger will check if the user A can perform the read operation on the symbol.

  2. user B wants to create a ledger , so it should have permissions to access the server first. After that the ledger will check if the user has permissions to create a ledger.

Examples of child record access rules at server level:

/**
 * Defines constraints to access the server. To access the server the
 * user must send a token signed by the specified key.
 */
{
  action: 'access',
  bearer: {
     $signer: {
       public: '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
     }
  }
}
 
/**
 * Defines constraints to access a ledger. To access a ledger the
 * user must send a token signed by the specified key.
 */
{
  action: 'access',
  record: 'ledger',
  bearer: {
     $signer: {
       public: '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
     }
  }
}
 
/**
 * Defines constraints to access any record. To access any record the
 * user must send a token signed by the specified key.
 */
 
{
  action: 'access',
  record: 'any',
  bearer: {
     $signer: {
       public: '1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU='
     }
  }
}

Examples of child record access rules at ledger level:

/**
 * Defines constraints to access a ledger. To access the ledger the user
 * must send a token signed by any signer.
 */
 
{
  action: 'access',
  bearer: {
     $signer: {}
  }
}
 
/**
 * Defines constraint to access any wallet. The access any wallet the user
 * must send a token signed by any signer.
 */
 
{
  action: 'access',
  record: 'wallet', 
  bearer: {
     $signer: {}
  }
}

Server level rules

Server level rules are defined via an environment variable named SERVER_ACCESS_RULES set on ledger API.

Access to any record and action can be defined at server level since it's the root level of access control.

/**
 * server access rules that restrict server access to token-signed users 
 * and allow any signer to create ledger instances.
 */
 [
   {
      "action": "access",
      "bearer": {
         "$signer": {}
      }
   },
   {
      "action": "create",
      "record": "ledger",
      "signer": {}
   }
]

Ledger level rules

Ledger level rules are defined in access property by creating a ledger instance.

Access to any record can be defined at ledger level, except from server .

/**
 * Payload of a ledger which allows every
 * signer to perform any 
 * operation on any ledger record.
 */
{
   "hash": "99dbff500c451a7480ce2dc5928875478cb47b2a358baeaf4064a60eadd897a5",
   "data": {
      "handle": "some_ledger",
      "signer": "some_signer",
      "access": [
         {
            "action": "any",
            "record": "any",
            "bearer": {
               "$signer": {}
            }
         }
      ]
   },
   "meta":{
      "proofs":[
         {
            "method": "ed25519-v2",
            "public": "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU=",
            "result": "lPZsbs+BlWnu5Y5SMWH8AflAFzfKIvfvCgQ2dxZHC6D0j91N5o6F90hiWe6B8JV4MqSYsfGTzb9Rpfz8ecSbAg==",
						 "digest": "3f294cf7533bf5c24675ad238fbfd235860fcc2bc02f8d666894726b7f4ce523",
						 "custom": {
							 "moment": "2023-02-20T21:42:10.279Z"
						 }
         }
      ]
   }
}

Record level rules

Record level rules are defined in access property by creating a record.

It can be used to define access to read and update the target record.

/**
 * Payload of a signer which can be updated and read by the signer who
 * created the record.
 */
{
    "hash":"914816628f3481e57a246d4906b90e8b0125fb0f508dd24b5f1a849545f2a5d1",
    "data":{
        "handle": "some_signer",
        "public": "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU=",
        "format": "ed25519-raw",
        "access": [{
            "action": "read",
            "bearer": {
                 "$signer": {
                    public: "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU="
                }
            }
        }, {
            "action": "update",
            "signer": "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU=",
            "bearer": {
                "$signer": {
                    public: "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU="
                }
            }
        }]
    },
    "meta": {
        "proofs": [
            {
            "method": "ed25519-v2",
            "public": "1bZhhSgwDZ5C9pXsD2Q79A7rhxAZPBM3912G+lW/xIU=",
            "result": "lPZsbs+BlWnu5Y5SMWH8AflAFzfKIvfvCgQ2dxZHC6D0j91N5o6F90hiWe6B8JV4MqSYsfGTzb9Rpfz8ecSbAg==",
                "digest": "3f294cf7533bf5c24675ad238fbfd235860fcc2bc02f8d666894726b7f4ce523",
                "custom": {
                    "moment": "2023-02-20T21:42:10.279Z"
                }
            }
        ]
    }
}

On this page