Backend Integration

In this section you will enable your backend application to call the Fusebit HTTP API. This will allow your backend application to manage users and the access those users have to Fusebit functions. You will also learn how to invoke Fusebit functions from your backend application.

Managing Users within the Fusebit Platform

Managing users within the Fusebit platform is done by calling the Fusebit HTTP API from the backend of your application. To do this, you'll first need to create a client using the Fusebit CLI and give that client proper access to the platform. Setting up a client is very similar to setting up members of your team. Once you have your client, you'll add a little bit of code to your application to obtain access tokens for the client and then have the client call the Fusebit HTTP API to create users and give them access to particular Fusebit functions and boundaries.

Let's take a look at each of these steps in more detail.

Setting up a Client

The first step is to create the client itself. The only input you need to provide to the command is a display name. This can be any value that is meaningful to you.

Create a client with the CLI client add command:

fuse client add "Backend Application"

The output of the client add command should look like the following:

Client Added  │  Client 'clt-7cc9104fa50c4507' was successfully added

Backend       │  Id: clt-7cc9104fa50c4507
Application   │

Just like when setting up a user, the next step is to provide the client with the proper access. Since the backend of the application will be responsible for adding users to the Fusebit platform, the client must have access to perform user-related actions, user:*, on the account. In addition, the client also needs access to perform all function-related actions, function:*, on the subscription. This is because the client will be granting users access to particular functions or boundaries, and any agent (user or client) in the Fusebit platform must have that access itself in order to grant the access to others.

The Fusebit CLI commands to provide the client with the proper access below. We reference the client via the client id.

Grant access to the client with the CLI client access add command:

fuse client access add clt-7cc9104fa50c4507 user:*
fuse client access add clt-7cc9104fa50c4507 function:*

The output of the second client access command should look like the following:

Access Added  │  The access was successfully added to the client
              │  'clt-7cc9104fa50c4507'

Backend       │  Id: clt-7cc9104fa50c4507
Application   │
              │  Allow:
              │  • action:   user:*
              │    resource: /account/acc-9d9341ea356841ed
              │  • action:   function:*
              │    resource: /account/acc-9d9341ea356841ed
              │              /subscription/sub-9d9341ea356841ed

Issuing Access Tokens

The final step for setting up the client depends on how you provision and run the backend of your application. The client that we've just created will need a mechanism for obtaining JWT-based access tokens that it uses when calling the Fusebit HTTP API. There are two main ways to generate these tokens:

  • Authorization server, assuming it issues JWT-based access tokens (like Auth0, for example)
  • Your application backend

If you are relying on an authorization server to issue the tokens, the last remaining step is to register an issuer representing this server.

If you want to use your application backend to generate access tokens, you will need an asymmetric key pair and a JSON Web Token (JWT) library (example). One option is to use the Fusebit CLI to generate an asymmetric key pair on your local machine and register an issuer associated with that public key with the Fusebit platform. You can then export the private key and store it in your backend, and use the JWT library to issue tokens from your application code. Finally delete the key pair from your local machine, or keep it temporarily, so you can use the CLI to issue requests on behalf of the client for debugging purposes. Here are the steps:

  1. After creating the client, use the client init command of the Fusebit CLI
  2. Run the init command with the initialization token generated in the previous step. This step will generate an asymmetric key pair on your local machine and register an issuer associated with that public key
  3. Run profile get --includeCredentials -o json to create a convenient JSON serialization of your private key
  4. Transfer the generated JSON object and store it in your application backend. Please treat this JSON object as a secret, only transfer it over a private medium, and make sure it is secured at rest.
  5. Use the JWT library to generate tokens from your application code, using the private key along with the issuer and subject values from the JSON object\
const identity = // Load JSON object from previous step
const jwt = require('jsonwebtoken');
const options = {
    algorithm: identity.pki.algorithm,
    expiresIn: 60 * 60 * 24,
    audience: identity.baseUrl,
    issuer: identity.pki.issuer,
    subject: identity.pki.subject,
    keyid: identity.pki.kid,
    header: {
        jwtId: Date.now().toString(),
    },
};
const token = jwt.sign({}, identity.pki.privateKey, options);
  1. (Optional) Remove the asymmetric key pair from your local machine using the profile rm command

Adding Users to the Fusebit Platform

Once you have a client for the backend of your application and a mechanism for generating access tokens for that client, you can now have your backend application create users and grant them the proper access to a particular function or boundary. In fact, all of this can be accomplished with a single HTTP API call.

Below is an example for adding the user, Jane Doe, to the Fusebit platform. The crucial piece is the identity that is included in the request body. Any access tokens that the Fusebit platform sees with an iss value of https://sales-anchor.auth0.com and a sub value of google-oauth2|700634445110388888322 will be associated with Jane, and the request will be authorized only if it is for managing functions in the jane-doe-boundary boundary.

POST https://api.{region}.fusebit.io/v1/user
Authorization: Bearer {token}

{
  "firstName": "Jane",
  "lastName": "Doe",
  "identities": [
    {
       "iss": "https://sales-anchor.auth0.com",
       "sub": "google-oauth2|700634445110388888322"
    }
  ],
  "access": {
     "allow": [
        {
          "action": "function:*",
          "resource": "/account/acc-9d9341ea356841ed/subscription/sub-356841ed9d9341ea/boundary/jane-enduser-boundary",
        }
     ]
  }
}

