405 error / CORS Allow Origin error

GreggomsGreggoms Member Posts: 5

After a few days of research and debugging, I'm here to see if anyone can shed some light on my problem. I apologize for the long post. I just want to be thorough.

Main Problem

The 405 error. I'm almost certain that fixing this will solve the CORS error. According to research, the 405 error means that the LS API recognizes my request, but rejects it. I'm not sure what is causing this rejection. The main fix is to make sure the requested URL is correct. This can't be my issue because the same URL works perfectly in Postman. Possibly something wrong in my headers? The only thing I can assume is that Lightspeed won't give access to http://localhost. Most other suggested fixes involve changing around backroom settings inside the CMS being used.

Overview

Here is my file:

import React, { useState, useEffect } from "react"
import Layout from "../components/layout"
import Seo from "../components/seo"
const axios = require("axios")


const IndexPage = () => {
  const [item, setItem] = useState([])
  const [loading, setLoading] = useState(true)


  // My super secret keys
  const key = process.env.ACC_ID
  const token = process.env.TOKEN
  const access_token = `Bearer ${token}`


  // Using Axios to make request, and storing it inside fetchItems to be called later inside a React useEffect hook.
  const fetchItems = async () => {
    axios
      .get(
        `https://api.lightspeedapp.com/API/Account/${key}/Item.json?load_relations=["ItemShops", "Category"]&ItemShops.qoh=>,1&categoryID=15&limit=5`,
        {
          headers: {
            Authorization: access_token,
            Accept: "application/json",
            // Some suggested adding this in the header, but it seems to achieve absolutely nothing.
            "Access-Control-Allow-Origin": "*",
          },
        }
      )
      .then(function (response) {
        // handle success
        console.log(response)
        // Remove the loader from screen and display the content.
        setLoading(false)
      })
      .catch(function (error) {
        // handle error
        console.log(error)
      })
      .then(function () {
        // always executed
      })
  }


  useEffect(() => {
    fetchItems()
    // eslint-disable-next-line
  }, [])


  return loading ? (
    <h2>Loading...</h2>
  ) : (
    <Layout>
      <Seo title="Home" />
      <h1>Hello World!</h1>
    </Layout>
  )
}


export default IndexPage

The Important Bit

  const fetchItems = async () => {
    axios
      .get(
        `https://api.lightspeedapp.com/API/Account/${key}/Item.json?load_relations=["ItemShops", "Category"]&ItemShops.qoh=>,1&categoryID=15&limit=5`,
        {
          headers: {
            Authorization: access_token,
            Accept: "application/json",
            // Some suggested adding this in the header, but it seems to achieve absolutely nothing.
            "Access-Control-Allow-Origin": "*",
          },
        }
      )
      ...
      ...
  }

Either I'm not providing sufficient credentials or I'm providing them incorrectly.

More Info

The error in Firefox Dev Tools. No errors appear in my development environment.

The successful request from Postman. Proof the URL is correct.

I've set both the Website and Redirect URI to http://localhost when setting up my Lightspeed Retail API Client at https://cloud.lightspeedapp.com/oauth/update.php

Bottom Line

The request is fine in Postman. When the same information is transferred to my IDE, I'm suddenly not authorized by Lightspeed. I don't know what I'm missing. Let me know if I can offer more information.

Links

Axios GitHub - https://github.com/axios/axios

Info on 405 Errors - https://airbrake.io/blog/http-errors/405-method-not-allowed

