January 18, 2015
November 25, 2019
Ferris 2 (Google App Engine), JSON Web Token and CORS
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 datetimeimport jwtfrom ferris import settingsdef require_user(controller):token = controller.request.headers.get('Authorization')if not token:return Falsetry:payload = jwt.decode(token, settings.get('app_config').get('token_secret'))except jwt.DecodeError:return Falseif datetime.fromtimestamp(payload['exp']) < datetime.now():return Falsecontroller.user = payload['user']return True
import functoolsdef 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_authorizationsfrom ferris import routefrom app.utils.auth import (allow_options,require_user)@allow_options@route@add_authorizations(require_user):def profile(self, username):user = self.controller.user