Handling Credentials in Custom Components

Handling Credentials in Custom Components

This post refers to an earlier version of Prismatic. Consult our docs on writing custom components with connections or contact us if you'd like help with any of the topics addressed in this post.

For B2B software companies, developing integrations for your customers usually involves authenticating against a variety of third-party APIs and services and juggling credentials for multiple customers, each of whom has a unique set of credentials for those APIs and services.

Authentication and credential management is an inherently hard problem. You need to manage customers' credentials is a secure way, and handle various methods of authentication, like basic auth, API keys, OAuth, and public / private key pairs. Managing the storage of credentials yourself can get messy, and handling the OAuth flow yourself can make you question why you got into software development in the first place. And honestly, it's not the best use of your time – you absolutely have to get it right, but your time would be far better spent working on the domain-specific aspects of your integration or moving your core product forward rather than implementing OAuth 2.0 from scratch.

Prismatic has simplified credential management (including the OAuth flow), so it's easy for you to manage multiple customers' credentials for multiple APIs in your integrations.

In this first of a two-part series on managing credentials in integrations, let's look at writing custom components that handle API keys, passwords, key pairs, and other sensitive information so you can integrate with as many niche or industry-specific third-party applications as your heart desires. After going through some basics, we'll pop open source code for Prismatic's built-in Google Cloud Storage component together, and examine how auth is handled against Google's cloud APIs.

Accessing credentials in your custom component

A common scenario when using Prismatic is writing a custom component that connects to a niche, industry-specific third-party application your customers use. Of course, those custom components typically need to handle authentication.

As we built Prismatic, we considered credentials to be a first-class citizen and made it easy to develop custom components that can use credentials from Prismatic's credential store.

In the simplest case, suppose we're writing a custom component that needs a username and password to integrate with a third-party API (basic auth). To add credential support to our custom component, all we need to do is add basic to the list of authentication types our component supports:

export default component({
  authorization: {
    required: true,
    methods: ["basic"],
  },
});

Once we have that, our component's actions will be able to access usernames and passwords that are bound to them:

