Facing CORS error while integrating scheduler

In the backend, I’m generating a URL using the Nylas Node.js SDK with the following code snippet:

const options = {
  clientId: Config.get('email.nylas.clientIdv3'),
  redirectUri: Config.get('email.nylas.authRedirectURIv3'),
  scopes: ['email', 'calendar', 'contacts'],
  state: randomString,
  accessType: 'offline',
};

let nylasURL = this._client.auth.urlForOAuth2(options);

After a successful login, I exchange the authorization code for an access token using this code:

const nylasResponseData = await this._client.auth.exchangeCodeForToken({
  clientSecret: Config.get('email.nylas.clientSecretv3'),
  clientId: Config.get('email.nylas.clientIdv3'), // This differs from the API key
  redirectUri: Config.get('email.nylas.authRedirectURIv3'),
  code: response.code,
});

My redirectUri while generating the url isn’t localhost, it is “https://mydomain.com/authNylas/store
I store the access token, grant ID, and other user-related information in the backend. Now, I want to integrate the Nylas scheduler into my Vue.js project. Since I’m using Vue.js, I’m importing the Nylas scheduler as a Vanilla JS wrapper. I send the access token from the backend to the frontend via an API, and the frontend has the access token. However, I’m facing an issue where the scheduler doesn’t function correctly and i receive CORS error whenever i try opening the scheduler. I’ve been stuck on this for a few days, and any help would be greatly appreciated. Here is the frontend code:

openV3Scheduler() {
  this.schedularEditorDialog = true;
  this.$nextTick(() => {
    const schedulerEditor = document.querySelector('nylas-scheduler-editor');
    schedulerEditor.schedulerPreviewLink = `${window.location.origin}/?config_id={config.id}`;

    const nylasApiRequest = new CustomIdentityRequestWrapper(
      'access token from backend',
      'https://api.us.nylas.com/v3'
    );
    schedulerEditor.nylasApiRequest = nylasApiRequest;
  });
}

Here is my CustomApiWrapper for Nylas:

export class CustomIdentityRequestWrapper {
  accessToken
  domain

  constructor(accessToken, domain) {
    // Initialize the class
    this.accessToken = accessToken
    this.domain = domain
  }
  async request(args) {
    try {
      const response = await fetch(`${this.domain}/grants/me/${args.path}`, {
        method: args.method,
        body: JSON.stringify(args.body),
        headers: {
          ...args.headers,
          Authorization: `Bearer ${this.accessToken}`,
          'Content-Type': 'application/json',
        },
      })

      // Check if the response is not okay (e.g., 404, 500)
      if (!response.ok) {
        console.error(`Error: ${response.status} ${response.statusText}`)
        return { error: `Error: ${response.status} ${response.statusText}` }
      }

      // Parse the response
      const data = await response.json()
      return [data, null]
    } catch (error) {
      console.error('Fetch error:', error)
      return { error: 'Error' }
    }
  }

  /**
   * This method returns the current user's information.
   */

  async currentUser() {
    // IMPLEMENT: Get the logged in user's ID token and return the user information
    return {
      // id: 'idToken',
      // email: 'abc@gmail.com',
      // name: 'Hammad',
      // provider: 'google',
    }
  }

  /**
   * This method sets the default authentication arguments to use when authenticating the user.
   */
  async setDefaultAuthArgs(authArgs) {
    // Set the default authentication arguments
    return authArgs
  }

  /**
   * This method returns the URL to redirect the user to for authentication.
   */
  async authenticationUrl() {
    // IMPLEMENT: Return the URL to redirect the user to for authentication
    return 'http://localhost:8080/scheduler-editor'
  }
}

My user has already authenticated his email and has given me permission for calendars, events and mailing. I want to skip the user from logging in again in the scheduler UI.

I am facing a similar issue.
I have tried all 3 approaches they suggest in their docs.

Approach 1: Sessions. That doesn’t work for me because the user has to login again from the scheduler editor which I don’t want.

Approach 2: Using existing nylas hosted auth Identity. In this approach, It doesn’t automatically login on scheduler editor either.

Approach 3: Custom auth Configure authentication for the Scheduler Editor Component | Nylas Docs. This doesn’t like localhost so I always get CORs on this.

try this if possible Regarding auth flow of email and scheduler - #10 by dhruv

or just add mode: ‘cors’, in the request after body

I did try it, Just to be clear. The user.id we are passing in

