How do I list a customer's devices?

This worked as expected before we set up organizations.

But now I get the following error:

02-18 18:05:43.247 4037 4094 D Retrofit: <--- HTTP 400 https://api.particle.io/v1/devices (46ms)
02-18 18:05:43.247 4037 4094 D Retrofit: Server: nginx
02-18 18:05:43.247 4037 4094 D Retrofit: Date: Thu, 18 Feb 2016 23:05:43 GMT
02-18 18:05:43.247 4037 4094 D Retrofit: Content-Type: application/json; charset=utf-8
02-18 18:05:43.247 4037 4094 D Retrofit: Content-Length: 74
02-18 18:05:43.247 4037 4094 D Retrofit: Connection: keep-alive
02-18 18:05:43.247 4037 4094 D Retrofit: Access-Control-Allow-Origin: *
02-18 18:05:43.247 4037 4094 D Retrofit: Cache-Control: no-store
02-18 18:05:43.248 4037 4094 D Retrofit: Pragma: no-cache
02-18 18:05:43.248 4037 4094 D Retrofit: ETag: W/"4a-5e1fb355"
02-18 18:05:43.248 4037 4094 D Retrofit: OkHttp-Selected-Protocol: http/1.1
02-18 18:05:43.248 4037 4094 D Retrofit: OkHttp-Sent-Millis: 1455836743201
02-18 18:05:43.249 4037 4094 D Retrofit: OkHttp-Received-Millis: 1455836743247
02-18 18:05:43.249 4037 4094 D Retrofit: {
02-18 18:05:43.249 4037 4094 D Retrofit: "error": "invalid_scope",
02-18 18:05:43.249 4037 4094 D Retrofit: "error_description": "Permission denied"
02-18 18:05:43.249 4037 4094 D Retrofit: }
02-18 18:05:43.249 4037 4094 D Retrofit: <--- END HTTP (74-byte body)

These are the http logs from the android app, but I get the exact same response from the cloud API and the iOS app.

I've tried setting the "Can a customer access the device via the API" to "Yes":

This didn't solve the issue.

So now I'm trying to get a "scoped access token" for my customer:
https://docs.particle.io/reference/api/#generate-a-customer-scoped-access-token

But this is the response I get:

❯ curl --verbose -u <APP ID>:<APP SECRET> -d grant_type=client_credentials -d scope=customer=<CUSTOMER EMAIL> https://api.particle.io/oauth/token
*   Trying 52.4.72.147...
* Connected to api.particle.io (52.4.72.147) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.particle.io
* Server certificate: COMODO RSA Domain Validation Secure Server CA
* Server certificate: COMODO RSA Certification Authority
* Server certificate: AddTrust External CA Root
* Server auth using Basic with user '<APP ID>'
> POST /oauth/token HTTP/1.1
> Host: api.particle.io
> Authorization: Basic <AUTH TOKEN>==
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Length: 78
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 78 out of 78 bytes
< HTTP/1.1 503 Service Unavailable
< Server: nginx
< Date: Fri, 19 Feb 2016 22:08:54 GMT
< Content-Type: application/json; charset=utf-8
< Content-Length: 68
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Cache-Control: no-store
< Pragma: no-cache
<
{
  "error": "server_error",
  "error_description": "server_error"
* Connection #0 to host api.particle.io left intact
}

I've redacted the actual credentials, but I can email them to anyone that is willing to help with this.

I'm not sure if this step is even necessary for the request that I'm trying to make. I'm really just grasping at straws here.

The documentation for the SDKs seem to imply that getDevices should work for any authenticated user.

getDevices
List the devices that belong to currently logged in user and find a specific device by name:

Am I reading this wrong? Or are customers not treated as users?

Hey @lynn,

I’m Jeff, one of the engineers at Particle. Sorry you’re stuck but hoping that I could be of some assistance. The first thing I’d like to ask – have you taken a close read of the product creator guide in the docs? Specifically, the article on Authentication & Security would be most helpful to provide some clarity.

That request you made received a 503, which suggests a back-end server error on our part. Perhaps we were going through some downtime when you tried to make the request? I just hit the /oauth/token myself to create a scoped customer access token, and it worked. Here was my request, almost identical to yours:

curl -u <client-id>:<secret> -d grant_type=client_credentials -d scope=customer=jeff.xxxxx@gmail.com https://api.particle.io/oauth/token

This may be a silly question, but had you already created the customer using POST /v1/orgs/:orgSlug/customers? This documentation is found here. You will also get an access token back when you create a customer, like this:

{
  "token_type": "bearer",
  "access_token": "9ccXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "expires_in": 7776000,
  "refresh_token": "fc866XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "scope": "customer=jeff.xxxxxx@gmail.com"
}

This token should be stored in your DB if you are using two-legged auth and used to make requests to control the customer’s device.

Hope this helps,

Jeff

1 Like

Hi @jeiden thanks for your help.

It seems to be working now, but I haven’t changed anything at all. This may have been a temporary backend issue on Particle’s side.

