authentication methods for mattermost apps

Authentication Methods for the Mattermost Apps Framework

In the first part of this series, we explored the fastest way to get started with Mattermost Apps. In the second installment, we looked under the hood of a Mattermost App and examined how it works and how its components interact with each other. In this piece, we’ll outline the various authentication methods available using the Mattermost Apps framework.

The vital role of authentication

Authentication plays a key role in integrations because it’s necessary in order to prevent malicious usage and correctly regulate user interactions. In many cases, it’s also the first step that developers struggle with.

In this part of the series, let’s examine different authentication methods along with examples of how to access resources from apps that back a Mattermost instance.

The modular architecture of Mattermost Apps and Integrations makes it very simple to work with. In particular, the authentication protocols are rather straightforward. Keep reading for examples that illustrate how authentication can be accomplished in a variety of ways.

Method #1: No Auth/Open APIs

Let’s see how to connect to an API that doesn’t require any authentication. 

var info;

info = await fetch('https://yesno.wtf/api')
  .then(res => {
  return res.json();
  })
  .then(data => {
  return data;
  });

let message = info.answer+'\n'+info.image;//'Your Answer is ';
const submittedMessage = formValues.message;
message='The Answer is '+ message+'\n';

This is a sample from an application written in TypeScript. Obviously, this is the simplest of all means to connect with an API and consume information. JSON parsing methods within the language/framework in which the app is written help parse the information.

Not many APIs actually allow this kind of interaction. Therefore, we’ll need to learn how to handle other authentication methods. 

Method #2: Using API Keys

One of the most common methods of connecting with external APIs is by using an API key. These typically take the form of encoded strings of varying lengths. These strings are higher-order representations of binary data. API keys can also be known as access tokens or personal access tokens. Several products that offer APIs are switching to this method — most notably GitHub. 

Whether a REST connection uses API keys or personal access tokens, they are stored in .env files. These files are then read by the app and used for embedding inside the app when creating the payload for the GET/POST request being made.

While environment variables are a rather old paradigm in programming, the use of .env files has been popularized by the JavaScript community. In Node.js, there’s a core module called ‘process’ which contains an ‘env’ property. This holds the values of all the environment variables from the moment the (current) process is started. These variables could come from the shell, passed as arguments, or from .env files available in the root directory of the application. 

// Example .env file
USER_ID=”test51892397”
API_KEY=”jas9d8yiuemdsudo32j4kmeddsijl”
_ENV=”pre-prod”

Here’s how to read the .env file within a JavaScript application:

require('dotenv').config();

process.env.USER_ID // "239482"
process.env.USER_KEY // "foobar"
process.env.NODE_ENV // "development"

When used in the context of creating an API call, this is an example code snippet:

info = fetch("https://foo.com/api/v2/users?age=35, {
        "method": "GET",
        "headers": {
        "host": process.env.URL,
        "user": process.env.USER_ID,
        "key": process.env.USER_KEY
         }
})
.then(response => {
        return response.json()
})
.catch(err => {
        console.error(err);
});

The response variable — which is parsed and the contents assigned in a JSON format to the info variable — can be used for further processing. 

Method #3: Using Auth0

Using .env files to store API keys and personal access tokens is very convenient. But it brings about a few risks and several inconveniences. The chief risk is that often developers will commit the file to publicly available repositories. This will expose what should be private credentials and make them publicly visible. It’s also not a truly parameterized mechanism, which can cause problems when code has to interact in different environments.

Auth0 is an authentication platform that solves this problem — and solves it extremely well. Auth0 comes with two mechanisms to work with authentication, namely SDKs and web-based. It works with all popular web and mobile programming languages and provides a unified interface that can act as an effective security substrate when working with APIs.

Auth0 works with your applications to create a parameterized way to exchange API keys with designated endpoints. A user will have to first configure domains and keys using a valid Auth0 account and then make use of the data fetched from the account in order to complete authentication. The snippet below shows how to fetch and parse authentication information. 

// ..
const configureClient = async () => {
    const response = await fetchAuthConfig();
    const config = await response.json();

    auth0 = await createAuth0Client({
          domain: config.domain,
          client_id: config.clientId
  });
};

It can also be written to environment variables that can in turn be invoked from the application — just like what’s reflected in the previous example. 

Method #4: Using OAuth2 protocols

