retrieveProfile for Xero Oauth2 id_token

Greetings - I’m currently trying to authenticate to the Xero API using Forge external-auth. I can get most of the way through the OAuth2 flow but currently stumped at how to configure retrieveProfile for this case…

Xero does not (apparently) have an API to fetch the current user details. Instead the user details are provided by xero in a separate JWT (JSON Web Token) alongside the access_token.

The following API call is specified as the exchange action in the providers section of my forge manifest. It produces both the access_token PLUS an id_token in JWT format…

https://identity.xero.com/connect/token

Example response:

{
   "id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6...",
   "access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6Ij...",
   "expires_in":1800,
   "token_type":"Bearer",
   "refresh_token":"7c59b817313e71725d4043c...",
   "scope":"openid profile email accounting.transactions accounting.contacts accounting.settings offline_access accounting.attachments accounting.journals.read accounting.reports.read"
}

(ref: https://developer.xero.com/documentation/guides/oauth2/auth-flow/)

The id_token contains user details needed for retrieveProfile but I’m not sure how I am supposed to specify the mandatory retrieveProfile configuration in my forge manifest

Could anyone point me in the right direction?

manifest.yml attached for reference:

modules:
  macro:
    - key: hello-xero-app-hello-world
      function: main
      title: hello-xero-app
      description: Inserts Hello world!
  function:
    - key: main
      handler: index.run
      providers:
        auth:
          - xero
app:
  id: ari:cloud:ecosystem::app/d2d1dcb5-604a-44de-a590-faab9717434e
providers:
  auth:
    - key: xero
      name: Xero
      scopes:
        - 'openid'
        - 'profile'
        - 'email'
        - 'accounting.settings'
        - 'accounting.reports.read'
        - 'accounting.journals.read'
        - 'accounting.contacts'
        - 'accounting.attachments'
        - 'accounting.transactions'
        - 'offline_access'
      type: oauth2
      clientId: SECRET_SQUIRREL
      remotes:
        - xero-apis
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: xero-account
          path: /identity/connect/authorize
        exchange:
          remote: xero-oauth
          path: /connect/token
        revokeToken:
          remote: xero-oauth
          path: /connect/revocation
        retrieveProfile:
          remote: UNKNOWN
          path: UNKNOWN
          resolvers:
            id: UNKNOWN
            displayName: UNKNOWN
remotes:
  - key: xero-apis
    baseUrl: https://api.xero.com
  - key: xero-account
    baseUrl: https://login.xero.com
  - key: xero-oauth
    baseUrl: https://identity.xero.com
permissions:
  external:
    fetch:
      backend:
        - 'https://api.xero.com'
        - 'https://login.xero.com'
        - 'https://identity.xero.com'

Hi @drew , thanks for trying out Forge and External Authentication!

Unfortunately we do not currently support extracting information from JWTs for the profile retriever.

There is a work-around where you could point it at a random API that returns a 200 response code always, and then use a dynamic profile retriever to return some hard-coded response.

Eg. return an id of '1' always, and a generic name.

It’s not ideal, but the tradeoffs here mean:

  • A user cannot use multiple Xero accounts in the same app
  • The generic name means the user wont know which account it is
    • If the person uses the same app across multiple Atlassian products, they wont be able to know which Xero account its linked to

We may look to support the JWT profile information in the future, but for now we do not support it.