7 comments

  • paul1_apaul1_a Member, Moderator, Lightspeed Staff Posts: 72 moderator

    Hey,

    Thanks for reaching out! Are you still experiencing this error?

    Thanks

    Paul

  • GreggomsGreggoms Member Posts: 5

    Hello,

    I haven't found a solution yet. I still believe the problem lies within my fetch request.

    Here is how the calls interact over the network:

    I'm getting the "CORS Missing Allow Origin" and "NS_ERROR_DOM_BAD_URI" errors. The first error is caused by Lightspeed's rejection of my request. It returns an options call that is supposed to contain the "Access-Control-Allow-Origin" in the response header. It isn't listed though which makes me wonder how I'm supposed to gain access to the api.

    I thought part of the problem was caused from a cross-domain specification while trying to request information from https:// to http://. I stopped believing this when I hosted the project on an https:// Netlify url and was receiving the same issues.

    Another weird thing is when I click the NS error related to the POST request, the description says "The request method must be POST when requesting an access token". The browser is recognizing it as a POST as shown in the picture above, so I'm at a loss.

    I've read each documentation page at least 10 times now, some related community questions from others here, and loads of other forums, blog posts, and videos. I just can't seem to figure out where I'm going wrong. Cors doesn't seem to like me very much.

  • gregaricangregarican Member Posts: 706 

    This appears to be a challenge running Axios on a localhost. Here's a long discussion thread on their GitHub project site. Perhaps one of the workarounds in there might help?

    https://github.com/axios/axios/issues/569

  • GreggomsGreggoms Member Posts: 5

    I don't believe axios is the problem. I tried using the fetch API as well. Everything comes full circle to:

    Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api.lightspeedapp.com/API/Account.json. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
    

    I don't know how anybody else using the LS Retail API bypassed this. I've made many api calls in many projects. This api is proving more difficult to access than any I've used. Here are the only possible solutions I've come across:

    • Ask the owner of the api to enable cors support
    • Create a proxy server to handle the requests and/or create and use a middleware function -- I've never done either of these things.

    This article explains my exact problem:

    Through trial, error, and researching, my understanding is the the LS API does not offer cors support and I'm in no position to enable it. The above error keeps being thrown because the https://api.lightspeedapp.com/API/Account.json endpoint (for example) will ONLY give access to someone who is making a request from https://api.lightspeedapp.com. Since I'm using http://localhost:8000 (development) or https://juicemenu.netlify.app/ (production), it doesn't match the Same https://api.lightspeedapp.com Origin and automatically refuses anyone making a request from another url. This is a theory. I don't actually know who is given access since its not returned in the OPTIONS response header. I thought the purpose of setting up the API Client details at https://cloud.lightspeedapp.com/oauth/update.php was to "ok" access to localhost and then I would change it to the Netlify url when it was ready to be hosted, just like every other api.

    I've tried installing the cors package, but its use of express destroys my dev environment for some reason.

    If I swap the url to something like https://jsonplaceholder.typicode.com/todos using the same http request, it works perfectly fine and I receive the requested data. It is a "simple" call that doesn't require any credentials though. As soon as I add the

    Authorization: "Bearer {token}"
    

    header, it turns into a "non-simple" or "complex" http request. I've tried every combination of non-forbidden header out there but nothing is enough because I can't get the OPTIONS method returned by the browser from Lightspeed to return the Access-Control-Allow-Origin header.

  • gregaricangregarican Member Posts: 706 

    I self-host my own API web service, which in turn makes the API calls out to the Lightspeed Retail API endpoints. It works fine and doesn't fail due to CORS issues.

    While the source code below is in C#, you get the idea about what my Options method is doing. When it receives an OPTIONS request, it sends back the following header values.

    public HttpResponseMessage Options()

        {

          var resp = new HttpResponseMessage(HttpStatusCode.OK);

          resp.Headers.Add("Access-Control-Allow-Origin", "*");

          resp.Headers.Add("Access-Control-Allow-Methods", "*");

          resp.Headers.Add("Access-Control-Allow-Headers", "*");

          resp.Headers.Add("Access-Control-Allow-Credentials", "true");

          return resp;

        }

    It's up to the API server end to specify CORS policy. So you'd likely have to implement your own API web service in order to API clients to consume things.

  • GreggomsGreggoms Member Posts: 5

    All I'm trying to do is render product information returned from the LS API to my website using javascript.

    Here's what I've learned in this process:

    • The coder CANT send an options request. It can only be returned from the browser.
    • All of the "Access-Control-Allow-*" Headers are forbidden and CANT be set as request headers. Any attempt to do so will be ignored for security reasons. How it's working for you and many others out there is beyond me.
    • The requests work perfectly in postman and as cURL commands as long as my access token is renewed (still manually sending the request in postman every hour until this error is resolved)
      • This is what leads me to believe the LS API isn't set up for javascript http requests. Unfortunately, it's the only language I'm comfortable in.
      • I'm only allowed to fulfill my purpose if the "Access-Control-Allow-Origin" is returned as a response header from the LS API.
      • This missing header will only be returned if I'm requesting access from the same origin. I'm most definitely not. In fact, the LS Docs say to use https://localhost which doesn't make sense. Whos localhost uses a secured protocol?
    • Any other method to force myself into the API is frowned upon by the majority of other developers for one reason or another.
      • Or it's up to me to develop a backend with the sole purpose of bypassing browser security to gain access..? Not happening.

    It seems I'm misunderstanding the purpose of this API and/or how it's meant to be used. If it's up to the API server end to specify cors policy, what's stopping LS from modifying it to fit these needs? I scanned ~800 discussions and the 3 that were relatable weren't that helpful. Is my issue really that odd?

  • GreggomsGreggoms Member Posts: 5

    If anyone's still interested in helping me find a solution, check out the website for more info -> https://juicemenu.netlify.app/

Sign In or Register to comment.