Executing Fusebit Functions

To execute a Fusebit function you just make a regular HTTP call.

The HTTP method may be any one of the following: GET, POST, PUT, PATCH, DELETE, HEAD. Whether or not the Fusebit function performs different actions based on the HTTP method will depend on how the given Fusebit function was implemented.

The HTTP request body (if applicable) and response body will be in the JSON format.

The execution URL of the Fusebit function is specific to the function and is determined by the Fusebit platform at the time the Fusebit function is created. The URL can also be discovered via a Fusebit HTTP API call:

HTTP GET https://api.{region}.fusebit.io/v1/account/{accountId}/subscription/{subscriptionId}/boundary/{boundaryId}/function/{functionId}/location

See Get the URL for executing the function for details.

For each Fusebit function the execution URL may be different. The caller cannot assume the execution URL of a Fusebit function follows a particular pattern. However, once a function is created, the execution URL will remain stable.

const Superagent = require('request');

let functionUrl = '...';
Superagent.post(functionUrl)
  .send({ hello: 'there' }) // becomes ctx.body.hello in the function
  .then((res) => console.log(res.status, res.body));

The example above shows how to call an unauthenticated Fusebit function. The next section talks about adding security to the Fusebit functions.

Securing your Fusebit Functions

By default, Fusebit functions are unauthenticated. Anyone who knows the URL of the function can call it. If only the backend of your application should be allowed to call the Fusebit function, you must introduce an authorization mechanism. Since a Fusebit function has access to the entire HTTP request, you can use any HTTP-specific authentication or authorization scheme. However, the recommended and convenient way of securing your functions is to use the same authentication and authorization mechanisms that protect access to the Fusebit HTTP API, described in the Securing Functions section.

The simplest way to protect your Fusebit function is to specify authentication and authorization requirements in the function specification at the time of creation, for example:

PUT https://api.{region}.fusebit.io/v1/account/acc-9d9341ea356841ed/subscription/sub-9d9341ea356841ed/boundary/dev-john/function/hello
Authorization: Bearer {token}
{
  "nodejs": {
    "files": {
      "index.js": "module.exports = async (ctx) => { return { body: 'Hello, world!' } }"
    }
  },
  "security": {
    "authentication": "required",
    "authorization": [
      {
        "action": "function:execute",
        "resource": "/account/{{accountId}}/subscription/{{subscriptionId}}/boundary/{{boundaryId}}/function/{{functionId}}/"
      }
    ]
  }
}

The function specification above requires that the caller authenticates by presenting an access token that is trusted by Fusebit in the Authorization: Bearer {token} HTTP request header, and that the caller has at least the function:execute permission on the resource representing the function. If either check does not pass, the call is rejected with the HTTP 403 status code.

To invoke the function above, you must add the Authorization: Bearer {token} HTTP request header to the request, and supply an access token with at least the required permissions:

const Superagent = require('request');

let functionUrl = '...';
let accessToken = '...';
Superagent.post(functionUrl)
  .set('Authorization', `Bearer ${accessToken}`)
  .send({ hello: 'there' })
  .then((res) => console.log(res.status, res.body));

For quick experiments, you can get an access token representing your own Fusebit CLI client using fuse token -o raw command. Keep in mind these access tokens have short expiry times and should not be used for anything other than development.

Creating Access Tokens to Call Secured Fusebit Functions

When using an access token to invoke a secured Fusebit function, you must take into account that the code of that function (including all developers with access to the function), will be able to intercept that token and effectively leverage the permissions it represents to make their own calls to protected endpoints, for example the Fusebit API. Sometimes this is desired and useful. But in situations where the secure Fusebit function you are invoking exists in a different trust boundary (e.g. tenants of your system own the Fusebit function), you really want to use an access token with the minimum set of permissions required to invoke it rather than all permissions of your client or user.

The inline permissions of an access token address this situation by allowing you to limit its effective permissions to a subset of the permissions of the client or user it represents. To limit the permissions of an access token, you must include a custom JWT claim https://fusebit.io/permissions in the token payload, with the value representing the effective permissions you want the token to have. For example, to create an access token that has the necessary permissions to call the Fusebit function created in the previous section, you could construct the token as follows:

const payload = {
  'https://fusebit.io/permissions': {
    allow: [
      {
        action: 'function:execute',
        resource: '/account/acc-9d9341ea356841ed/subscription/sub-356841ed9d9341ea/boundary/dev-john/function/hello/',
      },
    ],
  },
};
const token = jwt.sign(payload, identity.pki.privateKey, options);

The code above will create an access token with permissions restricted to the ability to execute a single Fusebit function in the system, even if the token represents a Fusebit client with a much broader permission set, for example function:* permission on the entire account /account/acc-9d9341ea356841ed/. Note that inline permissions can only be used to restrict the permission set of the client or user. An attempt to use an access token with inline permissions that are broader than the permissions of the client or user it represents will result in an authorization failure and the HTTP 403 response.


Did this page help you?