Hi @jeiden, I’m sorry but I spoke too soon.

TL;DR - getDevices works after registration, but not after the user signs out and then signs back in again.

When I register a new user using the Android or iOS setup SDKs, the new user is authenticated using the following method:

Responses.LogInResponse signUpAndLogInWithCustomer(@Field("grant_type") String grantType,
                                                   @Field("email") String email,
                                                   @Field("password") String password,
                                                   @Path("orgSlug") String orgSlug);

This creates the following request / response:

---> HTTP POST https://api.particle.io/v1/orgs/<<< ORG SLUG >>>/customers
03-04 02:09:07.065  2336  2363 D Retrofit: Authorization: Basic <<< ORG TOKEN >>>==
03-04 02:09:07.065  2336  2363 D Retrofit: Content-Type: application/x-www-form-urlencoded; charset=UTF-8
03-04 02:09:07.066  2336  2363 D Retrofit: Content-Length: 89
03-04 02:09:07.066  2336  2363 D Retrofit: grant_type=client_credentials&email=<<< EMAIL >>>&password=<<< PASSWORD >>>
03-04 02:09:07.066  2336  2363 D Retrofit: ---> END HTTP (89-byte body)
03-04 02:09:07.765  2336  2363 D Retrofit: <--- HTTP 201 https://api.particle.io/v1/orgs/<<< ORG SLUG >>>/customers (699ms)
03-04 02:09:07.765  2336  2363 D Retrofit: Server: nginx
03-04 02:09:07.765  2336  2363 D Retrofit: Date: Fri, 04 Mar 2016 07:09:07 GMT
03-04 02:09:07.765  2336  2363 D Retrofit: Content-Type: application/json; charset=utf-8
03-04 02:09:07.765  2336  2363 D Retrofit: Content-Length: 178
03-04 02:09:07.765  2336  2363 D Retrofit: Connection: keep-alive
03-04 02:09:07.765  2336  2363 D Retrofit: Access-Control-Allow-Origin: *
03-04 02:09:07.765  2336  2363 D Retrofit: OkHttp-Selected-Protocol: http/1.1
03-04 02:09:07.765  2336  2363 D Retrofit: OkHttp-Sent-Millis: 1457075347371
03-04 02:09:07.765  2336  2363 D Retrofit: OkHttp-Received-Millis: 1457075347765
03-04 02:09:07.766  2336  2363 D Retrofit: {
03-04 02:09:07.766  2336  2363 D Retrofit:   "token_type": "bearer",
03-04 02:09:07.766  2336  2363 D Retrofit:   "access_token": "<<< ACCESS TOKEN >>>",
03-04 02:09:07.766  2336  2363 D Retrofit:   "expires_in": 7776000,
03-04 02:09:07.766  2336  2363 D Retrofit:   "refresh_token": "<<< REFRESH TOKEN >>>"
03-04 02:09:07.766  2336  2363 D Retrofit: }
03-04 02:09:07.766  2336  2363 D Retrofit: <--- END HTTP (178-byte body)

I am able to list the user’s devices with this token using the getDevices method from the SDK.

But then when I sign out and sign back in again, I am no longer able to list the user’s devices. The login request looks like this:

> 03-04 02:10:45.047  2336  2365 D Retrofit: ---> HTTP POST https://api.particle.io/oauth/token
> 03-04 02:10:45.047  2336  2365 D Retrofit: Authorization: Basic <<< ORG TOKEN >>>==
> 03-04 02:10:45.047  2336  2365 D Retrofit: Content-Type: application/x-www-form-urlencoded; charset=UTF-8
> 03-04 02:10:45.047  2336  2365 D Retrofit: Content-Length: 82
> 03-04 02:10:45.047  2336  2365 D Retrofit: grant_type=password&username=<<< EMAIL >>>&password=<<< PASSWORD >>>
> 03-04 02:10:45.047  2336  2365 D Retrofit: ---> END HTTP (82-byte body)
> 03-04 02:10:45.488  2336  2365 D Retrofit: <--- HTTP 200 https://api.particle.io/oauth/token (441ms)
> 03-04 02:10:45.488  2336  2365 D Retrofit: Server: nginx
> 03-04 02:10:45.488  2336  2365 D Retrofit: Date: Fri, 04 Mar 2016 07:10:45 GMT
> 03-04 02:10:45.488  2336  2365 D Retrofit: Content-Type: application/json; charset=utf-8
> 03-04 02:10:45.488  2336  2365 D Retrofit: Content-Length: 178
> 03-04 02:10:45.488  2336  2365 D Retrofit: Connection: keep-alive
> 03-04 02:10:45.488  2336  2365 D Retrofit: Access-Control-Allow-Origin: *
> 03-04 02:10:45.488  2336  2365 D Retrofit: Cache-Control: no-store
> 03-04 02:10:45.488  2336  2365 D Retrofit: Pragma: no-cache
> 03-04 02:10:45.488  2336  2365 D Retrofit: X-Content-Type-Options: nosniff
> 03-04 02:10:45.488  2336  2365 D Retrofit: OkHttp-Selected-Protocol: http/1.1
> 03-04 02:10:45.488  2336  2365 D Retrofit: OkHttp-Sent-Millis: 1457075445299
> 03-04 02:10:45.488  2336  2365 D Retrofit: OkHttp-Received-Millis: 1457075445487
> 03-04 02:10:45.489  2336  2365 D Retrofit: {
> 03-04 02:10:45.489  2336  2365 D Retrofit:   "token_type": "bearer",
> 03-04 02:10:45.489  2336  2365 D Retrofit:   "access_token": "<<< ACCESS TOKEN >>>",
> 03-04 02:10:45.489  2336  2365 D Retrofit:   "expires_in": 7776000,
> 03-04 02:10:45.489  2336  2365 D Retrofit:   "refresh_token": "<<< REFRESH TOKEN >>>"
> 03-04 02:10:45.489  2336  2365 D Retrofit: }
> 03-04 02:10:45.489  2336  2365 D Retrofit: <--- END HTTP (178-byte body)

