When I started working on my new project I decided to use Google App Engine and Ferris 2. Since App Engine had to be used only for backend, the next decision was AngularJS and JSON Web Tokens. At the moment I switched to Ferris 3 (it is a great tool!) but I will write here only about how to solve the problem with CORS.
Let's start with Ferris 2 authorization chain. This method is responsible for verification of a user token. It is very simple, it gets Authorization header, gets secret token, tries to decode token, checks expiration time, returns user in controller.user if token is correct, otherwise it returns False. You can read more here.
from datetime import datetime import jwt from ferris import settings def require_user(controller): token = controller.request.headers.get('Authorization') if not token: return False try: payload = jwt.decode( token, settings.get('app_config').get('token_secret') ) except jwt.DecodeError: return False if datetime.fromtimestamp(payload['exp']) < datetime.now(): return False controller.user = payload['user'] return True
import functools def allow_options(function): @functools.wraps(function) def inner(self, *args, **kwargs): if self.request.method == 'OPTIONS': self.response.headers.add_header( 'Access-Control-Allow-Origin', 'http://localhost:9000' ) self.response.headers.add_header( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ) self.response.headers.add_header( 'Access-Control-Max-Age', str(1000) )
Of course you need to replace http://localhost:9000 with your frontend address and you can change values in Access-Control-Allow-Methods and Access-Control-Max-Age if you need this. I just tried it only on localhost (because I moved to Ferris 3 and endpoints) but it should work correctly. It just catches a requests with OPTIONS method, adds required headers and return response so the next request should get correct answer instead of error about domain not allowed by Access-Control-Allow-Origin.
Short example how to use it:
from ferris.core.controller import add_authorizations from ferris import route from app.utils.auth import ( allow_options, require_user ) @allow_options @route @add_authorizations(require_user): def profile(self, username): user = self.controller.user