Implementing Key Authentication in Express Gateway

You’ve probably used it before: key authentication. The basic idea is simple, to authenticate your app or client with a given service you send a key to identify (and authorize) yourself. This is not intended for individual users necessarily, but rather for systems talking to each other.

Implementing Key Authentication in Express Gateway

In this article I’ll be showing you how to get up and running with key authentication quickly and easily with Express Gateway (EG). We’ll talk about setting up and configuring your gateway, creating credentials, and sending authenticated requests. This is a brief introduction, so be sure to read the documentation and test things before you deploy your API gateway!

Creating and Configuring the Gateway

Your first step might be to generate a new Express Gateway instance. (Feel free to skip this step if you’ve already done this.) You’ll want to first install the express-gateway package and then generate a new gateway:

~$ npm i -g express-gateway
~$ eg gateway create
? What's the name of your Express Gateway? widget-factory
? Where would you like to install your Express Gateway? widget-factory
? What type of Express Gateway do you want to create? Getting Started with Express Gateway
    created package.json
    created server.js

    ...

To start widget-factory, run the following commands:
    cd widget-factory && npm start

Great! Before we start up our gateway for the first time, let’s go ahead and configure it.

Gateway Configuration

Express Gateway has two primary configuration files (plus model configuration) in the /config directory of your new project: the gateway.config.yml file and the system.config.yml file. The system config file is where you will set up things like database access (for the gateway, not your individual microservices) and certain security settings for things like OAuth2. We won’t be working in that file today (which also means our users and credentials will not be saved for this example).

The gateway configuration file is where you configure HTTP, endpoints, policies (like key authentication), and pipelines (which are just a series of policies applied to some endpoints). The default generated config file creates one API endpoint for the gateway at /ip and proxies those requests to https://httpbin.org/ip - you’ll want to change that later, but we’ll leave it for now. What we need to do is add a policy to the “api-basic” pipeline (the only one in there).

Find your “pipelines” block in the gateway.config.yml file and make it look like this:

pipelines:
  - name: api-basic
    apiEndpoints:
      - api
    policies:
      - key-auth:  ### This line is new!!
      - proxy:
        - action:
            serviceEndpoint: httpbin
            changeOrigin: true

Notice that we added our “key-auth” policy before the “proxy” policy. The policies in each pipeline are ordered, so be sure to put them in the order you want them to execute. In our case, we should not proxy the API request if the authentication fails.

Believe it or not, that’s the only change you need to make to our demo gateway to enable key authentication! You can test this out by starting up the gateway and making a simple GET request to /ip (or any other endpoint really):

~/widget-factory$ npm start

You can test the HTTP request using cURL, or a tool like Postman (which I highly recommend for API development). Here is the response you might get using cURL:

~$ curl -D - "http://localhost:8080/ip"
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 9
ETag: W/"9-PatfYBLj4Um1qTm5zrukoLhNyPU"
Date: Sun, 16 Jul 2017 19:37:52 GMT
Connection: keep-alive

Forbidden

Notice that our response code was 401 because we did not send an API key. We’ll talk more about these status codes later, but for now let’s make an API key.

Generating Key Credentials

Our first step to create an API key is to create a “user” in the system. Open up another terminal window and navigate to your gateway project directory. Now we can use the same eg command we used to generate the gateway to create credentials:

~/widget-factory$ eg users create
? Enter username [required]: jordan
? Enter firstname [required]: Jordan
? Enter lastname [required]: Kasper
? Enter email: [email protected]
? Enter redirectUri:
✔ Created jordan

I’ve left the redirectUri blank here because we are not using it in key authentication, but you might need for other schemes. Now that we have a user, can either create an “app” for that user and then “credentials”, or we can just create the “credentials” for the user themselves. I’ll do the second option for now:

~/widget-factory$ eg credentials create -c jordan -t key-auth
✔ Created
 {
  "isActive": true,
  "createdAt": "Sun Jul 16 2017 15:48:48 GMT-0400 (EDT)",
  "updatedAt": "Sun Jul 16 2017 15:48:48 GMT-0400 (EDT)",
  "keyId": "0J6961Lhn8JgxYybTXFdRg",
  "keySecret": "23BfI6QVqgdxP3ty8F4Jx3",
  "scopes": null,
  "consumerId": "jordan"
}

