Authentication¶
plone.restapi
uses Plone PAS for Authentication.
That means that any authentication method supported by an installed PAS Plugin should work, assuming it’s an authentication method that makes sense to use with an API.
For example, to authenticate using HTTP basic auth, you’d set an Authorization
header:
GET /Plone HTTP/1.1
Authorization: Basic Zm9vYmFyOmZvb2Jhcgo=
Accept: application/json
HTTP client libraries usually contain helper functions to produce a proper Authorization
header for you based on given credentials.
Using the requests
library, you’d set up a session with basic auth like this:
import requests
session = requests.Session()
session.auth = ('username', 'password')
session.headers.update({'Accept': 'application/json'})
response = session.get(url)
Or the same example using curl
:
curl -u username:password -H 'Accept:application/json' $URL
JSON Web Tokens (JWT)¶
plone.restapi
includes a Plone PAS plugin for authentication with JWT. The
plugin is installed automatically when installing the product.
Acquiring a token (@login)¶
A JWT token can be acquired by posting a user’s credentials to the @login
endpoint.
POST /plone/@login HTTP/1.1
Accept: application/json
Content-Type: application/json
{
"login": "admin",
"password": "secret"
}
curl -i -X POST http://nohost/plone/@login -H 'Accept: application/json' -H 'Content-Type: application/json' --data-raw '{"login": "admin", "password": "secret"}'
echo '{
"login": "admin",
"password": "secret"
}' | http POST http://nohost/plone/@login Accept:application/json Content-Type:application/json
requests.post('http://nohost/plone/@login', headers={
'Accept': 'application/json',
'Content-Type': 'application/json',
}, json={
'login': 'admin',
'password': 'secret',
})
The server responds with a JSON object containing the token.
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0"
}
Authenticating with a token¶
The token can now be used in subsequent requests by including it in the
Authorization
header with the Bearer
scheme:
GET /plone/ HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0
curl -i http://nohost/plone/ -H 'Accept: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
http http://nohost/plone/ Accept:application/json Authorization:'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
requests.get('http://nohost/plone/', headers={
'Accept': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0',
})
Renewing a token (@login-renew)¶
By default the token will expire after 12 hours and thus must be renewed before
expiration. To renew the token simply post to the @login-renew
endpoint.
POST /plone/@login-renew HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0
curl -i -X POST http://nohost/plone/@login-renew -H 'Accept: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
http POST http://nohost/plone/@login-renew Accept:application/json Authorization:'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
requests.post('http://nohost/plone/@login-renew', headers={
'Accept': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0',
})
The server returns a JSON object with a new token:
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0"
}
Invalidating a token (@logout)¶
The @logout
endpoint can be used to invalidate tokens. However by default
tokens are not persisted on the server and thus can not be invalidated. To enable
token invaldiation, activate the store_tokes
option in the PAS plugin. If you
need tokens that are valid indefinitely you should also disable the use of Plone’s
keyring in the PAS plugin (option use_keyring
).
The logout request must contain the existing token in the Authorization
header.
POST /plone/@logout HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0
curl -i -X POST http://nohost/plone/@logout -H 'Accept: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
http POST http://nohost/plone/@logout Accept:application/json Authorization:'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0'
requests.post('http://nohost/plone/@logout', headers={
'Accept': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmdWxsbmFtZSI6IiIsInN1YiI6ImFkbWluIn0.RVl8ZFJWIaA-8ujyulJvw0j3F3qFjIHDIJFK0GF6j_0',
})
If invalidation succeeds, the server responds with an empty 204 reponse:
HTTP/1.1 204 No Content
Permissions¶
In order for a user to use the REST API, the plone.restapi: Use REST API
permission is required.
By default, installing the plone.restapi:default
profile will assign this
permission to the Anonymous
role, so everybody is allowed to use the REST
API by default.
If you wish to control in more detail which roles are allowed to use the REST API, please assign this permission accordingly.
As well as the plone.restapi: Use REST API
permission some of the common
Plone permissions are also required, depending on the particular service.
For example, retrieving a resource using GET will require View
, adding an
object using POST will require Add portal content
, and so on.
In order to modify/override this behavior, if your custom service class
inherits from plone.restapi.services.Service
, just override the method
check_permission
and add your custom checks accordingly.