Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/miguelgrinberg/REST-auth

Example application for my RESTful Authentication with Flask article.
https://github.com/miguelgrinberg/REST-auth

Last synced: about 2 months ago
JSON representation

Example application for my RESTful Authentication with Flask article.

Awesome Lists containing this project

README

        

REST-auth
=========

Companion application to my [RESTful Authentication with Flask](http://blog.miguelgrinberg.com/post/restful-authentication-with-flask) article.

Installation
------------

After cloning, create a virtual environment and install the requirements. For Linux and Mac users:

$ virtualenv venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt

If you are on Windows, then use the following commands instead:

$ virtualenv venv
$ venv\Scripts\activate
(venv) $ pip install -r requirements.txt

Running
-------

To run the server use the following command:

(venv) $ python api.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader

Then from a different terminal window you can send requests.

API Documentation
-----------------

- POST **/api/users**

Register a new user.

The body must contain a JSON object that defines `username` and `password` fields.

On success a status code 201 is returned. The body of the response contains a JSON object with the newly added user. A `Location` header contains the URI of the new user.

On failure status code 400 (bad request) is returned.

Notes:
- The password is hashed before it is stored in the database. Once hashed, the original password is discarded.
- In a production deployment secure HTTP must be used to protect the password in transit.

- GET **/api/users/<int:id>**

Return a user.

On success a status code 200 is returned. The body of the response contains a JSON object with the requested user.

On failure status code 400 (bad request) is returned.

- GET **/api/token**

Return an authentication token.

This request must be authenticated using a HTTP Basic Authentication header.

On success a JSON object is returned with a field `token` set to the authentication token for the user and a field `duration` set to the (approximate) number of seconds the token is valid.

On failure status code 401 (unauthorized) is returned.

- GET **/api/resource**

Return a protected resource.

This request must be authenticated using a HTTP Basic Authentication header. Instead of username and password, the client can provide a valid authentication token in the username field. If using an authentication token the password field is not used and can be set to any value.

On success a JSON object with data for the authenticated user is returned.

On failure status code 401 (unauthorized) is returned.

Example
-------

The following `curl` command registers a new user with username `miguel` and password `python`:

$ curl -i -X POST -H "Content-Type: application/json" -d '{"username":"miguel","password":"python"}' http://127.0.0.1:5000/api/users
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 27
Location: http://127.0.0.1:5000/api/users/1
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 19:56:39 GMT

{
"username": "miguel"
}

These credentials can now be used to access protected resources:

$ curl -u miguel:python -i -X GET http://127.0.0.1:5000/api/resource
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 30
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 20:02:25 GMT

{
"data": "Hello, miguel!"
}

Using the wrong credentials the request is refused:

$ curl -u miguel:ruby -i -X GET http://127.0.0.1:5000/api/resource
HTTP/1.0 401 UNAUTHORIZED
Content-Type: text/html; charset=utf-8
Content-Length: 19
WWW-Authenticate: Basic realm="Authentication Required"
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 20:03:18 GMT

Unauthorized Access

Finally, to avoid sending username and password with every request an authentication token can be requested:

$ curl -u miguel:python -i -X GET http://127.0.0.1:5000/api/token
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 139
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 20:04:15 GMT

{
"duration": 600,
"token": "eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4NTY2OTY1NSwiaWF0IjoxMzg1NjY5MDU1fQ.eyJpZCI6MX0.XbOEFJkhjHJ5uRINh2JA1BPzXjSohKYDRT472wGOvjc"
}

And now during the token validity period there is no need to send username and password to authenticate anymore:

$ curl -u eyJhbGciOiJIUzI1NiIsImV4cCI6MTM4NTY2OTY1NSwiaWF0IjoxMzg1NjY5MDU1fQ.eyJpZCI6MX0.XbOEFJkhjHJ5uRINh2JA1BPzXjSohKYDRT472wGOvjc:x -i -X GET http://127.0.0.1:5000/api/resource
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 30
Server: Werkzeug/0.9.4 Python/2.7.3
Date: Thu, 28 Nov 2013 20:05:08 GMT

{
"data": "Hello, miguel!"
}

Once the token expires it cannot be used anymore and the client needs to request a new one. Note that in this last example the password is arbitrarily set to `x`, since the password isn't used for token authentication.

An interesting side effect of this implementation is that it is possible to use an unexpired token as authentication to request a new token that extends the expiration time. This effectively allows the client to change from one token to the next and never need to send username and password after the initial token was obtained.

Change Log
----------

**v0.3** - Return token duration.

**v0.2** - Return a 201 status code and Location header from */api/users* endpoint.

**v0.1** - Initial release.