That’s it! Notice that the output above shows us out "keyId" and "keySecret". These two pieces together create our final API key for the system.

Sending Authenticated Requests

Now that we have some credentials we can send some more requests to our API (through our gateway).

~$ curl -H "Authorization: apiKey 0J6961Lhn8JgxYybTXFdRg:23BfI6QVqgdxP3ty8F4Jx3" -I "http://localhost:8080/ip"
HTTP/1.1 200 OK
x-powered-by: Flask
connection: close
server: meinheld/0.6.1
date: Sun, 16 Jul 2017 19:54:49 GMT
content-type: text/html; charset=utf-8
content-length: 12793
access-control-allow-origin: *
access-control-allow-credentials: true
x-processed-time: 0.00561594963074
via: 1.1 vegur

There are two points of note above: first, we got a 200 response! That’s great, that means our auth check passed! So how did we do that? We sent the Authorization header with our key in it. Remember, our key is made up of two parts. So you can see above that the Authorization header value is actually two pieces of information separated by a colon (“:”). Additionally, we prefix that value with our header scheme: “apiKey”.

Authorization: apiKey 0J6961Lhn8JgxYybTXFdRg:23BfI6QVqgdxP3ty8F4Jx3

 header name : scheme        keyId          :      keySecret
Status Codes

As you see above, we got a 200 response status code, meaning success! That status code actually came from our API service (in this case httpbin.org) not from our gateway. If you request a resource that doesn’t exist on that service you should receive a 404, for example. The gateway will send back a 401 when the key is not authenticated - or missing entirely. But it could also send back a 403 if the user is authenticated, but not authorized for the given resource. This could happen if you are using “scopes”.

What’s a Scope?

We won’t get deep into scopes in this blog post, but the scopes are the main entities for specifying authorizations within Express Gateway. A scope is simply a pre-defined string added to your gateway configuration (both on an API endpoint and then again on a policy in a pipeline for that endpoint). API endpoints are secured by specifying scopes. To be authorized for an API endpoint that is secured by a scope, a consumer must have a credential containing the scope listed on the API endpoint. In other words, the scopes on the endpoint have to match the scopes on the user’s (or app’s) key credentials.

Additional Configuration

There are more options you can add to your gateway “key-auth” policy to secure it further or simply customize it. For example, by default the gateway will accept keys in both the headers and query string as well. You can easily disable this with the disableQueryParam option:

pipelines:
  - name: api-basic
    apiEndpoints:
      - api
    policies:
      - key-auth:
        - disableQueryParam: true  ### Disable API keys in the query string
      - proxy:
        - action:
            serviceEndpoint: httpbin
            changeOrigin: true

You can also change the header used for authentication (although this would break with current standards) or the scheme used:

pipelines:
  - name: api-basic
    apiEndpoints:
      - api
    policies:
      - key-auth:
        - apiKeyHeader: "X-My-Auth-Header"
        - apiKeyHeaderScheme: "key-pair"
      - proxy:
        - action:
            serviceEndpoint: httpbin
            changeOrigin: true

If you used the configuration above you would need to modify the header you send in all authenticated API requests like so:

~$ curl -H "X-My-Auth-Header: key-pair 0J6961Lhn8JgxYybTXFdRg:23BfI6QVqgdxP3ty8F4Jx3" -I "http://localhost:8080/ip"

Managing Key Credentials

Our last topic for this post has to do with managing those keys you’ve generated. You will probably find a time when you need to deactivate a user’s access. This is easily accomplished on the command line by deactivating their credentials:

~$ eg credentials deactivate -t key-auth 0J6961Lhn8JgxYybTXFdRg
✔ Deactivated 0J6961Lhn8JgxYybTXFdRg

After performing this action, the given keyId will no longer be authenticated in the gateway. This is not a permanent action, and the credentials can easily be reactivated with the companion activate sub command:

~$ eg credentials activate -t key-auth 0J6961Lhn8JgxYybTXFdRg
✔ Activated 0J6961Lhn8JgxYybTXFdRg

Wrapping Up

One of the biggest things an API gateway can do for you is centralize the authentication for your various microservices. And Express Gateway makes this process extremely straight forward. Take a look at the documentation and give it a try!

Originally posted on www.lunchbadger.com.