Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Ionic App Integration with SmartThings

ANSWERED

Hello,

 

I'm trying to write an app that runs on the Ionic (+ companion) and connects to the SmartThings web service. I followed the FitBit App OAuth example here, with a SmartThings Client ID and Client Secret generated as described here. I'm running the app in the simulator and getting the following error:
"Failed to load https://graph.api.smartthings.com/oauth/token: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://app-settings.fitbitdevelopercontent.com' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

Also, my settingsStorage.onchange handler never gets called, presumably due to the error above.

 

The odd thing is, if I run Fiddler while the simulator is making its calls, it looks like the call to graph.api.smartthings.com goes through fine (returns 200 OK), and even returns a proper bearer token. This is a valid bearer token, as I can use it in the Fiddler Composer to make successful calls to the SmartThings web service.

 

I have two resulting questions:

1. Which call results in the error reported in the simulator? Everything seems fine on the wire...

2. How do I fix the error and get the bearer token in the companion app, ready for subsequent calls?

Thanks for the help!

 

Cheers,

~alex

Best Answer
0 Votes
1 BEST ANSWER

Accepted Solutions

It's a CORS issue with the settings page. The only way to do this would be to use the <WebConfig> component and handle the token request manually. This isn't really documented, but you should find examples on Discord.

View best answer in original post

Best Answer
0 Votes
6 REPLIES 6

It's a CORS issue with the settings page. The only way to do this would be to use the <WebConfig> component and handle the token request manually. This isn't really documented, but you should find examples on Discord.

Best Answer
0 Votes

Wow, that worked like a charm... Thank you, @JonFitbit!

 

The gist of the solution, for reference:

1. Use the onReturn functionality of the OAuth component to persist the authorization response, including the short-lived OAuth code.

2. In the companion app, listen to storage events. When the persisted information from 1 (above), shows up, manually call the token endpoint of the OAuth WebApp to exchange the code for an actual token (for an example of how to do this, see the ExchangeToken function below, suggested by @JonFitbit). Then, store the token and use it as you see fit (likely an "Authorization: Bearer <token>" header on outgoing calls)

3. Enjoy the benefits of securely authenticated REST calls.

 

 

async function tokenRequest(body) {
  body += `&client_id=${oauthConfig.clientId}&client_secret=${oauthConfig.clientSecret}`;
  const response = await fetch(
    oauthConfig.requestTokenUrl,
    {
      method: 'POST',
      body,
      headers: {
        'content-type': 'application/x-www-form-urlencoded',
      }
    },
  );
  const tokenResponse = await response.json();
  if (tokenResponse.error) throw new Error(tokenResponse.error_description);
  return tokenResponse;
}

async function exchangeToken({ code }) {
  settingsStorage.removeItem('oauthResponse');
  const redirectUri = 'https://app-settings.fitbitdevelopercontent.com/simple-redirect.html';
  const data = `grant_type=authorization_code&code=${code}&redirect_uri=${redirectUri}`;
  const tokens = await tokenRequest(data);
  settingsStorage.setItem('oauthTokens', JSON.stringify(tokens));
}

 

Best Answer

Hello @Jon,

The solution above has been working great for my Fitbit to SmartThings integration. Unfortunately, without any code change (that is, I'm using the code above), I just started getting this error:
"Access to fetch at 'https://graph.api.smartthings.com/oauth/token' from origin 'https://app-settings.fitbitdevelopercontent.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

 

Any idea what might be going on? Thanks!

 

Cheers,

~alex

Best Answer
0 Votes

@JonFitbit, one more thing. It appears that the settingsStorage.onChange event no longer gets invoked when the OAuth code is fetched, which meant that I can't just take the code and exchange it for an OAuth token outside of the browser, and therefore impervious to CORS policies. Because of this, the previous workaround is unfortunately no longer an option. Any thoughts would be much appreciated. Thanks!

Best Answer
0 Votes

Do you know when this started happening? Is it the same on iOS or Android?

Best Answer
0 Votes

Unfortunately, I'm not sure exactly when it started happening. I noticed it over the winter holidays (late December 2021, early January 2022). It happens when the companion runs on my Android phone or in the desktop-based simulator. FWIW, when I'm the simulator, I can run Fiddler and see the /token call coming back fine over the wire - it's the browser that then notices that the domain of the current page is not in the access-control-allow-origin header returned by the server and refuses to render or, in this case, hand over the data to the settings code. If I manually tamper with the call and add access-control-allow-origin: * to the return, the code runs as expected.

Best Answer