nameko token authentication

279 views
Skip to first unread message

darko....@gmail.com

unread,
Nov 27, 2017, 9:23:10 AM11/27/17
to nameko-dev
Hello!

I started to play with nameko.

I made one rpc service with sqlite as backend,

one http gateway service (as in nameko-examples)

and one rpc auth service which generates jwt token.


gateway service has auth endpoint which calls auth service authenticate (username, password) and returns token.

Other gateway endpoint methods call the other rpc service, I send token in http header and then pass that token to rpc service methods as explicit method parameter.
rpc service then checks if token is valid and if specified role is granted for the operation, best by calling auth service.


Is there a better way of doing something like that, token authentication and authorization?


Darko

jakub...@student.com

unread,
Nov 27, 2017, 12:08:33 PM11/27/17
to nameko-dev
Hi Darko,

I think there is few things you can improve here.

You could grab JWT out of HTTP headers and add it to Nameko's worker context data. Once you do that, when you make rpc calls to any downstream services context will travel with requests and you won't have to specifically pass it as method parameters.

Grab Http Authorization Header and add it to context:

from nameko.web.server import WebServer as BaseWebServer
from nameko.web.handlers import HttpRequestHandler as BaseHttpRequestHandler

class WebServer(BaseWebServer):

 
def context_data_from_headers(self, request):
 context_data
= super().context_data_from_headers(request)
 context_data
['authorization'] = self.get_auth_token(request)
 
return context_data

 
@staticmethod
 
def get_auth_token(request):
   auth
= request.headers.get('Authorization', None)

   
if not auth:
     
return

   parts
= auth.split()

   
if len(parts) != 2:
     
raise BadRequest(
       
'Authorization header must be auth-scheme + \s + auth-param'
     
)
   
return {'scheme': parts[0], 'param': parts[1]}

class HttpEntrypoint(BaseHttpRequestHandler):

 server
= WebServer()

http
= HttpEntrypoint.decorator

Use http decorator above as your default @http decorator in your gateway service

Then you would want to combine it with some sort of auth dependency that will read and validate JWT. This can be shared dependency that gateway and all downstream services use:

import jwt
from nameko.extensions import DependencyProvider


class Auth(DependencyProvider):

 
def get_dependency(self, worker_ctx):
   
self.secret = 'secretive_secret'  # Grab it from config
   
self.jwt = None

   auth
= self.worker_ctx.data.get('authorization', None)

   
if auth and auth['scheme'].lower() == 'bearer':
     
self.jwt = auth['param']

 
def decode_jwt(self):
   
"""Decode a JWT using the given secret and algorithm."""
   
return jwt.decode(
     
self.jwt, self.secret, algorithm='HS256', issuer='auth'
   
)

Then use it in your service:

from . import Auth, http

class MyService:
  name
= 'my-service'

  auth
= Auth()

 
@http('GET', '/')  
 
def get_something(self, request):
    jwt_payload
= self.auth.decode_jwt()

Now if you make rpc call from within any method in your service it will carry JWT token to downstream services.

JWT's payload should contain all the authorization info like roles, so additional calls to auth service should not be required. 

Hope this helps.

Jakub

darko....@gmail.com

unread,
Nov 27, 2017, 1:20:47 PM11/27/17
to nameko-dev
Jakub,
thanks!

This is exactly what I was looking for.

It wasn't clear to me how this context part works.

Darko Poljak

unread,
Nov 27, 2017, 1:26:04 PM11/27/17
to nameko-dev
Jakub,
just one more question, for now.
Is there a way to get context_data/worker_ctx from service method like from get_something from your sample?

class MyService:
  name
= 'my-service'

  auth
= Auth()

 
@http('GET', '/')  
 
def get_something(self, request):
    jwt_payload
= self.auth.decode_jwt()




Darko

**All work and no play makes Jack a dull boy**


--
You received this message because you are subscribed to a topic in the Google Groups "nameko-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/nameko-dev/KQ0R7zYrpq8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to nameko-dev+unsubscribe@googlegroups.com.
To post to this group, send email to namek...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/nameko-dev/b1cd2268-576a-44d4-8d52-1e9a09d63595%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jakub Borys

unread,
Nov 27, 2017, 4:39:53 PM11/27/17
to nameko-dev
You should create Dependency to manage your context. You can find example here: https://github.com/nameko/nameko/blob/master/test/standalone/test_rpc_proxy.py#L29

Also be mindful of context data keys already used by Nameko: https://github.com/nameko/nameko/blob/master/nameko/contextdata.py

Cheers,
Jakub
To unsubscribe from this group and all its topics, send an email to nameko-dev+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages