RequestValidationError: Schema validation error when google Oauth

Hello There! I’m new in Forge development.
I’m trying to build an app who’s calls Google API and in the redirect URL i got:

RequestValidationError: Schema validation error

After that, i’ve follow the documentation https://developer.atlassian.com/platform/forge/use-an-external-oauth-2.0-api-with-fetch/

And my manifest looks like this now:

modules:
  'jira:issuePanel':
    - key: gcalendar-hello-world-panel
      function: main
      title: Gcalendarv4
      icon: https://developer.atlassian.com/platform/forge/images/issue-panel-icon.svg
  function:
  - key: main
    handler: index.run
    providers:
      auth:
        - google
app:
  id: my app id

  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:
  scopes:
    - 'read:jira-work'
    - 'write:jira-work'
  external:
    fetch:
      backend:
        - 'https://www.googleapis.com'
        - 'https://oauth2.googleapis.com'
        - 'https://accounts.google.com'

Providers:
  auth:
    - key: google
      name: Google
      scopes:
        - 'profile'
        - 'https://www.googleapis.com/auth/userinfo.email'
      type: oauth2
      clientId: (my id) apps.googleusercontent.com
      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

when trying to deploy i got this error

13:0    error    app should NOT have additional property 'remotes'  valid-document-required

Anyone if can help me would appreciate it.
Thanks
Ruben

@RubnOviedo,

I think you might be dealing with a compound problem. With multiple things going wrong, it’s hard to tell where to focus. To start, Forge Remotes should not be nested inside the app node, they are a top-level option. Unindent them please.

Hello @ibuchanan, first, thank you for your reply

Have to look like this?

modules:
  'jira:issuePanel':
    - key: gcalendar-hello-world-panel
      function: main
      title: Gcalendarv4
      icon: https://developer.atlassian.com/platform/forge/images/issue-panel-icon.svg
  function:
  - key: main
    handler: index.run
    providers:
      auth:
        - google
app:
  id: ari:cloud:ecosystem::app/d5467e22-85b1-4542-99f4-3bf7af3d3b74

 
permissions:
  scopes:
    - 'read:jira-work'
    - 'write:jira-work'
  external:
    fetch:
      backend:
        - 'https://www.googleapis.com'
        - 'https://oauth2.googleapis.com'
        - 'https://accounts.google.com'

Providers:
  auth:
      key: google
      name: Google
      scopes:
        - 'profile'
        - 'https://www.googleapis.com/auth/userinfo.email'
      type: oauth2
      clientId: myclientid

Again, sorry i’m too noob for this.

@RubnOviedo,

What I meant was change this:

app:
  id: my app id

  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

To this:

app:
  id: my app id

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

In YAML, whitespace matters. It seems that you have simply removed them (and some other things). Or is that a copy/paste error?

Perhaps the easiest thing would be to copy/paste the Google example from here: https://developer.atlassian.com/platform/forge/manifest-reference/providers/#example

Be sure to keep your existing modules and app sections.

The let us know what errors you get.

1 Like

Now perfectly understand the whitespace and now able to deploy the app.

modules:
  'jira:issuePanel':
    - key: gcalendar-hello-world-panel
      function: main
      title: Gcalendarv4
      icon: https://developer.atlassian.com/platform/forge/images/issue-panel-icon.svg
  function:
  - key: main
    handler: index.run

app:
  id: myid

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:
  scopes:
    - 'read:jira-work'
    - 'write:jira-work'
  external:
    fetch:
      backend:
        - 'https://www.googleapis.com'
        - 'https://oauth2.googleapis.com'
        - 'https://accounts.google.com'

providers:
  auth:
    - key: google
      name: Google
      scopes:
        - 'profile'
        - 'https://www.googleapis.com/auth/userinfo.email'
      type: oauth2
      clientId: myid
      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

but the Schema validation error persist.
image

Maybe is something wrong with my app?

import ForgeUI, { render, IssuePanel, Text, Button, useProductContext, useState } from '@forge/ui';
import api, { route } from "@forge/api";

const App = () => {
  const { extensionContext: { issueKey } } = useProductContext();
  const [transferStatus, setTransferStatus] = useState('');
  const [authUrl, setAuthUrl] = useState('');

  const clientId = 'myid';
  const redirectUrl = 'https://id.atlassian.com/outboundAuth/finish';

  const getAuthorizationUrl = () => {
    const authEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
    const scope = 'https://www.googleapis.com/auth/calendar';
    const responseType = 'code';

    const urlParams = new URLSearchParams({
      client_id: clientId,
      redirect_uri: redirectUrl,
      scope: scope,
      response_type: responseType,
    });

    const authUrl = `${authEndpoint}?${urlParams.toString()}`;

    return authUrl;
  };

  const moveToGoogleCalendar = async () => {
    try {
      // Fetch issue details from Jira
      const response = await api.asApp().requestJira(route`/rest/api/3/issue/${issueKey}`);
      const issueData = await response.json();

      if (!issueData) {
        throw new Error('Failed to fetch issue details');
      }

      // Construct the event data for Google Calendar
      const eventData = {
        summary: issueData.fields?.summary,
        description: issueData.fields?.description,
        start: {
          date: issueData.fields?.duedate, // Use date instead of dateTime
        },
        end: {
          date: issueData.fields?.duedate, // Use date instead of dateTime
        },
      };

      // Get the authorization URL
      const url = getAuthorizationUrl();

      setTransferStatus('Please authorize the app by copying the following URL into your browser:');
      setAuthUrl(url);

    } catch (error) {
      console.error('Error:', error);
      setTransferStatus('Transfer failed. Please try again.');
    }
  };

  return (
    <IssuePanel>
      <Text content={`Current issue key: ${issueKey}`} />
      <Button text="Move to Google Calendar" onClick={moveToGoogleCalendar} />
      <Text content={transferStatus} />
      {authUrl && (
        <Text content={authUrl} />
      )}
    </IssuePanel>
  );
};

export const run = render(<App />);

Really appreciate your help at this point @ibuchanan

Please if anyone can help me

@RubnOviedo,

It looks like your code is trying perform part of the OAuth flow. Like, in your UI code, you have the authUrl as if it were a hyperlink. The error you are getting is probably because that flow has to be paired with some back-end API exchanges. The point of these auth providers is to abstract all of that away.

Before trying to add your own calendar logic, I think you should make sure the example code for calling an API (with the google external auth provider) works in your app. Then, you’ll probably want to replace the example google.fetch('/userinfo/v2/me') with the more elaborate call to POST a calendar item.