OAuth stands for Open Authorization. Currently, OAuth is in version 2.0 and is popularly referred to as OAuth 2.0. 

In case you’re unfamiliar, OAuth 2.0 is an open standard that uses access tokens internally and couples them with well-defined roles, scopes, and authorization codes. Its protocol mandates that every time a connection is made to an API using OAuth 2.0, the client requesting the connection must obtain a client ID and a client secret from the central authorization server.

The twin abilities of OAuth to work across platforms and help websites and web applications function without separate login information made the standard extremely popular. The use of OAuth pioneered the process that no longer required users to create and remember separate user IDs and passwords for related web applications and workflows. This also allowed settings to remain uniform across several services.

In order to make use of OAuth 2.0 credentials within an application, there are a few distinct stages. In this example, we will examine them in relation to the Mattermost Apps framework. The following are the five distinct stages:

1. Signing up for credentials (e.g., Google)

2. Configuring the app

3. Setting up admin permissions

4. Calling the login ‘handler’

5. Obtaining the ‘Config’

All the code snippets that follow are written in Go. They have been borrowed from a Mattermost App framework tutorial available here.

First, make sure that you have signed up for credentials from a web service that makes use of OAuth 2.0. Google credentials are a popular choice. You can make use of your Google username and password for this.

Next, the app has to be configured for making use of these credentials. A few moving pieces contribute to this. Add the act_as_admin and remote_oauth2 permissions to the manifest file. Also, remember to include bindings for configure and connect commands in order to configure the Mattermost app and also configure the client ID and client secret corresponding to the Google account.

func connect(w http.ResponseWriter, req *http.Request) {
        creq := apps.CallRequest{}
        json.NewDecoder(req.Body).Decode(&creq)

        json.NewEncoder(w).Encode(apps.CallResponse{
        Markdown: md.Markdownf("[Connect](%s) to Google.", 
creq.Context.OAuth2.ConnectURL),
         })
}

With the admin permission granted, the configuration method will affect changes internally to write the two key-value pairs using the StoreOAuth2App method. To persist these credentials within the scope of the user for the present app, Mattermost exposes an expand.oauth2_app property.

A ‘connect’ event triggers the OAuth connection required for the app. This will call the endpoint and prompt a user to supply their credentials (i.e., Google username/password). From the context of the request and response, the required data will be extracted and saved.

func oauth2Connect(w http.ResponseWriter, req *http.Request) {
         creq := apps.CallRequest{}
         json.NewDecoder(req.Body).Decode(&creq)
         state, _ := creq.Values["state"].(string)

         url := oauth2Config(&creq).AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.ApprovalForce)
         json.NewEncoder(w).Encode(apps.CallResponse{
         Type: apps.CallResponseTypeOK,
         Data: URL,
         })
}

func oauth2Complete(w http.ResponseWriter, req *http.Request) {
        creq := apps.CallRequest{}
        json.NewDecoder(req.Body).Decode(&creq)
        code, _ := creq.Values["code"].(string)

        token, _ := oauth2Config(&creq).Exchange(context.Background(), code)

        asActingUser := appsclient.AsActingUser(creq.Context)
        asActingUser.StoreOAuth2User(creq.Context.AppID, token)

        json.NewEncoder(w).Encode(apps.CallResponse{})
}

Finally, the client ID and client secret received will be used to configure the required services and set the scope for the app.

func oauth2Config(creq *apps.CallRequest) *oauth2.Config {
          return &oauth2.Config{
          ClientID: creq.Context.OAuth2.ClientID,
          ClientSecret: creq.Context.OAuth2.ClientSecret,
          Endpoint: google.Endpoint,
          RedirectURL:  creq.Context.OAuth2.CompleteURL,
          Scopes: []string{
                    "https://www.googleapis.com/auth/calendar",
                    "https://www.googleapis.com/auth/userinfo.profile",
                    "https://www.googleapis.com/auth/userinfo.email",
     },
}
}

As you can see, there are several ways to enable authentication for your apps — especially apps and integrations that you build for the Mattermost Apps framework. Please use our documentation to learn more about it, or join us on the community server to sweat out the details. 

In the meantime, you may also be interested in checking out the first two installments in this series:

Read more about:

mattermost apps
mm

Ram Iyengar is an engineer by practice and an educator at heart. He enjoys helping engineering teams around the world discover new and creative ways to work.