carlpett / nginx-subrequest-auth-jwt

Auth requests through NGINX with JWT tokens
MIT License
43 stars 17 forks source link

Pass back claims as headers #6

Open carlpett opened 5 years ago

carlpett commented 5 years ago

As suggested on Slack:

Is there some mechanism for passing information (eg user email extracted from JWT) back to nginx, and for nginx to include that information in requests to backend (eg header)? Like https://github.com/kubernetes/ingress-nginx/blob/29c5d770688b04d0a8beedf70aebd76990332d56/docs/examples/customization/external-auth-headers/deploy/echo-service.yaml#L52

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: public-demo-echo-service
annotations:
nginx.ingress.kubernetes.io/auth-url: http://demo-auth-service.default.svc.cluster.local?code=200
nginx.ingress.kubernetes.io/auth-response-headers: UserID, UserRole
ellieayla commented 5 years ago

Assume some backend service can handle read and write of some data for many users, as is normal in multi tenant systems. For this example, we'll assume a User Profile microservice, where a client can read/write user profiles. The following is simple AuthZ logic:

Let's assume the backend User Profile microservice already exists, and is already enforcing the above logic. It is doing so by parsing the JWT from the Authorization header's bearer token, and extracting the user's identity from the list of claims, the comparing that identity against the current resource.

The responsibility for extracting the user identity from the JWT is being moved to nginx-subrequest-auth-jwt. The responsibility for comparing identity against resource AuthZ could also be moved, or could remain as-is. This issue assumes the latter is desirable.

The backend microservice could explicitly trust provided headers, and its AuthZ logic be reduced to:

@route('/user/{id}')
def handle_write(request):
    if request.headers.get('UserID') == request.id or request.headers.get('UserRole') == 'mega_admin'):
         # do write
    else:
         raise NoPermissionError()

By trusting someone else to handle AuthN enforcement, the User Profile microservice cannot be securely run without that someone else. This grants advantages and disadvantages:

Such downsides could be mitigated by teaching the User Profile microservice some method to trust the ingress controller (eg, HMAC-signing the headers) but down that path lies a reimplementation of JWT, at which point there's no point in using nginx-subrequest-auth-jwt.

carlpett commented 5 years ago

Thanks! Would you see which headers to be returned as something you'd configure statically for the nginx-subrequest-auth-jwt instance, or something that could be passed into the validation request? Or could it be either? Compare with how there is a static and queryString mode for the claims to validate.

ellieayla commented 5 years ago

I can see that desire going either way. The set of claims received by multiple Ingress Rules is probably constant for one app, but could be varied across multiple apps. The application I'm thinking of would probably only need a max of 5? claims shared by all microservices. If the auth-backend sanely handled missing/optional claims (leave out those headers?) then the set could happily be constant.

While I probably wouldn't use queryStringesque mapping rules, I can see someone else wanting it later.