Search
  • Mitchell Bosecke

Dynamic Row-Level Authorization on Web Maps using JWT

Updated: Jul 28

The current state of authorization, when it comes to web-based mapping, is simply not granular or dynamic enough if you're building a sufficiently complex application. Let's explore an innovative authorization scheme using JWT tokens that significantly increases flexibility in how we can apply authorization to our web maps.


Problems with existing solutions


There are two well-known solutions that you will find on various open-source and cloud platforms (GeoServer, ArcGIS Online, MapBox):

  1. An API key is a secret that you expose to the end-user, which is included with every request to your mapping server. The mapping server may have mechanisms that allow you to configure which resources the API Key can access. It's simple to understand and easy to implement.

  2. A User/Role based system that you configure on your mapping server to match your application's authorization scheme. You'll often have to integrate your mapping server with your application's authentication system as well.

The first problem with these solutions is that they are often not granular-enough; perhaps they limit the end-user to a particular layer whereas you require this end-user to be limited to a particular bounding box or to a subset of the features in that layer. The Geofence extension to GeoServer has done a good job at tackling this problem, so it is solvable.


The more pressing problem with these solutions is that they aren't dynamic enough. Whatever authorization scheme your application uses, you will have to mimic this behaviour on your mapping server, and if your application uses any sort of ACL-based authorization, or anything that can't be reduced into basic roles, you're going to have a bad time.


For example, if an end-user is dynamically granted permission to a new dataset within your application, and you want to allow him/her to view a new layer that they couldn't view in the past, you either have to (a) generate or alter an API key or (b) generate or alter the user/role system. It's vital yet incredibly challenging to keep your mapping server's authorization in sync with your application's authorization; you're effectively doing the work twice.


Using JWT Tokens with CQL


The solution we are proposing, is that your application defines the authorization inside of a JWT token, and the mapping server simply obeys the rules within the token. We effectively move the responsibility from the mapping server back to your application which is already doing authorization.


Here's an example JWT payload, that defines a whitelist of layers the user can access, and an optional CQL filter that restricts the data within the layers:


{
    layers: "foo,bar,baz"
    cql_filter: "company=ABC"
}

Like all JWT tokens, it is cryptographically signed and you can apply a time-based expiration. The idea is that you will pass this signed JWT token as an HTTP header with every request to your mapping server, and your mapping server will verify the token with the request.


Unlike an API key, you aren't exposing secrets to the end-user that you may need to revoke one day; you are providing a time-based authorization token that is specific to that user's needs. Only your application and the mapping server will know the secret key used to sign the request.


If the end-user passes their own CQL filter as a query parameter, it will be combined with the CQL filter defined in the JWT token, as to make it impossible for them to escape the JWT constraints. If the end user modifies or removes the JWT token entirely, the mapping server should reject the request.



Advanced Usages


Using naming conventions and pattern matching to determine which layers the user can access:

{
    layers: "company_abc_(.*)"
}

Applying a unique CQL filter for each whitelisted layer:

{
    layers: "states,population"
    cql_filter: "name IN ('New York', 'California');age BETWEEN 10 AND 30"
}

Applying a complex CQL filter (ex. geographic bounding box):

{
    layers: "foo",
    cql_filter: "BBOX(the_geom,-90,40,-60,45)"
}

Specifying WRITE permission in the case of a WFS-T request:

{
    layers: "foo",
    permission: "WRITE"
}

Use Cases


Using JWTs is best reserved for complex authorization requirements that rapidly change. As previously alluded, a common example would be a multi-tenant application where authorization to layers (or features within layers) can be changed at runtime.


If your application has relatively static authorization requirements, it's probably best to stick with the simpler solutions.


Downsides


One downside to this solution is that it's more difficult to implement than simple API keys. You will need to use a third-party JWT library to generate your tokens server-side, and you will need to alter your frontend to include the tokens in the requests to the mapping server. However, this should still be no more than a handful of lines of code.


Another downside to this solution is that CQL filters often prevent server-side caching of your tiles. This can either be a good thing or a bad thing, depending on how many distinct values you'll have for your filters. Some mapping servers such as GeoServer+GWC allow you to whitelist which CQL filter values should be cacheable, which is helpful if you have a relatively small list of possible values. The CQL filter component of the JWT token is optional and the token can still be used to whitelist layers without a filter.


Availability


This is immediately available on the Dimetric Cloud platform, on our WMS, WFS, and WMTS services. Dimetric is the first geospatial infrastructure service to be geared towards developers. Our focus is on developer-friendly APIs, industry-standard best practices, and real-world requirements.


Please share your thoughts in the comments below!

165 views0 comments

Recent Posts

See All