Hey all,
So I have experience writing the actual logic of microservices but I'm pretty new to the art of rigging them together and the whole API Management/Gateway/Proxy/Specification ecosystem(s) have been doing my head in for the past week or so.
I'm trying to build an API to face organizational clients. I'd like to eventually offer this in terms of an organization holding a set of individually revokable API keys of different roles, but to get something out I'm just aiming to have one API key per org.
I have a definition, in both OpenApi 2 and 3, it's a REST-y kind of api - GET /resourceAs/{uuid}/subResourceB/{uuid} and I'd like to implement it with firebase/google functions cos that's my wheelhouse, but I'm happy to try other backend platforms if they work better. The stumbling block I feel like I have hit is in configuring org-level permissions - that is I need to somehow communicate in a secure manner to the backend service that APIGee has authenticated the request as containing an API key owned by organization X (where my backend service can then handle the implication that Organization X owns the resource at /resourceAs/{uuid}/subResourceB/{uuid}).
I feel like the answer is some combination of JWT and or opaque tokens? I don't want the user's to need to have completed authentication with me other than their possession of an API key transmitted in the headers. I had a look at the Google cloud functions extension but it seems like seeing as it only trigger's HTTPS functions I can't just trust (from the backend's perspective) content added to the headers/payload sent in the Policy there (such as the custom attribute from a manually registered developer's App) - or am I wrong in thinking that?
Essentially I am looking for a way to assert some claims associated with a particular App's API key, in a secure way to my backend service. Can anyone help a hapless developer thrown into the devops arena?
Solved! Go to Solution.
There are numerous ways to communicate that information from an API Gateway like Apigee Edge into a backend system like Firebase or Google Cloud Functions. (or AppEngine, or etc)
if the inbound call is carrying an access token, then the API Proxy should call VerifyAccessToken. After successful verification, there will be context variables populated including
(see the doc for full details)
That perhaps isn't quite the information that you seek when you say "Organization X". But take heart, there is more power here.
The easiest way to attach the information about "organiztion X" to a request that the proxy sends to the backend is sometimes known as "header injection". The API Gateway can simply add a new header to the request that is sent to the backend, and this header can contain whatever you like. Do this with AssignMEssage and a Set element.
<AssignMessage name="AM-InjectHeader'> <Set> <Headers> <Header name='my-new-header'>{variable_name_from_the_token}</Header> </Headers> </Set> </AssignMessage>
If you want additional information in the header, the "text value" in the XML config is a Message Template. This means you can refer to multiple distinct variables within curly braces and the value of the header is then derived from all those referenced variables.
If the API proxy receives a request like this:
POST /v1/foo/bar Content-Type: application/json Accept: */* Authorization: Beadrer ABC12345
The backend will see something like this:
POST /v1/foo/bar Content-Type: application/json Accept: */* Authorization: Bearer ABC12345 My-New-Header: value-of-referenced-variable-here
You can also (probably should) remove the token before invoking the backend. You can o this with assignMEssage also:
<AssignMessage name="AM-RemoveAuthzHeader'> <Remove> <Headers> <Header name='authorization'/> </Headers> </Remove> </AssignMessage>
And optionally you can combine those AssignMessage policies into one:
<AssignMessage name="AM-AdjustHeaders'> <Remove> <Headers> <Header name='authorization'/> </Headers> </Remove> <Set> <Headers> <Header name='my-new-header'>{variable_name_from_the_token}</Header> </Headers> </Set> </AssignMessage>
You mentioned a JWT. The value of "my-new-header' can just as easily be a JWT, generated within the Apigee Edge proxy via the GenerateJWT policy, asserting various claims. You would do this if you wanted an extra level of security in the backend; it can verify the signature on the JWT before trusting the claims within it.
In my experience, this is more than is necessary, unless you have a somewhat complex dependency graph in your services mesh, in which service A calls B, which calls C, which calls D and E and F, and some of those call G and H and so on. And each service may wish to assert some claims. In that case a receiving service might want to understand which of the previous services has asserted claims, and you could use a signed JWT to verify the integrity and issuer of claims, at any point in the graph, in a zero-trust environment.
But often that is more than you need. Often a simple text header saying "Organization X" is suitable and satisfactory.
User | Count |
---|---|
1 | |
1 | |
1 | |
1 | |
1 |