Securing a FHIR endpoint in Node-RED

So in a comment to the previous post on using Node-RED to create Tasks from Assessments, Michael suggested that I talk about some of the  security considerations that you might have. There are a few ways you could make this more secure that I can think of.

  • The simplest way would be to put the whole thing behind as API manager (such as APIgee or WSO2) and delegate security to that application. In effect you are trusting the API manager.
  • Another way is to follow the SMART / OAuth process – check that a valid access token issued by an Authorization Server (Authz) has been provided in the call, rejecting the call if not.
  • And yet another option is to perform the whole OAuth2 dance – look for a valid access token, taking on the role of the Authorization server if not – validating the user and issuing tokens.

(And there will be plenty of others I’m sure). Naturally all calls will use SSL.

Let’s look a bit further into the second option – checking for a valid access token, and simply rejecting the call if there is not one present.

This is going to involve trusting a separate Authorization server to identify a user and issue tokens.

The exact method by which the interaction between our app (called a resource server in OAuth parlance) and the Authorization server occurs is not defined in the spec. A common method is that the access token is actually a digitally signed JWT token – signed with the Authorization servers private key so that we know it is legitimate. We decrypt the token with the Authorization servers public key, and extract the data we need from the token – things like whether the token hasn’t expired, what access rights were granted by the Authorization server, the users actual identity and so forth.

Another option is to call the Authorization server each time we receive a call and get it to validate the token for us – sometimes called a ‘back channel’ call. Obviously, the call to the Authorization server must be secure, but it does have the advantage that we don’t need to manage keys and the whole decryption process. There’s an extra performance overhead of course – but that’s acceptable to us in this case. So let’s implement that.

And here’s the flow that implements it (only the security bits).

Screen Shot 2019-03-15 at 12.05.13 PM.png

It’s actually quite simple.

When the call comes in we look for an authorization header containing the access token. If we don’t find one, we reject the call with a status code of 403 (forbidden) – setting a polite message. This is the point where we could re-direct to a login page if we were handling more of the overall auth process.

Assuming we find a token, we call the Auth server, passing it the token. The Authorization server will validate the token and return us information (maybe a JWT token) if valid, or a 403 status if not. (Remember that this is only one way that this interaction could occur). Again we reject the call if the token is not valid

Now that we have the JWT token from the Authorization server we’re good to go. If we need to we can extract other data from the token – we might want to check the scope of access for example, to determine if this user is allowed to perform the functionality  we are providing. Or (if it’s in the token) we pull out the user identity (this is the OpenID connect stuff).

Here’s a very simple Authorization server (just so we can get the flow working):

auth server

The ‘check token’ function just looks if the token has the word ‘Bearer’ in it – setting the statusCode and payload for the HTTP response. In real life you’d query some store of tokens that the Authorization server has issued, check that they exist and haven’t expired, manage refresh tokens and suchlike.

So that’s how we can make our app more secure. There’s a lot more information on SMART – including a number of posts on this blog. There are quite a number of ways we could improve this (in truth, it is a bit klunky). For example we could create a sub-flow that gets called for each HTTP input. Or – even better – we could implement this security as middleware within Node-RED itself (It’s based on node.js and uses Express, so it could be set up to be automatically called for every HTTP input – which would be much safer than relying on the flow developer to call the appropriate functionality). This will also allow us to use libraries like passport rather than rolling our own – generally a better idea with security related matters…

 

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an HL7 Fellow, Chair Emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

2 Responses to Securing a FHIR endpoint in Node-RED

  1. Thanks for this insightful post.
    I want to add a fourth altenrnative approach which is based on leaving the auth business out of the FHIR server entirely and relying on a reverse http proxy to handle the authorization logic. This has the benefit of isolating the authorization business from the FHIR service itself, so that changes can be made to the authorization logic without affecting the FHIR server. Also it enables trusted backend services to have unrestricted access to the FHIR service behind the proxy.
    I have a preliminary prototype implementation for this approach as part of the Pauldron project:
    https://github.com/mojitoholic/pauldron/tree/master/pauldron-hearth

    • David Hay says:

      Yeah – that would be similar to the idea of an API manager – though rather simpler… It’s a nice idea – thanks!

Leave a Reply

Discover more from Hay on FHIR

Subscribe now to keep reading and get access to the full archive.

Continue reading