paycheck

0.1.2 • Public • Published

PayCheck - a JSON payload authorization middleware

Lightweight, low footprint, compatible with any storage and server mechanism, allowing code reuse between frontend and backend.

How does it work

A backend API receives payloads. For example, an express server, websocket server or a worker that consumes a queue.

Different users and/or user roles or groups are authorized to send different types of payloads with different sets of parameters and values. You heard right - this isn't just about routes, it's also about authorizing resources.

As part of the login process, PayCheck can be invoked to generate a JSON object containing templates of expected payloads and the associated routes or JSON RPC methods. This can be stored by the invoking application, for example in a signed JSON Web Token, or redis.

For subsequent requests, PayCheck can be invoked to check the incoming payload against the templates generated earlier. PayCheck either rejects or accepts the payload based on if the template can be matched.

How do the JSON payload templates get specified in the first place?

Payload templates

Payload templates are compiled from 'templates', 'substitutions' and 'contexts' that are all associated with the user who is logging in. Basic templates can be created automatically by PayCheck via the 'listening' mode. After this, specific features can be edited manually.
Templates, substitution and contexts can be stored and retrieved using any kind of datastore. Dynamic substitutions that run code are hard coded by the app.

Simple templates have no variables, only fixed values and wildcards:

{
    myresource: ["a", "b", "c"]       // this means any of these values are accepted
    canBeAnything: "<%= * %>" // this is a wildcard, any value is allowed
}

This would allow the user associated with this template to access resources named "a", "b" and "c". For example the following payloads would be accepted:

{
    myresource: ["b"]
    // canBeAnything is undefined, and that's ok because it can be anything
}

or:

{
    myresource: ["c", "a"]
    canBeAnything: {"a" : "b"}
}

However, resources like this will probably be used again within multiple templates. Hence we support variables within templates:

{
    myresource: "<%= allowed_resources %>",
    myOtherResource: "foo"
}

and:

{
    myresource: "<%= allowed_resources %>",
    myOtherResource: "bar"
}

Templates defined like this require substitutions to be defined:

{
    allowed_resources: ["a", "b", "c"]
}

However, often the situation might require that the resources in the substitution are automatically kept up to date. To solve this, they can be derived by using a query at the point where the JSON payload templates are compiled, IE in the login step.

For example, the template could be:

{
    myresource: "<%= allowed_resources %>",
    myOtherResource: "bar"  //this must match exactly
}

The substitution would be defined in code by the application, eg:

{
    allowed_resources: function () {
        return myDataStore.getDataAndReturnPromise(this.myDataQueryQualifier) 
    }
}

And the context may need to also be provided:

{
    myDataQueryQualifier: "qualifierForCurrentUserOrRole"
}

At the point of login, PayCheck takes a set of templates, substitutions and contexts, and publishes payload templates, which are free from any variables or functions.

Code examples

Compiling payload templates via login step

var paycheck = new (require('../paycheck.js'))({})
paycheck.compile(templates, substitutions, contexts)
.then((payloadTemplates) => {
    storeInJWT(payloadTemplates)
}).catch((err) => {
 
});

Checking payloads

var paycheck = new (require('../paycheck.js'))({})
 
function receivePayloads(payload, next) {
    extractJWT(payload)
    .then((payloadTemplates) => {
        var checkedPayload = paycheck.check(payload, payloadTemplates)
        next(checkedPayload)
    }).catch(PayloadDenied, (err) => {
        // payload doesn't match templates
    }).catch((err) => {
        // other errors, eg JWT invalid
    })
}

Listening for payloads, converting them into payload templates.

var paycheck = new (require('../paycheck.js'))({})
 
paycheck.listenStart()
 
function receivePayloads(payload, next) {
    
    paycheck.observe(
        payload
    )
    
}
 
function finish() {
    var resultingTemplates = paycheck.observed; 
    paycheck.listenStop()   
    paycheck.observed = {};
    myDataStore.save(resultingTemplates) // TODO: these need to be converted into an array of payload templates for easier storage
}
 

Readme

Keywords

none

Package Sidebar

Install

npm i paycheck

Weekly Downloads

25

Version

0.1.2

License

GPL-3

Last publish

Collaborators

  • nightlife
  • nightlifemusic