How to Use Scopes to Secure Your API
Express Gateway has built-in support for numerous authentication mechanisms, like OAuth2 and key auth. On top of these authentication mechanisms, Express Gateway supports restricting access to certain endpoints to certain users using the notion of scopes. In this article, I’ll provide a “Hello, World” example of using scopes and then dive into a more realistic example of using scopes to protect access to an external API.
Hello, Scopes
To get started, let’s create a gateway with two endpoints, one of which is accessible to all logged in users and one of which is only accessible to users we’ve designated as admins. First, make sure you’ve installed Express Gateway and created a new gateway called ‘scopes’:
~$ npm i -g express-gateway
~$ eg gateway create
? What's the name of your Express Gateway? scopes
? Where would you like to install your Express Gateway? scopes
? 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 scopes && npm start
Next, do cd scopes
and change the config/gateway.config.yml
file to the below. This config file defines 2
API endpoints that both proxy out to httpbin.org: one called ‘ip’ that gets your IP address,
and one called ‘get’ that gets your IP address and whatever headers your browser sent. These endpoints are not
very useful or sophisticated, but they’re sufficient for this rudimentary exercise. The ‘get’ endpoint has an
associated list of scopes that contains one element, ‘admin’. This means that only users that are admins based on
key-auth will be able to access this endpoint.
http:
port: 8080
admin:
port: 9876
hostname: localhost
apiEndpoints:
# Two endpoints, first one that gets the current IP...
ip:
host: localhost
paths: '/ip'
# And a second endpoint that gets your IP, headers, etc.
get:
host: localhost
paths: '/get'
# Only users with the 'admin' scope will be able to access
# this endpoint
scopes:
- 'admin'
serviceEndpoints:
httpbin:
url: 'https://httpbin.org'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
pipelines:
default:
# This pipeline operates on both API endpoints
apiEndpoints:
- ip
- get
policies:
# First, this pipeline enforces that the user must be logged in
# This will also check scopes
- key-auth:
# And if the user got past the key-auth policy, we'll proxy them to the correct endpoint
- proxy:
- action:
serviceEndpoint: httpbin
changeOrigin: true
Now that you’ve configured the gateway, make sure you run npm start
, and then run eg users create
to create a new user.
$ eg users create
? Enter username [required]: val
? Enter firstname [required]: val
? Enter lastname [required]: karpov
? Enter email: [email protected]
? Enter redirectUri:
✔ Created 9b06d8c8-ba46-49c7-a3be-2ef65298de5b
{
"firstname": "val",
"lastname": "karpov",
"email": "[email protected]",
"isActive": true,
"username": "val",
"id": "9b06d8c8-ba46-49c7-a3be-2ef65298de5b",
"createdAt": "Wed Sep 20 2017 17:58:31 GMT-0700 (PDT)",
"updatedAt": "Wed Sep 20 2017 17:58:31 GMT-0700 (PDT)"
}
Next, let’s create a scope. You can think of a scope as Express Gateway’s equivalent to a user role in
frameworks like LoopBack. A scope is what grants a
user access to a protected resource. Creating a new scope called ‘admin’ is easy, just run eg scopes create admin
.
$ eg scopes create admin
✔ Created admin
The key difference between scopes and the user roles you might be used to in other frameworks is that scopes are associated with credentials, not users. In Express Gateway, a user has one or more credentials that enable them to authenticate against the gateway using different mechanisms, like OAuth2 or key auth. A credential then has one or more scopes. This means that a user may have access to different endpoints based on which key they use or whether they logged in with OAuth2 or not.
Run eg credentials create
to create a credential for the user ‘val’ with the scope ‘admin’:
$ eg credentials create -c val -t key-auth -p "scopes=admin"
✔ Created 0J6jIYDCbmLF4UBpKAk549
{
"isActive": true,
"createdAt": "Wed Sep 20 2017 21:35:08 GMT-0700 (PDT)",
"updatedAt": "Wed Sep 20 2017 21:35:08 GMT-0700 (PDT)",
"keyId": "0J6jIYDCbmLF4UBpKAk549",
"keySecret": "0LDD2FJ1WHA57vqcF25vrs",
"scopes": [
"admin"
],
"consumerId": "val"
}
$
Great, now you have a user ‘val’ that should have access to both the ‘ip’ and ‘get’ endpoints. Let’s create another user that does not have the ‘admin’ scope, and therefore only has access to the ‘ip’ endpoint.
$ eg users create
? Enter username [required]: foo
? Enter firstname [required]: foo
? Enter lastname [required]: bar
? Enter email: [email protected]
? Enter redirectUri:
✔ Created b72ee69b-178b-411d-aa8a-10f066929580
{
"firstname": "foo",
"lastname": "bar",
"email": "[email protected]",
"isActive": true,
"username": "foo",
"id": "b72ee69b-178b-411d-aa8a-10f066929580",
"createdAt": "Wed Sep 20 2017 18:05:53 GMT-0700 (PDT)",
"updatedAt": "Wed Sep 20 2017 18:05:53 GMT-0700 (PDT)"
}
$ eg credentials create -c foo -t key-auth
✔ Created 2gnzY5H8s8vrGJIASH0NOD
{
"isActive": true,
"createdAt": "Wed Sep 20 2017 18:05:59 GMT-0700 (PDT)",
"updatedAt": "Wed Sep 20 2017 18:05:59 GMT-0700 (PDT)",
"keyId": "2gnzY5H8s8vrGJIASH0NOD",
"keySecret": "1fb7Pn0YlfPppAu1rTLHoA",
"scopes": null,
"consumerId": "foo"
}
$
Now, if you send an HTTP request to ‘/get’ using the ‘foo’ user’s keys using the
Postman chrome extension, you’ll get an error message
that says you’re unauthorized. Remember that, by default, key auth in Express Gateway checks the HTTP authorization
header and expects credentials to be in the format apiKey ${keyId}:${keySecret}
where keyId
and keySecret
are
as shown in the output of eg credentials create
above.
You can access ‘/ip’ endpoint with the ‘foo’ user’s key though:
If you use the ‘val’ user’s key, you can access the ‘/get’ endpoint:
Controlling KeenIO API Access
KeenIO’s REST API is a good example of what you can do with Express Gateway. Their API has a powerful role-based access control mechanism, but suppose you’re responsible for maintaining roles and permissions for hundreds of developers on dozens of APIs. Maintaining integrations with each API’s access control paradigm would drive you nuts. Enter Express Gateway. Let’s say you want any logged in user to run ad-hoc queries on KeenIO, but only admins can modify saved queries.
The config for restricting access to the saved queries portion of the KeenIO API is below. To use it, you would have
to replace ‘MY_PROJECT_ID’ with your KeenIO project ID and ‘MY_READ_KEY’ with your KeenIO read key. The key insight
is that order matters in the apiEndpoints
property, so if /queries/saved
comes before /queries/*
every endpoint under
/queries/saved
will require ‘admin’ scope, but /queries/count
will not.
http:
port: 8080
admin:
port: 9876
hostname: localhost
apiEndpoints:
saved:
host: localhost
# This endpoint is the base endpoint for interacting with saved queries
paths: '/queries/saved'
# Require the 'admin' scope
scopes:
- 'admin'
adhoc:
host: localhost
# The `paths` key can be a list of endpoints, and can include '*' as a
# wildcard. Note that **order matters** with `apiEndpoints`, if `saved`
# was listed after `adhoc` this config wouldn't block non-admins from
# accessing the `saved` endpoint.
paths:
- '/queries/*'
serviceEndpoints:
keen:
# Replace with your KeenIO Project ID.
url: 'https://api.keen.io/3.0/projects/MY_PROJECT_ID'
policies:
- basic-auth
- cors
- expression
- key-auth
- log
- oauth2
- proxy
- rate-limit
pipelines:
default:
apiEndpoints:
- saved
- adhoc
policies:
- key-auth:
# Custom expression, replace 'MY_READ_KEY' with your KeenIO read key.
# More about expressions:
# https://www.lunchbadger.com/expressions-expressjs-in-express-gateway/
- expression:
- action:
jscode: >
req.headers.authorization = 'MY_READ_KEY';
- proxy:
- action:
serviceEndpoint: keen
changeOrigin: true
With this config, a user without admin scope can query to count the number of events that have been tracked to the ‘opened post’ collection in the last 7 days:
But they get an ‘Unauthorized’ error when they try to list the saved queries.
Moving On
Express Gateway’s authentication methods and proxying ability are indispensible for centralized access to APIs. You can generate one API key for every employee and use Express Gateway to proxy their requests with the correct credentials. No need to worry about rotating keys when an employee leaves or scratch your head wondering who’s blowing through the free tier on your development sandbox account. Scopes give you even more fine grained control, letting you do role-based access control through a centralized tool rather than dealing with API providers directly. Stop worrying about provider-specific access control and get started with Express Gateway!
More Resources
- Join the community on our Gitter channel
- Learn more about upcoming features and releases by checking out the Express Gateway Roadmap