const response = await fetch(`${this.domain}/grants/${user.id}/${args.path}`

is the idToken that we receive from nylas upon registration right? Also this is the object the i receive from nylas upton registration.

accessToken: '',
refreshToken: ‘’,
grantId: '',
email: ‘’,
expiresIn: 3600,
idToken: '',
provider: 'google',
tokenType: 'Bearer',
scope: 'openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/contacts'

@dhruv In your post you said “I was using the wrong sub”. What do you mean by “sub”?

I am still seeing cors error even after copying your whole code.

The sub is basically the grant id of the registered user so you can directly use it if you do it though API_KEY

So we’re able to skip the login on scheduler-editor component.
The problem was that we were using accessToken in authorization Bearer header. We have to use API_KEY (i.e.NYLAS_V3_SECRET).

Now facing another problem when we try open the scheduler-editor and hit “Create New”

Here is our code in Vue:

<template>
  <div>
       <v-btn @click="openV3Scheduler">Open scheduler</v-btn>
       <v-dialog v-model="schedularEditorDialog">
          <nylas-scheduler-editor />
        </v-dialog>
  </div>
</template>


<script>
import { CustomIdentityRequestWrapper } from '@/utils/nylasApiRequestWrapper.js'

export default {
  name: 'CalendarScheduler',
  data() {
    return {
      schedularEditorDialog: false,
    }
  },
  methods: {
    openV3Scheduler() {
      this.schedularEditorDialog = true
      this.$nextTick(() => {
        const schedulerEditor = document.querySelector('nylas-scheduler-editor')
        const accessToken = 'XXXXXX' // passing API KEY here
        const domain = 'https://api.us.nylas.com/v3' 
        const nylasApiRequest = new CustomIdentityRequestWrapper(
          accessToken,
          domain
        )
        schedulerEditor.nylasApiRequest = nylasApiRequest
        schedulerEditor.schedulerPreviewLink = `${window.location.origin}/?config_id={config.id}` 

        schedulerEditor.defaultSchedulerConfigState = {
          selectedConfiguration: {
            requires_session_auth: false, // creates public configuration which does not require session tokens
            scheduler: {
              // organizer_confirmation_url: `${window.location.origin}/confirmation/:booking_ref`,
              cancellation_url: `${window.location.origin}/cancel/:booking_ref`,
              rescheduling_url: `${window.location.origin}/reschedule/:booking_ref`,
              // confirmation_redirect_url: `${window.location.origin}/booking-confirmed`
            },
          },
        }
      })
    },
  },
}
</script>

My class for custom request:

export class CustomIdentityRequestWrapper {
  accessToken
  domain

  constructor(accessToken, domain) {
    // Initialize the class
    this.accessToken = accessToken
    this.domain = domain
  }
  async request(args) {
    try {
      const user = await this.currentUser()
      const response = await fetch(
        `https://api.us.nylas.com/v3/grants/${user.id}/${args.path}`,
        {
          method: args.method,
          body: JSON.stringify(args.body),
          headers: {
            ...args.headers,
            Authorization: `Bearer ${this.accessToken}`,
            'Content-Type': 'application/json',
          },
        }
      )

      // Check if the response is not okay (e.g., 404, 500)
      if (!response.ok) {
        console.error(`Error: ${response.status} ${response.statusText}`)
        return { error: `Error: ${response.status} ${response.statusText}` }
      }

      // Parse the response
      const data = await response.json()
      return [data, null]
    } catch (error) {
      console.error('Fetch error:', error)
      return { error: 'Error' }
    }
  }

  /**
   * This method returns the current user's information.
   */

  async currentUser() {
    return {
      id: 'XXXX', // using grant ID here
      email: 'XXXX',
      name: 'XXX',
      provider: 'google',
    }
  }

  /**
   * This method sets the default authentication arguments to use when authenticating the user.
   */
  async setDefaultAuthArgs(authArgs) {
    // Set the default authentication arguments
    return authArgs
  }

  /**
   * This method returns the URL to redirect the user to for authentication.
   */
  async authenticationUrl() {
    // IMPLEMENT: Return the URL to redirect the user to for authentication
    return 'http://localhost:8080/scheduler-editor'
  }
}

What am I doing wrong?

Any ideas @dhruv on this error:

this might be because you haven’t imported node_modules becuase it doesn’t able to find nylas component so import node module file like this and yes sorry for late reply
import '../../node_modules/@nylas/web-elements/dist/nylas-web-elements/nylas-web-elements.esm.js';