Link auth0 to @forge/api as an external, oauth connector returning unrecognized Bearer token

I need to secure API calls from a jira plugin to my server but am not getting a recognizable bearer token. I am using the @forge/api library to authenticate and make http requests with the library’s fetch function. My server uses auth0 to manage user authentication with auth0’s flavor of the access token flow.

I have followed the instructions on external oauth configuration and my code looks very similar to atlassian’s example code. I have set the client secret with forge providers configure

Here is my manifest file:

modules:
  jira:issuePanel:
    - key: <<key>>
      resource: main
      resolver:
        function: resolver
      viewportSize: large
      title: <<Title>>
      tooltip: <<tooltip>>
      icon: '<<https://icon.png>>'
  function:
    - key: resolver
      handler: index.handler
      providers:
        auth:
          - auth0
resources:
  - key: main
    path: static/app/build
    tunnel:
      port: 3000
remotes:
  - key: auth0-apis
    baseUrl: https://myAuth0Domain.us.auth0.com
  - key: myDomain-apis
    baseUrl: https://myDomain.com
permissions:
  scopes:
    - storage:app
    - read:issue:jira
    - read:jira-work # Depreciated
  external:
    fetch:
      backend:
        - 'https://myAuth0Domain.us.auth0.com'
        - '*.myDomain.com'
        - 'https://myDomain.com'
  content:
    styles:
      - unsafe-inline
providers:
  auth:
    - key: auth0
      name: Auth0
      scopes:
        - openid
        - profile
        - email
        - offline_access
        - https://myAuth0Domain.us.auth0.com/userinfo
        - https://myAuth0Domain.us.auth0.com/oauth/revoke
        - https://myAuth0Domain.us.auth0.com/oauth/token
        - https://myAuth0Domain.us.auth0.com/authorize
      type: oauth2
      clientId: <<myClientId>>
      remotes:
        - auth0-apis
        - myDomain-apis
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: auth0-apis
          path: /authorize
        exchange:
          remote: auth0-apis
          path: /oauth/token
          resolvers:
            accessToken: access_token
            accessTokenExpires: expires_in
            refreshToken: refresh_token
        revokeToken:
          remote: auth0-apis
          path: /oauth/revoke
        retrieveProfile:
          remote: auth0-apis
          path: /userinfo
          resolvers:
            id: sub
            displayName: email
app:
    id: ari:cloud:ecosystem::app/XXXXXXXXXXXXXXXXX-XXXXX-XXXXXX

Here is my login request with a console log for the new listCredentials() fn response:

export const login = async () => {
  const auth0 = api.asUser().withProvider('auth0', 'auth0-apis')
    if(!await auth0.hasCredentials()) {
      await auth0.requestCredentials()
    }
    // Extract credentials
    const creds = await auth0.listCredentials()
    console.log('Creds', creds)
    return
}

Here is a sample request to my server, using the @forge/api fetch:

export const test = async () => {
  const auth0 = api.asUser().withProvider('auth0', 'auth0-apis')
  console.log('Auth0-apis has creds:', await auth0.hasCredentials())
    if(!await auth0.hasCredentials()) {
      await auth0.requestCredentials()
    }
    const myDomain = api.asUser().withProvider('auth0', 'myDomain-apis')
    const postTestRes = await myDomain.fetch('/api/test/', {
        method: 'POST',
        body: JSON.stringify({
            message: 'Test message'
        })
    })
}

The auth flow against auth0’s authorize, exchange and userinfo endpoints seem to work fine. There are no errors in the forge tunnel or console logs and I see 200 responses in my auth0 logs.
But when I try to use the fetch api to call my own server, the bearer token @forge/api attached to the headers of the request is wrong and fails to authenticate. I expect a jwt style access token, and instead get something of this length and format:
authorization: 'Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
Likewise, I don’t recognize the credentials returned by auth0.listCredentials which are formatted like this:
00000000-000a-000a-a000-aa0000a0000a_000a0aa0-0a00-0000-00aa-0000000a0aa0:auth0:XXXXXXXXXXXXXXXXXXXXX/XXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXX==
I expect an access token because, in my manifest file, I have configured the exchange resolvers like below, and login fails when the resolver mapping is missing the access and refresh tokens.

resolvers:
  accessToken: access_token
  accessTokenExpires: expires_in
  refreshToken: refresh_token

Does anyone:

  • Recognize the bearer token
  • Recognize the credential format returned by listCredentials
  • Know how to append the accessToken presumably retrieved by the exchange action to fetch requests?
  • Know a different method to authenticate with auth0 as an external provider
2 Likes

Hi @TOrionWilmerding thanks for trying out external authentication!

When using external authentication, the application code does not get access to the user’s access tokens. They are automatically injected into the request by the system. What you are seeing there from listCredentials() is a unique ID of the account.

The place where the token is injected into the request is controlled by the bearerMethod option in the manifest.

In your case, the value injected in the header should be the value retrieved from the access_token parameter in the /oauth/token API.
In order for the login to succeed, it must have called the endpoint for retrieveProfile (in this case /userinfo), suggesting that the token is indeed a valid access token.

Unfortunately I do not know enough about Auth0 to know about how to use it with JWT style tokens.

I hope this helps!

1 Like

Thanks @MichaelCooper, we can work with that.