I used to work with several startups recently. Authentication is present in any product you do. Authentication is not simple. Not hard in the development sense — well-defined standards such as OAuth2 are complete, extensively documented and supported by an ecosystem of tooling.
Rather, authentication is hard to get right. There are number of limitations like time constraints, product owner concentrated on other features, budget constraints and so on. If you have some previously developed user subsystem which evolves from project to project - that is cool. Otherwise you are about to start yet another authentication subsystem for your project. Story starts... Stored your passwords as an MD5 hash instead of, saying, using bcrypt and salt — tadamm. Didn’t force HTTP Strict Transport Security (HSTS) and get down-grade tadamm. Rookie mistakes, few sprints to polish.
But what if short term you need to comply with some of the security standarts, like ISO27001/ISO27018, SOC II, HIPAA, PCI DSS etc? Implementation time grows while you need to implement your MVP instead.
So auth0 is authentication as a service solution, which offloads your pain with authentication for a while, and allows to delay your own implementation, while immediately start using your application with robust authentication approach.
API gateway. The same situation in a modern world. You want to have lightweight api, so you want to offload number of checks to some middleware while keep solution as modular as possible. Most of the major cloud providers have their solution (like AWS API gateway), but if you want to stay provider independent you again have less choices - either implement api gateway on your own, or use some opensource solution. Here comes kong - makes connecting APIs and microservices across hybrid or multi-cloud environments easier and faster. Not, that Kong gateway is the open source API gateway, built for multi-cloud and hybrid, and optimized for microservices and distributed architectures. It is built on top of a lightweight proxy(nginx + lua) to deliver unparalleled latency, performance and scalability for all your microservice applications regardless of where they run. It also allows flexibility by introducing granular control over your traffic with Kong’s plugin architecture.
Often you have situation, when you have separate api gateway, optional separate client portal and delegated authentication to auth0... And here comes purpose of this article - illustrate proof of concept of combining Auth0, Kong and your portal in one architecture. Unfortunately, by this time in number of situations - swiss knife of proven plugins is needed to make kong to work nicely in a non-standard architecture solutions.
Generally, there are two possible flows: you are going to consume services interactively from some web portal - in that scenario customer logs on it's own and consumes api directy from api gateway, or your api are about to be consumed on your serverside and then proxied to client of the your portal.
Let's review both approaches.
This flow supposes, that API accessible through kong will be consumed by some browser application, while user management and authorization is delegated to some openid2 compatible third party service, in our case Auth0.
To accomplish this, one of the Nokia teams implemented plugin kong-oidc
available at https://github.com/nokia/kong-oidc.
kong-oidc is a plugin for Kong implementing the OpenID Connect Relying Party (RP) functionality.
This plugin should be preinstalled on a kong mproxy
https://gist.github.com/eea74e937808d32d49ad7a414f800511
On Auth0 interface lets create SPA application.
For further ideas, check advanced settings, and ensure that you can add up to 10 255 byte long custom value pairs, that will be later passed to your api behind kong.
In most of situations, you would like to pass some application specific info from your authentication provider to your api endpoint. To accomplish this, Auth0 has rules concept, on a UI you can find Rules section, where you can implement number of javascript routines with custom logic, that may add number of realtime parameters to the request passed.
https://gist.github.com/0be938b7087414ed4475145e818c22ab
In a example above note, that any additional parameters should follow
uri scheme, in other case they will not be passed downstream (someattr1
, someattr2
) in snippet above.
In that scenario you will have some web application running at some domain. Let's assume, it is http://app.lvh.voronenko.net/ - it has some custom portal logic, but also consumes data from microservices hosted behind kong, running at endpoint http://app.lvh.voronenko.net:8000/
Lets mock it:
Create the service
https://gist.github.com/ad9d641d5c52205a39a3ef946d1217bf
Create route, service will be exposed at {{ domain }}/spa/
https://gist.github.com/4fdb02bf5095f337e5b19b7d8e57b388
If everything ok, you should get at /spa/ endpoint copy of the enchanced request received.
https://gist.github.com/10084e8fd69067aceac594ba13c4941d
Now lets protect our endpoind with auth0 authentication. We will need to get three additional parameters: client id, client secret and discovery file. All can be obtained directly from auth0.
https://gist.github.com/a574c0027d9250b41a7869f2dd4ff6dc
If you open spa discovery url, you will find, generally, "contract" supported by authenticatio provider, auth0
https://gist.github.com/4123481bf097dbb2015c132b968f2064
Let's associate plugin with the service
https://gist.github.com/c75e6926982139f888c855e3605dd517
If you check documentations of the plugins, you will note that it has number of customizations, that allows to redirect to proper pages of your main portal after login and so on.
Additionally, we need to inform auth0 about callbacks used. If your application
does not have own, kong can provide default ones, which would reside under
/cb
Now, if you would try to navigate to {{ domain_endpoint }}/spa/
, you will get
redirected to auth0, where you can login with credentials given, including social logins.
Upon successful login we are redirected back to kong, and now
https://gist.github.com/3650bd95dfe7ccd5c6e5e28edadacc97
As we see, now user is authorized to use or API in kong, and oidc
plugin
also adds special header called X-Userinfo
, which contains base64 encoded jwt token. At the same moment, plugin performs validation of the token signature, and also can be configured to do more background checks.
You can decode base64 decode to view token data insides, pay attention to presence of the custom attributes.
https://gist.github.com/80a4f364ca7c015643180e3f4ef6b7ea
The same does your code serverside in a microservice or any next plugin in a chain and performs necessary actions basing on information received.
So with kong oidc plugin flow looks like
Some interesting discussion can be also found on nokia/kong-oidc#15
And the same information can be now extracted by next plugin in chain or end service.
Note, that there is some mess with rules and hooks, as discussed in that thread https://community.auth0.com/t/custom-claims-not-added-to-access-token-despite-rule/9460/19
https://auth0.com/docs/api-auth/tutorials/client-credentials/customize-with-hooks
This time we create last type - machine to machine application, and link it with api created in Web application flow
Let's create direct route service
https://gist.github.com/140ddd4e50fa5bc61b9b3b3bb868a9cf
Organize route for the service
https://gist.github.com/7c6ebfca124a8a5777f2c8e745305bbf
Now associate jwt plugin with service.
https://gist.github.com/34a6ccaeaafc05877b5bca82e4a1bef6
Now call to api will return 401 Unauthorized
https://gist.github.com/36653f7f4430059124527221839d76db
How can we consume api ? We need to configure jwt consumer.
First of all, we need to get private key for message signing,
https://gist.github.com/a3797a0c23fd8b3122207c3e90b5a5c2
Having that key, lets create kong consumer
https://gist.github.com/59ec64dd1cae212b8721daa47951e296
and specify jwt
method of the authentication for him:
https://gist.github.com/e3f453f149ea5ba21dab40be1b5b17ee
At that moment we are ready to consume our api behind kong.
First of all, user obtains access token from Auth0:
https://gist.github.com/e78afdbcf659c14b9b0cacff2b384671
with response kind of
https://gist.github.com/bcd0eb61e892d42ae67b39e227420669
This request now passes, our API gets consumed.
https://gist.github.com/122b1ebb7b564954b676125e66121bda
Additionally. we get bunch of headers:
https://gist.github.com/acc9396a641684171c3099ab7794bf75
Header after Bearer is again our jwt token, that can be decoded.
https://gist.github.com/e819f872ead689876ca6887521a909f3
and later consumed by our API.
For M2M applications in difference to web based applications, where custom attributes can be injected via rules, point of extension are hooks.
Related documentation: https://auth0.com/docs/hooks
Take a look on a example below representing dummy Client Credentials Exchange
hook extending access_token with additional keypair
'https://foo.com/claim'
:bar
and somehookattr
:somehookattrvalue
https://gist.github.com/236a0e72c614700ae60904604be2ecdc
upon issuing request for token
https://gist.github.com/9cf9363a73e05a08d7adbec3ce30fa1b
we receive following response
https://gist.github.com/dba4859534f3e954c7dce46697e97087
Using debugger at jwt.io lets decode payload
https://gist.github.com/c77e271d3fa425b6e6b32dfa56365c6b
Important: note, that extension attributes passed via custom namespaced
uri was allowed, while un-namespaced custom attribute somehookattr
was
stripped out from the payload.
Please check, that iss
returned in token payload,
should match to key field of the jwt token
In example above, "iss": "https://voronenko.auth0.com/"
matches
to key attribute of the jwt key.
If you start with kong with auth0, you will note, that in number of situations you would not be able to find flow of plugins that ideally suits your needs. Fortunately that is not a dead lock, as you always can write your own kong plugin - and this task is not so hard
First step you need is to follow good boilerplate. Current recommended boilerplate for kong plugin can be found at https://github.com/Kong/kong-plugin where you can look for ideas
Per my experince - success key with kong plugin development - is strict test driven development, and addressing all compiler warnings. Take a look on a https://github.com/Voronenko/kong-plugin-sa-jwt-claims-validate plugin as an example - which was implemented to accomplish specific jwt token data validations and thus offload microservices behind.
Usual plugin is compact: ig could consists just of two files - schema.lua , which describes "configuration" contract for the plugin. You can keep validations simple while you are the only consumer of the plugin, but per my experience you would like to invest more efforts into configuration validation once you start targeting wider audience. Reasoning is that it is harder troubleshouting of misbehaved plugin in a production environment, if plugin fails on a configuration (consumer provides wrong configuration options, but that is not handled by schema validations), it could be also not clearly seen in kong logs.
Second file is handler.lua - you would need to implement few hooks according to your plugin logic. Important parts to pay attention to:
plugin priority - as it controls order of your plugin execution in chain of other plugins during the request processing
https://gist.github.com/f4d4bec7ad01002e8049bb565af2be40
and access callback, which is the one that will be implemented in every custom plugin.
https://gist.github.com/2dc58b8f6cbf6e83cf3d86190ea44af4
During callbacks implementation, pay attention to scope of the variables in lua - accidental exposing variable globally might lead to unexpected issues during concurrent calls. You might also want to introduce detailed logging activated using plugin configuration.
By that moment it is the end for the introduction with Auth0 , kong and your custom plugin. Good luck in your journey. Dockerized environment can be found at https://github.com/Voronenko/kong-compose-template/