const myAction = action({
  perform: async ({ credential }, params) => {
    const username = credential.fields.username;
    const password = credential.fields.password;
    // Do things with username and password...
 },
}

With just a half-dozen lines of code, our custom component has basic auth support.

Supported authentication types in Prismatic

In the example above we used basic auth (username and password). Some third-party APIs authenticate with a username and password, some others use API tokens, and still others use public/private key pairs for authentication. Let's talk about the various types of credentials that Prismatic supports.

Screenshot of Prismatic showing AWS S3 Credentials for Customer

Prismatic currently supports five types of credentials. Four of the five credential types that Prismatic supports save strings (like API keys, usernames, passwords, etc.) to a credential database. Those strings can then be passed into components of your integration when your integration runs. Let's take a closer look at these first four credential types:

  • API Key: An API key (or API token) has a single field, aptly named api_key. You typically see API keys when you interact with HTTP-based APIs, like SendGrid's email service, or Google Maps' API. An API key can be passed to an API as part of a query string, like POST /something?api_key=abc123, or as an HTTP header, like Authorization: Bearer abc123. Services that use JSON web tokens (JWTs) fall under the API key umbrella.
  • API Key / Secret Pair: Some services, most notably AWS, provide you with a key pair for authentication. AWS calls the pair of keys aws_access_key_id and aws_secret_access_key , but within Prismatic we generically call them api_key and api_secret respectively. Services that use key/secret pairs typically provide a client library that wraps the third-party's API, so you can authenticate with your key pair once and then perform multiple successive API actions.
  • Basic Auth: Do you remember when you created your first website back in the early 2000's, and uploaded files to an FTP server using a username and password? You likely used basic auth for that. You commonly see basic auth in FTP and HTTP transactions, and pass credentials using an HTTP header that looks like Authorization: Basic my-user-name:my-password. Services like Azure and Twilio, for example, have libraries that accept username/password combinations, so basic auth is appropriate.
  • Private Key: When I think about private keys I think about SSH and the services that are wrapped by SSH. Private Key authentication uses a username and a long private_key that generally begins with the line ----BEGIN PRIVATE KEY-----. You use private key authentication when you interact with SFTP servers or ssh-based git repositories, and Google Cloud Platform (GCP) uses private key authentication for API access.

The last type of credential that Prismatic supports deserves some special consideration, since it requires additional work behind the scenes. The other four credential types securely save username, password, API key, etc., in a database, and those values are passed into components when the component is invoked. OAuth 2.0 is different, and Prismatic takes care of some additional credential processing in the background:

  • OAuth 2.0: OAuth (open authorization) is an open standard protocol for authorizing a user to access a third-party application. Credentials are passed to the authorization URL of a third-party application and exchanged for an access code, which is then traded for an access token and refresh token.

    OAuth is complex, and probably contributes to some of the hair loss I've experienced over the years. Prismatic abstracts that complexity by taking care of exchanging authorization grants for tokens, and periodically refreshing the token for you. When you create an OAuth credential in Prismatic, you provide a client_id, client_secret, scopes, auth_uri, and token_uri, and someone – either you or your customer – enters credentials that will be sent to the OAuth provider. In the end, a simple API token is passed to the component that consumes the OAuth credential, and the token is used to interact with a third-party service. The OAuth credential type is accepted in Prismatic's Dropbox and QuickBooks components, for example.

Handling multiple credential types

Now that we've defined our credential types, let's look back at our simple example and examine how you can support multiple credential types in a single custom component. Suppose we want to integrate with a niche third-party API that accepts API tokens in addition to username/password combinations. In that case, we need to support api_key and basic auth. We can add these two auth types to the authorization block to our component declaration:

export default component({
  authorization: {
    required: true,
    methods: ["basic", "api_key"],
  },
});

Then, we can grab the credential object from within our component action's perform function like before.

The credential object contains the authorizationMethod that was used (basic or api_key), as well as the credential fields that were passed in. Previously we accessed credential.fields.username and credential.fields.password. In this example we'll also want to access credential.fields.api_key if we're not using basic auth:

import { ThirdPartyClient } from "thirdPartyLibrary";

const myAction = action({
  perform: async ({ credential }, params) => {
    if (credential.authorizationMethod == "basic") {
      const { username, password } = credential.fields;
      const client = new ThirdPartyClient({ username, password });
    } else {
      const { api_key } = credential.fields;
      const client = new ThirdPartyClient({ api_key });
    }
    // Do things with thirdPartyClient
  },
});

With a simple if block like this, we can handle authenticating against our third-party API with a username/password combination, or with an API token. If we later want to support OAuth 2.0, we can simply add another else if () block and pass in credentials.fields.token to our third-party library. Easy, huh?

Authentication in the Google Cloud Storage Component

With those basics down, let's look beyond stub code to a real-world scenario. Let's break apart Prismatic's built-in Google Cloud Storage component, which is used to upload files, delete files, move them around, etc., within Google's cloud platform. This component was written using the same framework that you use to write custom components, and is a good example for how you can manage authentication within your custom components. Source code for this component is available in our examples repo on GitHub.

The Google Cloud Storage component has several actions: one for listing files in a bucket, one for copying files, one for deleting a file, etc. It would be silly, and against DRY (don't repeat yourself) principles to repeat the same authentication code over and over, so we abstract authentication code into its own file named auth.ts and let each of our actions share that code. Here's the entire contents of auth.ts; we'll break down each line in a moment:

// auth.ts
import { Storage } from "@google-cloud/storage";
import { AuthorizationMethod, Credential } from "@prismatic-io/spectral";

export const authorizationMethods: AuthorizationMethod[] = ["private_key"];

export const googleStorageClient = (credential: Credential, project) => {
  const client_email = credential.fields.username;
  const private_key = credential.fields.private_key;
  return new Storage({
    projectId: project,
    credentials: {
      client_email,
      private_key,
    },
  });
};

We star by importing the Google Storage API client from the Google storage Node.js library. We then import some TypeScript definitions from Prismatic's Spectral library:

import { Storage } from "@google-cloud/storage";
import { AuthorizationMethod, Credential } from "@prismatic-io/spectral";

Next, we declare the auth methods that our component will accept. In this case, Google Cloud Platform expects an email address and private key, so the private_key auth method makes the most sense:

// auth.ts
export const authorizationMethods: AuthorizationMethod[] = ["private_key"];

// index.ts
import { authorizationMethods } from "./auth";

export default component({
  authorization: {
    required: true,
    methods: authorizationMethods,
  },
}

Note: we could have just added ["private_key"] to the component.authorization.methods declaration in index.ts – I just like having my code compartmentalized, so all the auth stuff is together in case I need to change it later.

Finally, we have a reusable function that takes in a credential object and the name of a GCP project, and returns a fully authenticated instance of a Google Cloud Storage client:

export const googleStorageClient = (credential: Credential, project) => {
  const client_email = credential.fields.username;
  const private_key = credential.fields.private_key;
  return new Storage({
    projectId: project,
    credentials: {
      client_email,
      private_key,
    },
  });
};

This googleStorageClient function is used by all five of our actions to simplify authentication. We don't need to write authentication logic for each of our actions. Instead, we can just pass in credentials and GCP project name to the googleStorageClient function and then start making API calls to GCP. Our deleteFileAction's perform function, for example, is really only made up of two lines: one that gets an authenticated API client, and one that deletes a file in Google Cloud Storage:

// actions.ts
const deleteFileAction = action({
  perform: async ({ credential }, { bucketName, fileName, project }) => {
    const storage = googleStorageClient(credential, project);
    await storage.bucket(bucketName).file(fileName).delete();
  },
});

This makes for very clean, readable, and modular code. If we want to add more auth methods to our component later, we can simply edit the code in auth.ts, and leave the rest of the component code alone.

Additional credential resources

This was the first of a two-part series on credentials in Prismatic. In this post we covered one facet of credential handling – handling authentication in custom components. In my next post we'll look at managing customer credentials within Prismatic and deploying instances with customer-specific credentials tied to them.

For more information on authentication methods check out our docs. We also have a quickstart that goes through the development of Prismatic's AWS S3 component, including how we handled authentication against AWS.


About Prismatic

Prismatic is the integration platform for B2B software companies. It's the quickest way to build integrations to the other apps your customers use and to add a native integration marketplace to your product. A complete embedded iPaaS solution that empowers your whole organization, Prismatic encompasses an intuitive integration designer, embedded integration marketplace, integration deployment and support, and a purpose-built cloud infrastructure. Prismatic was built in a way developers love and provides the tools to make it perfectly fit the way you build software.

Get the latest from Prismatic

Subscribe to receive updates, product news, blog posts, and more.