There doesn’t seem to be any way to specify the grant_type for the login request from the setup SDK.

So why does the getDevices method work after registration, but not after login? Is this by design?

@jensck I’m going to lean on @jensck, our Android expert, to provide you with an answer on this one.

@jeiden - the same is true for iOS, but I’m unable to log successful requests from iOS so I just included the Android logs.

@ido could you be of some help here?

EDIT: nevermind, the SDKs are using the correct grant type.

@ido it looks like both SDKs may be doing this wrong – in org mode, we should be using a grant type of “client_credentials” for login too, not just for registration.

Seems like that should fix the problem.

cc: @lynn

Interesting, I’ll look into that.

@ido, @jensck - any updates on this?

@lynn sorry for the delay – if you force stop & clear data on the app you have installed, and then do a fresh login, are you then able to retrieve the device list?

Hi @jensck - thanks for getting back to me. Nothing seems to have changed, I still get a 400 response with a “Permission Denied” error after logging in and using the listDevices method. I cleared all of the data, uninstalled and re-installed the app. Still getting the same result.

Try running these curl commands:

First run this one to generate a new access token for the user
curl https://api.particle.io/oauth/token -u your_oauth_client_id:your_oauth_secret -d grant_type=password -d username=joe@example.com -d password=examplepassword

Then run this one (with the access token generated above in place of the “1234”)
curl https://api.particle.io/v1/devices\?access_token\=1234

Does running these commands give you a device list?

@jensck - it does not.

I ran the first command as follows:

❯ curl https://api.particle.io/oauth/token -u << MY_CLIENT_OAUTH_ID>>:<< MY_OAUTH_CLIENT_SECRET >> -d grant_type=password -d username=<< MY_EMAIL >> -d password=<< MY_PASSWORD >>
{
  "token_type": "bearer",
  "access_token": "<< ACCESS_TOKEN >>",
  "expires_in": 7776000,
  "refresh_token": "<< REFRESH_TOKEN >>"
}

Using the << ACCESS_TOKEN >> value from the first step, I ran the second command as follows:

❯ curl https://api.particle.io/v1/devices\?access_token\=<< ACCESS_TOKEN >>
{
  "error": "invalid_scope",
  "error_description": "Permission denied"
}

The OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET values work for customer registration, so I assume they’re valid.

@jensck - it looks like I’m experiencing this issue now even without the organization enabled.

This was not the case a few weeks ago

I just tried to perform the following actions:

  1. Create a new account via the Android Setup SDK
  2. Run the getDevices method after the new user has been authenticated.

Here are the logs from those actions:

I’ve tried this by creating new accounts, and also by signing into accounts that worked in the past. I get the same result in both cases.

Please advise.

Since you’re having these problems even with the curl commands, I’m not sure the problem is with the SDK(s).

@jeiden or @ido looking at these logs, can you see anything amiss? What about the curl commands given above ? It seems like that should succeed, but he’s still getting “invalid_scope”.

@lynn, I think the issue is related to including your organization’s OAuth credentials in your request to POST /oauth/token. Instead of using your organization’s client, you should just use -u particle:particle. Using your org’s OAuth client will infer scope that you don’t want.

So, your request would look like:

 curl https://api.particle.io/oauth/token -u particle:particle -d grant_type=password -d username=<< MY_EMAIL >> -d password=<< MY_PASSWORD >>

I reverted my codebase to a previous version and now I’m able to see the user’s devices without the organization. I must have made a mistake with the settings, or I may have failed to clear out the last build. Sorry for the confusion.

@lynn I looked into your issue further and I think you stumbled upon a bug. The initial access token when you created the customer had the correct scopes applied. Further tokens created for the customer, using the scoped client credentials, would’ve been incorrectly scoped.

I’ve got a fix queued up, so once it passes review it will go out. Sorry about the trouble!