BEdita REST API

REST

Representational state transfer

  • is a software architecture style for building scalable web services.
     
  • communicate over the HyperText Transfer Protocol (HTTP) with the same HTTP verbs (GET, POST, PUT, DELETE, etc.) used by web browsers to retrieve/send data from/to web server

REST

Architectural constraints

  • Client–server
    A uniform interface separates clients from servers. 
  • Stateless
    The client–server communication is further constrained by no client context being stored on the server between requests. Each request from any client contains all the information necessary to service the request, and session state is held in the client
  • Cacheable
    clients and intermediaries can cache responses. Responses must therefore, implicitly or explicitly, define themselves as cacheable, or not, to prevent clients from reusing stale or inappropriate data in response to further requests
  • Code on demand (optional)
    Servers can temporarily extend or customize the functionality of a client by the transfer of executable code.

REST

Architectural constraints

  • Uniform interface
    Identification of resources
    Individual resources are identified in requests (URI). The resources themselves are conceptually separate from the representations that are returned to the client (server DB => client JSON)
    Manipulation of resources through these representations
    When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource
    Self-descriptive messages
    Each message includes enough information to describe how to process the message.  For example, which parser to invoke

    Hypermedia as the engine of application state (HATEOAS)
    Clients make state transitions only through actions that are dynamically identified within hypermedia by the server (e.g., by hyperlinks within hypertext)

REST

Request methods (HTTP VERBS)

GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS,...

Safe methods

"Safe" means that if a given HTTP method is invoked, the resource state on the server remains unchanged. GET is safe 

Idempotent methods

"idempotent" means that multiple identical requests should have the same effect as a single request .

GET, PUT, DELETE are idempotent

​HTTP Method ​Safe Idempotent
GET Yes Yes
POST No No
PUT No Yes
DELETE No Yes

is BEdita API RESTful compliant?

 

NO!

But... WTF!

74% of APIs claiming to be “RESTful” do not actually use hypermedia* 

Setup frontend to consume API

$ cd /path/to/bedita
$ ./cake.sh frontend init
// frontend.ini.php or frontend.cfg.php
$config['api'] = array(
    'baseUrl' => '/api/v1'
);

// core.php (used for authentication)
Configure::write('Security.salt', 'my-security-random-string');

Init frontend

Minimum setup

API response

{
    "api": "objects",
    "data": {},
    "method": "get",
    "params": [],
    "url": "http://example.com/api/v1/objects/1"
}



{
    "api": "objects",
    "data": {...},
    "method": "get",
    "paging": {
        "page": 1,
        "page_size": 10,
        "page_count": 10,
        "total": 995,
        "total_pages": 100
    },
    "params": [],
    "url": "http://example.com/api/v1/objects/1/children"
}

API errors

{
    "error": {
        "status": 405,
        "code": null,
        "message": "Method Not Allowed",
        "details": "Method Not Allowed",
        "more_info": null,
        "url": "http://example.com/api/v1/foobar"
    }
}

HTTP Status Code: 405 Method Not Allowed 

Header HTTP

Payload

GET API /objects

GET /api/objects

Dispatcher,
Routes,
Controller

ApiValidator

Get data

from datasource

ApiFormatter

Response

POST API /objects

POST /api/objects

Dispatcher,
Routes,
Controller

ApiValidator

Save

data

Response

ApiFormatter

Prepare response

ApiAuth

Unauthorized

token valid

ApiValidator Component

  • Check if an object is reachable
  • Check if an object is accessible
  • Validate data before save
  • ...

It takes care of validate data, for example

ApiFormatter Component

  • clean objects from unwanted fields
  • cast fields in the right type (integer, boolean, date ISO 8601, ...) using transformers
  • add to objects the formatted count of relations/children
  • format pagination data
  • format data for save

It takes care of data to make it consistent and well formatted

Authentication

The API follow a token based authentication flow using a Json Web Token as access_token and an opaque token as refresh_token useful to renew the access_token without ask again the user credentials.

The access_token must be used in every request that require permission. To use the access_token it has to be sent in HTTP headers as bearer token

