Forge app providers return [NEEDS_AUTHENTICATION_ERR: Authentication Required]

My application need feature like login with Google to get access token for using Google rest API.
Here is configuration of my providers in Manifest:

modules:
  jira:globalPage:
    - key: google-provider-hello-world-page
      resource: main
      resolver:
        function: resolver
      title: google-provider
  function:
    - key: resolver
      handler: index.handler
      providers:
        auth:
          - google
resources:
  - key: main
    path: static/hello-world/build
    tunnel:
      port: 3000
providers:
  auth:
    - key: google
      name: Google
      type: oauth2
      clientId: <client key>
      remotes:
        - google-apis
      bearerMethod: authorization-header
      scopes:
        - https://www.googleapis.com/auth/userinfo.email
      actions:
        authorization:
          remote: google-account
          path: /o/oauth2/v2/auth
        exchange:
          remote: google-oauth
          path: /token
          resolvers:
           accessToken: access_token
        revokeToken:
          remote: google-oauth
          path: /revoke
        retrieveProfile:
          remote: google-apis
          path: /userinfo/v2/me
          resolvers:
            id: id
            displayName: email
            avatarUrl: picture
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:
  content:
    styles:
      - 'unsafe-inline'
    scripts:
      - 'unsafe-inline'
  external:
    styles:
      - '*'
    frames:
      - '*'
    scripts:
      - '*'
    images:
      - '*'
    fetch:
      client:
        - '*'
      backend:
        - 'https://www.googleapis.com'
        - 'https://oauth2.googleapis.com'
        - 'https://accounts.google.com'
app:
  id: ari:cloud:ecosystem::app/5129f53c-5d50-4432-9315-91944482b33d
  runtime:
    name: nodejs18.x

here is my index.ts:

import API from '@forge/api';
import Resolver from '@forge/resolver';

const resolver = new Resolver();

resolver.define('getText', (req) => {
    console.log(req);

    return 'Hello world!';
}).define("process-provider-google", async (req) => {
    try{
        console.info("1")
        const google = API.asUser().withProvider('google', 'google-apis')
        console.info("2")
        const isHasCredentials = await google.hasCredentials();
        console.info("3")
        console.info(JSON.stringify(isHasCredentials))
        if (!isHasCredentials) {
            console.info("-- start request credentials --")
            await google.requestCredentials()
            console.info("-- request credentials done --")
        }
        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(),
        }
    }catch(exception){
        console.error(exception);
    }
});

export const handler = resolver.getDefinitions();


my app.js in static project is:

import React, { useState, useEffect } from 'react';
import { GoogleLogin, googleLogout, useGoogleLogin } from '@react-oauth/google';
import axios from 'axios';
import {invoke} from "@forge/bridge"
function App() {
    const [ data, setData ] = useState(undefined);
    
    useEffect(() => {
        const googleProvider = async () => {
            const response = await invoke("process-provider-google")
            setData(response)
        }

        googleProvider();
    },[])
    return (
        <div>
            <p>test</p>
            {data ? <p>{JSON.stringify(data)}</p> : <p>{}</p>}
        </div>
    );
}
export default App;

I already run forge providers configure and set Client secret.
When run forge tunnel and request to add-on, it have error logs

[NEEDS_AUTHENTICATION_ERR: Authentication Required] {
serviceKey: ā€˜google2ā€™,
options: undefined,
status: 401
}

When code hit await google.requestCredentials() in index.ts it throw error.
Thanks for helping me.

Hi @HaiNguyen1, that error is expected and the front end should interpret that error and present a consent prompt for the end user to go through the OAuth dance. It should look something like this:


Is this what you are seeing?

1 Like

Hi @BoZhang , I dont get this prompt when run, does I miss any configuration ? Iā€™m using Forge/cli version 9.0.0 .

What do you see on the front end? and also what version of @forge/api are you using?

Itā€™s just error page with exception which throw from resolver. I canā€™t find version of @forge/api, but I think it related with version of @forge/cli that Iā€™m using (9.0.0).

The version of @forge/api is defined in your package.json file, please note that you must be on version 3.4.0 to use External auth with the native Node runtime. I donā€™t think there is anything wrong with the version of the CLI that you are using.
Are you able to provide a screen shot of the error page? Also, does it work when you are not running the tunnel?

1 Like

Here is my package.json which generated by cli ā€œforge createā€ to create project.

{
  "name": "jira-global-page-custom-ui",
  "version": "1.0.42",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "devDependencies": {
    "eslint": "^8.56.0",
    "eslint-plugin-react-hooks": "^4.6.0"
  },
  "dependencies": {
    "@forge/resolver": "1.5.31"
  }
}

So because I have try/catch in resovler so it will log error in console when I tunnel:

[NEEDS_AUTHENTICATION_ERR: Authentication Required] {
  serviceKey: ā€˜google2ā€™,
  options: undefined,
  status: 401
}

Thanks, so can I suggest that you explicitly import the @forge/api package? Currently, you are using it as a transitive dependency, which is slightly older than the latest version.

Also, can you not wrap await google.requestCredentials() in a try-catch block? This error needs to be thrown so the front end understands that it needs to render a consent prompt.

3 Likes

Wow, It work after I remove try/catch. Thanks for your support!

2 Likes