Magic Cloud

An Open Source Application Generator


Magic Lambda Auth

Authentication and authorization helper slots for Magic. This project allows you to create and consume JWT tokens, to secure your magic installation. The project contains the following slots.

Notice, you will have to modify your auth:secret configuration setting, to provide a unique salt for your installation. If you don’t do this, some adversary can easily reproduce your tokens, and impersonate your users. Example of an “appsettings.json” secret you could apply can be found below (don’t use the exact same salt, the idea is to provide a random salt, unique for your installation)

However, during installation, Magic will automatically create a random secret for you. Below is an example of how this might look like.

{
  "auth": {
    "secret":"some-rubbish-random23545456-characters-goes-hereqwertygfhdfSDFGFDdfgh45fDFGH"
  }
}

The idea is that your SymmetricSecretKey is based upon the above configuration setting, implying you should keep it safe the same way you’d keep the pin code to your ATM card safe.

Below is an example of create a new JWT ticket.

auth.ticket.create
   username:foo
   roles
      .:howdy
      .:world

The above will return something resembling the following, minus the carriage returns, which are added for clarity.

auth.ticket.create:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1bmlxdWVfbmFtZSI6ImZvbyIsInJvbGUiOlsiaG93ZHkiLCJ3b3Js
ZCJdLCJuYmYiOjE2MTc5NDQyMzYsImV4cCI6MTYxNzk1MTQzNiwiaWF0
IjoxNjE3OTQ0MjM2fQ.
9eYROea_r-TJGT5k4H0DaGDW6LziHhEsK4on-uc-ECc

Typically Magic takes care of authentication for you, by exposing an authenticate endpoint for you, where you can submit a username and a password, for then to have your JWT token returned accordingly.

JWT tokens internals

A JWT token is actually just 3 JSON objects, that have been base64 encoded, and afterwards each entity is separated by a period (.). In fact, you can inspect a JWT token at for instance JWT.io to inspect its internals. A JWT token has 3 parts.

Unless an adversary knows your auth secret, it becomes impossible to create a valid JWT token. However, your token is still vulnerable for being stolen, so make sure you only return it over a secure HTTP connection, or something similar.

Using your JWT token

Magic expects the token to be specified as a Bearer token in your HTTP invocation’s Authorization HTTP header. To use the token therefor implies adding an HTTP Authorization header, and setting its value to something resembling the following.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xyz.qwerty

You can see how Magic does this internally by inspecting what the Magic dashboard transmits to the backend after having authenticated using e.g. Chrome Developer Tools.

Refreshing your token

JWT tokens have an expiration date. This is typically configured in your “appsettings.json” file in your Magic backend as magic:auth:valid-minutes, and once this time has passed, the token will no longer be considered valid, and using the token after this period, will result in an “Access Denied” being returned from the server.

However, since JWT token are just JSON, and the expiration time is just a Unix timestamp, you can parse the JWT token in your frontend, calculate the expiration date, and make sure you refresh your token before the expiration date is reached, and replace your existing token with the new token returned by Magic. Magic contains a refresh token endpoint to achieve this. Internally this endpoint will use the [auth.ticket.refresh] slot to create a new token, where the only difference is that it has a new expiration date.

Securing your endpoints

To secure an endpoint, and for instance require the user to belong to a specific role in order to be allowed to invoke an endpoint, you might add something such as the following to the top of your Hyperlambda files.

auth.ticket.verify:root, admin, user

The above will throw an exception unless the endpoint is given a valid JWT token, and the user belongs to one of the roles that you pass in as a comma separated list of role in its invocation. In fact, you can try this out, by pasting the following into your Hyperlambda evaluator in your dashboard and execute it.

auth.ticket.verify:non-existing-role

Inspecting your token’s payload

The following slots allows you to inspect the token’s payload.

Below is an example of how these are used.

auth.ticket.in-role:foo, root
auth.ticket.get

The first slot return true because your root user belongs to any (one) of the roles you supplied as a comma separated list of roles. The second slot above returns the username, and all the roles the user belongs to as a structured lambda object. As a general rule of thumb though, you’d not want to secure your endpoints using the above slots, but rather the verify slot above, to avoid having errors in your code resulting in that an unauthorized user gains access to invoke an endpoint he should not be allowed to invoke.

Project website

The source code for this repository can be found at github.com/polterguy/magic.lambda.auth, and you can provide feedback, provide bug reports, etc at the same place.

Quality gates

License

This project is the copyright(c) 2020-2021 of Thomas Hansen thomas@servergardens.com, and is licensed under the terms of the LGPL version 3, as published by the Free Software Foundation. See the enclosed LICENSE file for details.