+--------+                                     +---------------+
|        |--(A)- Authorization Request ------->|   Resource    |
|        |                                     |     Owner     |
|        |<-(B)-- Authorization Grant ---------|               |
|        |                                     +---------------+
|        |
|        |                                     +---------------+
|        |--(C)-- Authorization Grant -------->| Authorization |
| Client |                                     |     Server    |
|        |<-(D)----- Access Token (JWT) -------|               |
|        |                and                  |               |
|        |           Refresh Token             |               |
|        |                                     +---------------+
|        |
|        |                                     +---------------+
|        |--(E)----- Access Token (JWT) ------>|    Resource   |
|        |                                     |     Server    |
|        |<-(F)--- Protected Resource ---------|               |
+--------+                                     +---------------+
Authorization: Bearer eyJ0eXAi......

Json Web Token (JWT, pronounced jot)

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

  • JWT ships information that can verified and trusted with digital signature (JWS)
  • JWT allows the server to verify the information contained in the JWT  without necessarily storing state on the server (Stateless)
  • JWT works across different programming languages (many libraries ready to use)
  • JWT is self-contained: it will carry all the information necessary within itself
  • JWT can be passed around easily

JWT anathomy

A JWT is represented as a sequence of base64url encoded json (URL-safe) that are separated by period ('.') characters.

It is composed by three parts:

  1. header
  2. payload
  3. signature
aaaaaa
bbbbbb
cccccc
.
.

JWT Header

The header contains the metadata for the token (type of token and the algorithm used to sign the token).

{
    "typ": "JWT",
    "alg": "HS256"
}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

base64url encoded

JWT Payload

Carry JWT Claims (informations we want to transmit).  There are registered claims, public claims and private claims.

Registered Claims (optional)

  • iss: The issuer of the token
  • sub: The subject of the token
  • aud: The audience of the token
  • exp: Token expiration time defined in Unix time
  • nbf: “Not before” time that identifies the time before which the JWT must not be accepted for processing
  • iat: “Issued at” time, in Unix time, at which the token was issued
  • jti: JWT ID claim provides a unique identifier for the JWT

 

JWT Payload

{
    "iss": "https://example.com",
    "iat": "1441749523",
    "exp": "1441707000",
    "id": "15"
}
eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoiMTQ0MTc0OTUyMyIsImV4cCI6IjE0NDE3MDcwMDAiLCJpZCI6IjE1In0

base64url encoded

JWT Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoiMTQ0MTc0OTUyMyIsImV4cCI6IjE0NDE3MDcwMDAiLCJpZCI6IjE1In0.DcsoLlT1a_wivzFSCO9XjvTW84VK_hFGJJzmohCsFdE

The JWT standard follows the JSON Web Signature (JWS) specification to generate the final signed token.

HMACSHA256(
    base64UrlEncode(header) + '.' + base64UrlEncode(payload),
    'secret'
)

JWT

Default API endpoints

  • /auth
  • /objects
  • /poster
  • /me

What can I do to customize API?

  • custom base url response
  • custom endpoints
  • blacklist default endpoints
  • whitelist object type endpoints
  • custom objects filters
  • custom pagination
  • configure allowed origins
  • configure fields to remove
  • configure auth component
  • configure objects writable
$config['api'] = array(
    'baseUrl' => '/api/v1',
    'allowedOrigins' => array(),
    'auth' => array(
        'component' => 'MyCustomAuth',
        'JWT' => array(
            'expiresIn' => 600,
            'alg' => 'HS256'
        ),
    ),
    'formatting' => array(
        'fields' => array(
            // fields that should be removed from results
            'remove' => array(
                'title',
                'Category' => array('name')
            ),
            // fields (removed by default) that should be kept
            'keep' => array(
                'ip_created',
                'Category' => array('object_type_id', 'priority')
            )
        )
    ),
    'validation' => array(
        'writableObjects' => array('document', 'event')
    )
);

What is missing?

  • /objects/:id/relations/:rel_name add 'relations_params' to objects??
  • filter objects by query url:  /objects?object_type=document,gallery
  • handle API keys (dedicated table? using hash_jobs? to trace client_id and client_secret)
  • handle rate limit
  • conf to make all API protected (now GET is public unless the resource is protected with permissions)
  • caching
  • save custom properties
  • check on params relations
  • categories and tags endpoints?
  • HATEOAS (handle OPTIONS to discover request supported on API endpoint using Header Allow: GET, POST, ..)
  • handle sort in pagination
  • other?

fin

BEdita REST API

By Alberto Pagliarini

BEdita REST API

  • 822