OAuth for SmartThings

Hello,

I have a classic Smart App (installed via graph.api.smartthings.com), which I’m used to accessing programmatically, authenticated/authorized via OAuth. I’ve been successfully using SmartThings. Add a little smartness to your things. and https://graph.api.smartthings.com/oauth/token to get an OAuth token for years. Recently (this month), the browser code started giving me a CORS error, indicating that the Access-Control-Allow-Origin header is not set by the /token endpoint. I’d expect the ACAO header to at least contain the domain of the redirect URL, as configured in the SmartApp’s OAuth config.

My questions are:

  1. Is this CORS enforcement expected?
  2. Is there anything I can do to cause ST’s /token endpoint to include the redirected-to domain in its returned ACAO header?
  3. If there is no good option to address #2, can I instead use a Personal Access Token (from SmartThings. Add a little smartness to your things.) to access my classic Smart App (via the https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations, for example)?

Thanks!

Hi, @aburst42, it’s nice having you back.
That’s part of the legacy platform (please, take a look at this announcement), you should try updating to the new way of the OAuth integration.

What do you mean by “classic SmartApp”? What information do you want to get from it?

Can you describe more the function of your current integration, please? This way I can suggest a better alternative

1 Like

Thanks for the quick reply, @nayelyz!

I developed a smart app for my personal use - controlling home automation routines and getting status of SmartThings-connected devices, from my programmable Fitbit wearable. It essentially consists of securely calling SmartApp-exposed APIs via REST, controlling ST-connected devices and reading their state via the ST graph, and returning state to the caller. It was written in Groovy, on the legacy platform at graph.api.smartthings.com, hence “classic” (probably more aptly called “legacy”).

The high-level sequence was:

  1. When deploying the Fitbit client, go through the interactive authorization OAuth protocol at SmartThings. Add a little smartness to your things. and https://graph.api.smartthings.com/oauth/token
  2. Use the resulting token to call https://graph.api.smartthings.com/api/smartapps/endpoints and get the base URL of my installed smart app.
  3. Use the base URL and the token to formulate and invoke the REST endpoint with a JSON payload and receive results back.

Recently, step 1 started failing, because the /token endpoint’s Access-Control-Allow-Origin CORS header does not specify the domain to which the /authorize endpoint correctly performs the redirect (app-settings.fitbitdevelopercontent.com).

Right now, I’m trying to figure out if I can make a minimal change to get the system working again without a full rewrite of the app in the new infrastructure. In that context, my (rephrased) questions are:

  1. Is it possible to fix the OAuth flow for the legacy platform to allow CORS for the registered redirect domain?
  2. If not, can I use a PAT for my legacy platform smart app? Or are the PAT and OAuth2 authorization mechanisms reserved only for the new platform?
  3. If the latter, then it sounds like I have no other choice than to rewrite the smart app in the new platform, which I’ll do at some point, but I’d prefer not to be forced into it right now. If so, it sounds like a PAT is the way to go, and I don’t need to bother with the OAuth2 exchange (initial fetch and refresh), since this is a 1-user (me) solution. Is that correct?

Thanks again.

Cheers,
~alex

I made a request as shown below and I don’t receive errors:

//sample in nodeJS
var options = {
  'method': 'POST',
  'url': 'https://graph.api.smartthings.com/oauth/token?grant_type=authorization_code&client_id=...&client_secret=...&redirect_uri=...&code=...&scope=app',
  'headers': {
    'Authorization': 'Basic ...',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  form: {
    'grant_type': 'authorization_code',
    'code': '...',
    'redirect_uri': '...'
  }
};

In case you cannot avoid using the Access-Control-Allow-Origin header, yes, the easy path would be editing the app on the fitbit side to point at the ST API endpoints and use the PAT for the authorization. This way, you wouldn’t need the OAuth flow to get Access Tokens anymore.

I used the PAT for the endpoint https://graph.api.smartthings.com/api/smartapps/endpoints and I didn’t get any information, only the one from the SmartApp (“API endpoint” and “API token” in the IDE simulator), eg. https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/appId.

But, the PAT can be used to make other requests:

Note: Subscriptions are not available using this token type

Thanks again @nayelyz!

The node JS example you posted would work because it’s not running in a browser. With CORS, the call succeeds over the wire, and even returns a valid token, as evidenced by a Fiddler trace. It’s the browser itself that performs the current domain enforcement against the Access-Control-Allow-Origin header.

" I used the PAT for the endpoint https://graph.api.smartthings.com/api/smartapps/endpoints and I didn’t get any information"
I can confirm I see the same thing on my side - an empty JSON. In the legacy API, without a location, I cannot interact with SmartApps (right?), so that seems like a dead-end.

“, only the one from the SmartApp.”
I’m not sure what “only the one from the SmartApp” means in this case. It looks like the suggestions to use the PAT to “Get devices (list)”, “Get a device status” and “Send commands to devices” only talk about the new ST APIs, not the legacy ones. Can you please confirm that’s the case, rather than me misunderstanding your explanation?

Overall, it looks like using the PAT with the legacy endpoints is not an option, so I started looking into using a full control PAT (granted all the scopes, for testing purposes) from the Fiddler composer:

GET https://api.smartthings.com/v1/devices
Authorization: Bearer PAT
Content-Type: application/x-www-form-urlencoded
Host: api.smartthings.com

I’m getting back a 502 response:
image

Any idea what might be going on?

Cheers,
~alex

Apologies for the confusion, what I meant was using the endpoint https://graph-na04-useast2.api.smartthings.com/api/smartapps/installations/appId with the Access Token provided by the SmartApp in the IDE.

About the endpoints, these are part of the SmartThings API, before, you had to expose the endpoints through a SmartApp so you could interact with it and now, the communication is more direct.

In the case of the device endpoint (list, status, and command), you’ll be able to interact with all the devices no matter they’re using a DTH, because it is not limited to “non-Groovy devices”.

It could be caused by this section, using Postman, I got this snippet of the request using HTTP:

GET /v1/devices HTTP/1.1
Host: api.smartthings.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer PAT

Please, let me know if this helps.

This was very helpful in confirming my expectations.
Fiddler Composer and SmartThings don’t seem to play well together. I tried using curl instead, with the same parameters, and that worked as expected. Not sure whether the fault lies with Fiddler, with the SmartThings endpoints, or with a system in between the two.

My observations based on the curl invocations:
New APIs (api.smartthings.com) work with my (manually created) PAT
Old/Legacy APIs (graph.api.smartthings.com) work with my (manually fetched via the OAuth code exchange protocol) OAuth token
New APIs don’t work with the legacy OAuth token.
Old/Legacy APIs don’t work with the PAT.

Given that the Fitbit browser’s CORS protection is getting in the way of doing the code exchange and fetching a legacy OAuth token, I’ll have to move to the new APIs at some point.

Thanks for all the help, Nayely!

Is this the tool you’re using? I was able to make a request there

Happy to help! Let me know if you have more questions.

I was using Telerik’s Fiddler classic for Desktop, which has successfully worked for me in the past. Now that I know curl works, I’ll just use that. Thanks again!

Cheers,
~alex

1 Like