Skip to content

Instantly share code, notes, and snippets.

@voronenko-p
Created March 29, 2020 11:21
Show Gist options
  • Save voronenko-p/909bbd68fb80f055a65e74513c3c43b7 to your computer and use it in GitHub Desktop.
Save voronenko-p/909bbd68fb80f055a65e74513c3c43b7 to your computer and use it in GitHub Desktop.

Introduction

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.

Integrating with Auth0

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.

Web application (interactive customer login to api gateway via browser UI)

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.

alt

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.

alt

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

alt

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.

alt

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

image from kong oidc docs

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

M2M application (non-interactive scripts)

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

alt

and specify jwt method of the authentication for him:

https://gist.github.com/e3f453f149ea5ba21dab40be1b5b17ee

alt

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.

Extending Autho metadata

Hooks

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.

Known issues

No credentials found for given 'iss'

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.

alt

What's next?

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

Own plugin first steps

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

Own plugin implementation

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment