New: Forge External Authentication Makes Outbound OAuth Easy

Reading Time: 4 minutes

If you've built a Forge app, you're probably familiar with managed auth—the platform "magic" that lets you make calls to Jira and Confluence product APIs without worrying about credentials.

With the release of external authentication, we've extended the "magic" of managed auth to external providers that use OAuth 2.0, making it easier to build Forge apps that connect with platforms like Google, Slack, GitHub, and many more. With a little configuration and setup, external auth handles the behind-the-scenes work of fetching, storing, and refreshing tokens.

OAuth 2.0 is a standard protocol that allows apps to access platform services on behalf of a user. The diagram below shows a typical OAuth flow—the back-and-forth requests that need to happen for an application (like a Forge app) to programmatically and securely get an access token for a third-party service. OAuth is the secure standard for requesting tokens, but as you can see, the authorization flow takes several coordinated steps.

Before external auth, building integrations with third party platforms required app developers to build the entire OAuth flow themselves, a process that can require significant effort, particularly if the third-party provider doesn't supply an SDK. The more third-party services an app needs to connect with, the more complicated it can become to securely request, store, and manage OAuth tokens for each new integration.

External auth cuts this complexity considerably. Instead of building out the logic to handle the OAuth flow, you can simply specify the service you want to connect with in the manifest.yml file. With a little configuration, external authentication requests, stores, refreshes and injects the token automatically into your fetch() requests, all without exposing the token’s values.

Why do we think external auth will make developers’ lives easier? Because we've been using it ourselves for awhile now.

Smart Links is a Confluence feature that surfaces contextual information about hyperlinks. It supports links from 25+ platforms and products like Slack, GitHub, Miro, and Google Drive. The link unfurls into a card that shows details like the last time the linked resource was updated, a preview, or the user who owns the resource.

Some details displayed in a Smart Link card should only be visible to a user who has authenticated access to that resource. For example, a Google Drive document or a Miro board might only be viewable by members of a user's Google Drive or Miro organization. In other words, Smart Links needs to be aware of a linked resource's permissions, and it needs to be able to request access to show certain details in Confluence.

With over 25 external providers to think about, we developed external auth as an internal service. Building an app like Smart Links became far less complex with external auth taking care of requesting, storing, and refreshing tokens. (We should also mention: Smart Links is built entirely on Forge).

Building Smart Links at Atlassian cloud scale has us super excited to share external auth with Forge developers. Not only does external auth enable a new wave of practical and genuinely useful apps, it allows Forge apps to enhance the tools that Atlassian customers use outside of Jira or Confluence, by bringing those tools into the spaces where users work on a daily basis.

Watch a demo

The best way to get a feel for what external auth can do is to see it for yourself. Here's a demo of Michael Cooper, Senior Software Engineer, walking through the process of setting up a new provider.

If you prefer to jump right into the code, there are several example apps that use external auth. View and clone the apps on Bitbucket:

Getting started with external authentication

Adding a new third-party OAuth service to your Forge app can be completed in just a few steps.

First, authentication settings are configured under the provider key in the manifest.yml file. Under this key, values are specified for client ID, requested scopes, remotes, and other details.

providers:
  auth:
    - key: google
      name: Google
      scopes:
        - 'profile'
        - 'https://www.googleapis.com/auth/userinfo.email'
      type: oauth2
      clientId: <client id>
      remotes:
        - google-apis
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: google-account
          path: /o/oauth2/v2/auth
        exchange:
          remote: google-oauth
          path: /token
        revokeToken:
          remote: google-oauth
          path: /revoke
        retrieveProfile:
          remote: google-apis
          path: /userinfo/v2/me
          resolvers:
            id: id
            displayName: email
            avatarUrl: picture

Then, configure remotes and permissions.

remotes:
  - key: google-apis
    baseUrl: https://www.googleapis.com
  - key: google-account
    baseUrl: https://accounts.google.com
  - key: google-oauth
    baseUrl: https://oauth2.googleapis.com
permissions:
  external:
    fetch:
      backend:
        - 'https://www.googleapis.com'
        - 'https://oauth2.googleapis.com'
        - 'https://accounts.google.com'

Finally, reference the function in the function definitions.

function:
  - key: main
    handler: index.run
    providers:
      auth:
        - google

Once these additions have been made to the manifest.yml file, you can call the new provider.

import ForgeUI, { render, Code, Macro, useState } from '@forge/ui';
import api from '@forge/api';

const App = () => {
  const [data] = useState(async () => {
    const google = api.asUser().withProvider('google', 'google-apis')
    if (!await google.hasCredentials()) {
      await google.requestCredentials()
    }
    const response = await google.fetch(`/userinfo/v2/me`);
    if (response.ok) {
      return response.json()
    }
    return {
      status: response.status,
      statusText: response.statusText,
      text: await response.text(),
    }
  })

  return (
    <Code text={JSON.stringify(data, null, 2)} language="json" showLineNumbers />
  )
}

export const run = render(
  <Macro
    app={<App />}
  />
);

To complete the setup, deploy the app and set the client secret when prompted.

forge deploy
forge providers configure google

Check out the external auth documentation for full details and examples.

Conclusion

The less time spent figuring out how to authenticate between platforms, the more time you have for solving interesting challenges and building new features.

With easier OAuth, there are all kinds of exciting new app possibilities. Imagine embedding Google Calendar into a Confluence page or highlighting a sentence in a Jira issue and sharing it to Slack. Integrations like these help teams work more effectively, with less context switching and more access to insights and collaboration. It's no surprise that integrations are some of the most popular apps in the Atlassian Marketplace.

Give external auth a try and let us know what you think. You can join the discussion in the Developer Community to provide feedback and ask questions. Happy building!