nameko token authentication

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

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

···

On Monday, 27 November 2017 14:23:10 UTC, darko....@gmail.com wrote:

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,
thanks!

This is exactly what I was looking for.

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

···

On Monday, November 27, 2017 at 6:08:33 PM UTC+1, jakub...@student.com wrote:

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

On Monday, 27 November 2017 14:23:10 UTC, darko....@gmail.com wrote:

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,
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***

···

On Mon, Nov 27, 2017 at 7:20 PM, <darko.poljak@gmail.com> wrote:

Jakub,
thanks!

This is exactly what I was looking for.

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

On Monday, November 27, 2017 at 6:08:33 PM UTC+1, jakub...@student.com > wrote:

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

On Monday, 27 November 2017 14:23:10 UTC, darko....@gmail.com wrote:

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

--
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 nameko-dev@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
<https://groups.google.com/d/msgid/nameko-dev/b1cd2268-576a-44d4-8d52-1e9a09d63595%40googlegroups.com?utm_medium=email&utm_source=footer&gt;
.
For more options, visit https://groups.google.com/d/optout\.

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

···

On Monday, November 27, 2017 at 6:26:04 PM UTC, Darko Poljak wrote:

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***

On Mon, Nov 27, 2017 at 7:20 PM, <darko....@gmail.com <javascript:>> > wrote:

Jakub,
thanks!

This is exactly what I was looking for.

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

On Monday, November 27, 2017 at 6:08:33 PM UTC+1, jakub...@student.com >> wrote:

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

On Monday, 27 November 2017 14:23:10 UTC, darko....@gmail.com wrote:

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

--
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+...@googlegroups.com <javascript:>.
To post to this group, send email to namek...@googlegroups.com
<javascript:>.
To view this discussion on the web, visit
https://groups.google.com/d/msgid/nameko-dev/b1cd2268-576a-44d4-8d52-1e9a09d63595%40googlegroups.com
<https://groups.google.com/d/msgid/nameko-dev/b1cd2268-576a-44d4-8d52-1e9a09d63595%40googlegroups.com?utm_medium=email&utm_source=footer&gt;
.
For more options, visit https://groups.google.com/d/optout\.

Sorry for necrobumping this, but I have troubles following…

Shouldn’t the get_dependency() of the Auth class return something? I have troubles understanding what!

Yes it should. The example above is incomplete somehow. Maybe it didn’t make the transition from the googlegroup to Discourse properly.

As with any DependencyProvider, get_dependency should return whatever the service method uses to interact with that dependency.

There is a very toy example Auth DependencyProvider here: https://github.com/nameko/nameko/blob/master/docs/examples/auth.py

The rest of Jakub’s example is complete. The gist is that an entrypoint needs to extract the incoming token/credentials however it is provided (e.g. the “Authorization” HTTP header) and put it into the Nameko worker context data. Downstream services can then read the token back out of the context data and perform auth checks (e.g. validate a JWT, check it has appropriate roles etc)

1 Like