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:
- header
- payload
- 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