facundo-rodriguez-17
9/22/2017 - 5:45 PM

Example of OAuth2 autentication server with Client Credentials grant (using python-oauth2 and tornado)

Example of OAuth2 autentication server with Client Credentials grant (using python-oauth2 and tornado)

tornado
python-oauth2
redis
fakeredis
mongomock
pymongo

Client Step By Step

Get a Token

Request

curl -i http://localhost:8889/oauth/token -X POST -H 'Content-Type: application/json' -d '{"client_id": "abc", "client_secret": "xyz", "grant_type": "client_credentials", "scope": "foo"}'

Response

HTTP/1.1 200 OK
Content-Length: 100
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
Date: Tue, 18 Nov 2014 22:32:18 GMT
Server: TornadoServer/4.0.2
Proxy-Connection: keep-alive
Connection: keep-alive

{"token_type": "Bearer", "expires_in": 3600, "access_token": "7d2adcd2-2756-4531-b7d2-69c19f5b1063"}

Consumer Resource

Request

curl -i http://localhost:8889/foo -H 'Authorization: Bearer 7d2adcd2-2756-4531-b7d2-69c19f5b1063'

Response

HTTP/1.1 200 OK
Etag: "e8ac30e7653f247f956a04b1f901d893e593cd1b"
Content-Length: 23
Date: Tue, 18 Nov 2014 22:33:15 GMT
Content-Type: text/html; charset=UTF-8
Server: TornadoServer/4.0.2
Proxy-Connection: keep-alive
Connection: keep-alive

{"msg": "This is Foo!"}
# !/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Diego Garcia'

import tornado.web
import tornado.ioloop
import oauth2.tokengenerator
import oauth2.grant
import oauth2.store.redisdb
import oauth2.store.mongodb
import json
import time
import fakeredis
import mongomock


class OAuth2Handler(tornado.web.RequestHandler):
    # Generator of tokens (with client authentications)
    def initialize(self, controller):
        self.controller = controller

    def post(self):
        response = self._dispatch_request()

        self._map_response(response)

    def _dispatch_request(self):
        request = self.request
        request.post_param = lambda key: json.loads(request.body.decode())[key]

        return self.controller.dispatch(request, environ={})

    def _map_response(self, response):
        for name, value in list(response.headers.items()):
            self.set_header(name, value)

        self.set_status(response.status_code)
        self.write(response.body)


class BaseHandler(tornado.web.RequestHandler):
    def initialize(self, controller):
        self.controller = controller

    # authenticate tokens
    def prepare(self):
        try:
            token = self.get_argument('access_token', None)
            if not token:
                auth_header = self.request.headers.get('Authorization', None)
                if not auth_header:
                    raise Exception('This resource need a authorization token')
                token = auth_header[7:]

            key = 'oauth2_{}'.format(token)
            access = self.controller.access_token_store.rs.get(key)
            if access:
                access = json.loads(access.decode())
            else:
                raise Exception('Invalid Token')
            if access['expires_at'] <= int(time.time()):
                raise Exception('expired token')
        except Exception as err:
            self.set_header('Content-Type', 'application/json')
            self.set_status(401)
            self.finish(json.dumps({'error': str(err)}))


class FooHandler(BaseHandler):
    def get(self):
        self.finish(json.dumps({'msg': 'This is Foo!'}))


def main():
    # Populate mock
    mongo = mongomock.MongoClient()
    mongo['db']['oauth_clients'].insert({'identifier': 'abc',
                                         'secret': 'xyz',
                                         'redirect_uris': [],
                                         'authorized_grants': [oauth2.grant.ClientCredentialsGrant.grant_type]})
    # MongoDB for clients store
    client_store = oauth2.store.mongodb.ClientStore(mongo['db']['oauth_clients'])

    # Redis for tokens storage
    token_store = oauth2.store.redisdb.TokenStore(rs=fakeredis.FakeStrictRedis())

    # Generator of tokens
    token_generator = oauth2.tokengenerator.Uuid4()
    token_generator.expires_in[oauth2.grant.ClientCredentialsGrant.grant_type] = 3600

    # OAuth2 controller
    auth_controller = oauth2.Provider(
        access_token_store=token_store,
        auth_code_store=token_store,
        client_store=client_store,
        site_adapter=None,
        token_generator=token_generator
    )
    auth_controller.token_path = '/oauth/token'


    # Add Client Credentials to OAuth2 controller
    auth_controller.add_grant(oauth2.grant.ClientCredentialsGrant())

    # Create Tornado application
    app = tornado.web.Application([
        (r'/oauth/token', OAuth2Handler, dict(controller=auth_controller)),
        (r'/foo', FooHandler, dict(controller=auth_controller))
    ])

    # Start Server
    app.listen(8889)
    print("Server Starting")
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()