Example Web Setup App (Simple Authentication)

Hi guys

I’m working on setting up a web app for complete setup (create customer + setup WiFi + claim) of a product.
Firstly I’m wondering if anyone else has some example code of a similar setup? I know there is snippets of code in the Cloud API docs (which I’m planning to use), but I’m still a bit confused about how the overall workflow of the app should operate.

My understanding for a simple authentication setup is:

  1. Create OAuth client credentials
  2. Prompt the user for email & password and create a customer implicitly
  3. Use JS SoftAP library to connect the device to the internet
  4. Generate a device claim code for the product

Is this about right?

One thing I’m still not sure about is creating the OAuth client credentials, I see this isn’t yet available in the dashboard, but we can achieve this using the following cURL command:

curl -X POST -H "Authorization: Bearer 1234" -d name=MyApp -d type=web \
-d redirect_uri=http://www.particle.io/setup -d organization=my-org \
-d scope=create_customer https://api.particle.io/v1/clients

I’m confused however about the ‘redirect_uri’. Will the API now transfer the user to this URL every time you create a customer for this organisation?

Any ideas, @jeiden?

I’ve spent a whole day researching this but let me know if I’ve missed any relevant forum posts or docs :smile:

Cheers

1 Like

Hey @G65434_2! So excited that you are building a product on Particle! That is a very good question. Providing a redirect_uri allows us to stick to the OAuth spec of the "Implicit Grant Flow." Here's a quick summary of Implicit:

The Implicit Grant flow is used when the user-agent will access the protected resource directly, such as in a rich web application or a mobile app. The client secret is not used.

The user-agent connects to a URL on the authorization server. This could either be a direct connection, or through a redirect made from the client. The request contains the client id, the request scope, and the redirect URL. If the authorization server passes the request, it performs a redirect to the redirect URL with the access token and expiration time in the fragment (after the hash #).

While the redirect URL points to the client, code inside the client (that is, the server-side app) does not see it. Instead, the URL may be used to load JavaScript that takes the access token from the URL and uses it. Or, a mobile app can capture the redirect, extract the access token and use it in its code, in which case the URL may just point to static content.

That is to say, because you are building a web app, OAuth credentials are exposed in the browser and cannot be fully hidden/secured. As a result, using the implicit grant type only requires your OAuth Client ID, but not the Client Secret. The access token is appended to the URL of the redirect_uri after successful authentication with the Particle API. This redirect_uri should be the first step of your device setup after creating a Particle customer.

This all comes into play when you are creating a Particle customer for your web app so that the person can claim a device and generate access tokens to control that device. You will be hitting POST /v1/orgs/:orgSlug/customers to both create the customer and receive an access token back for that customer.

Because you are using the Implicit Grant Flow, you will use the "Implicit" version of the customer creation endpoint, Documented here. From the docs, you'll see that you pass your OAuth client ID as an HTTP header, but not your secret.

After a POST to this endpoint, the Particle API will redirect the customer to the redirect_uri that you provided when creating the OAuth client, and append the access token as a hash to the end of the url (i.e. http://setup.cool.com#123456789). The token is now available for you to grab via JavaScript to use to generate claim codes, complete setup, and allow the user to start controlling their device.

So, to recap:

  • For a web app, you will use the Implicit Grant Flow to protect your OAuth client credentials from being compromised
  • You should set your redirect_uri to be the start of the setup process after a customer has been created
  • This all comes into play when creating customers during device setup, and the API must be hit in a specific way to follow the Implicit Grant Flow
  • If done correctly, the customer will be created in Particle's system correctly, then redirected to start device setup with a scoped access token appended to the redirect_uri

Does this all make sense?

2 Likes

Hi @jeiden

Thank you very much for your detailed reply, it’s cleared a few things up thanks.
I have a few more questions, this may be painful sorry as I come from an embedded background and this is a huge learning curve for me!

Firstly, what actually is the OAuth client? Is it just a client ‘code’ and secret ‘password’ that is specific to my organisation and web app?
In terms of scope, does that mean that if you want a server to be able to talk to all of your customers devices then that server must possess both client code and secret, however if the server only needs to be able to create customers then it just needs the client code? So in effect the OAuth client (+ secret) is kind of a master key?

In terms of the process flow, is the idea that any SoftAP setup page should really always make sure that the customer is logged in before sending the WiFi credentials, so that the claiming can be done at the same time, like how the Particle mobile app currently works?

Currently I was planning on having a ‘new customer account creation’ page and then putting the URL for my softAP page in ‘redirect_uri’. Is that the idea?

Finally, I’m still a bit confused on the notation in the docs (I’m new to HTTP stuff). What is the connection between the ‘definition’ and ‘example request’ in the docs e.g. does

GET /v1/orgs/:orgSlug/products/:productSlug/customers

just translate to:

curl https://api.particle.io/v1/orgs/:orgSlug/products/:productSlug/customers

Thanks very much for your patience here!

One more question that someone may be able to answer, what is a recommended package for making POST/GET requests from JS? I see another user here had used httparty, any other suggestions?

@G65434_2,

OAuth credentials are a means of authenticating your web/mobile applications that interact with the Particle cloud. Put simply, it makes communication between you and the Particle cloud secure. It’s a way of limiting access of who can request information about your organization, it’s products, and customers, as well as locking down interactions with your customers’ devices to only authorized parties.

OAuth credentials are composed of two parts: A client ID and a client secret. Depending on your method of authentication, you will need one or both of these to make calls against the API. OAuth creds are like an access token, but represent organizational-level access instead of user-level access.

These credentials together represent the “keys to the castle”, and the combination of the ID and secret should be kept secret and never publicly exposed. In some cases, like with web-based authentication that run in a browser, it is OK to expose the client ID (this is required for implicit grant flow described above).

Your approach to your flow looks good to me. You should create the customer, then have the redirect_uri be the first page of the softAP setup. That way you’ll have a customer access token to create a claim code. You should base your flow off of the current mobile app.

Admittedly, we should really have a better JS SoftAp SDK that is up to par with the iOS and Android SDKs. Our team hasn’t really had the bandwidth to to this with the impending launch of the Electron. But once the Electron ships I think this will become a bigger priority.

As for your confusion with the endpoints, the top request leaves out the base url of the API for brevity (https://api.particle.io). The second is more of a copy-pastable request (you would still need to sub in your org slug and product slug) that can be executed via curl (https://github.com/bagder/curl).

For making requests from JS -> if you are using node the front runners are:

request: https://github.com/request/request
superagent: https://github.com/visionmedia/superagent

For a simpler app that just uses jQuery, you can use $.ajax(): http://api.jquery.com/jquery.ajax/

1 Like

Hi @jeiden

Thanks again for your excellent reply.
An improved JS SDK would be awesome as that’s what I’m used to using but I fully understand you guys are busy!

I’ve created my OAuth credentials all good, so now I’m on to creating a customer. I’ve tried to emulate what another user here was doing Creating a New Customer (two legged auth) - Now Working
Although this was with two legged auth and using Httparty, of which I’m not familiar with.

I’m trying this so far:

$.post(https://api.particle.io/v1/orgs/myCompany/customers,{
   client_id: myClientID,
   email: email,
   password: password,
   response_type: token
});

However I feel that I’m not passing authentication information properly. The docs say it should be passed as an HTTP Basic Auth Header, however I understand that I only need to pass my client ID to create a customer right? Or do I need to authenticate as well using my personal email & password?

Thanks in advance!

I’ve managed to get this working using the Google Chrome Rest Client add on:

However still no luck using .ajax or .post (I’ve tried just about every combination I can think of).

@bko, I see you’ve done some of this in other posts, any ideas?

I am not sure why you are having trouble–when I want to debug this type of thing I use a service like requestb.in to see exactly what works and what I am doing wrong. Set up a bin and use your REST client to send a “known good” request and then use your AJAX code to send the one you need to debug.

With the $.ajax calls are you sure the content type field is right? Passing the content type field via setRequestHeader as ‘application/x-www-form-urlencoded; charset=UTF-8’ might be safer since you are doing cross-domain.

Thanks for the link, @bko, that site is really useful. Now I seem to be having some CORS issues with AJAX, (I didn’t even know what CORS was this morning). I’ve tried setting my content type as you’ve suggested but still no luck.

I get a message from the console saying: ‘No ‘Access-Control-Allow-Origin’ header is present on the requested resource.’

I’m struggling to even get a basic message through to requestb.in from the $.ajax call.

Ok, so I still haven’t managed to use any of the HTTP testing sites due to cross origin issues.

This is my most recent attempt however it’s just making the browser hang…

$.ajax({
url: "https://api.particle.io/v1/orgs/myCompany/customers",
type: "POST",
dataType: "json",
  beforeSend: function(xhr) {
    xhr.setRequestHeader("client_id", "myAppID"),
    xhr.setRequestHeader("content-type", "application/json")
},
data: {
  email: johnsmith@gmail.com,
  password: johnspassword
}

});

Any ideas @zachary? @jeiden? Sorry to pester you guys, I’m just at a dead end and other jobs are piling up!

@G65434_2 - To tease apart OAuth setup issues from request construction issues, have you tried creating customers using the curl request templates from the docs? I assume you’re using simple auth, which means you probably created a client using a request like this:

$ curl -X POST -H "Authorization: Bearer [access_token]" -d name=[name] -d type=installed -d organization=[org-slug] -d scope=create_customer https://api.particle.io/v1/clients

I created two clients, one with -d scope=create_customer and one without. Then I ran this for each:

$ curl -X POST -u "[client_id]:[client_secret]" -d email=[email] -d no_password=true https://api.particle.io/v1/orgs/[org-slug]/customers

For the one without the scope, I got the following error:

{
  "ok": false,
  "code": 400,
  "error": {
    "name": "OAuth2Error",
    "message": "No valid scopes",
    "stack": "Error: No valid scopes\n    at saveAccessToken (/spark/api_service/releases/4652fd0e26bcc03a81d0d06f2e3bb73178d0be37/lib/OAuth2ServerModel.js:131:19)\n    at Grant.saveAccessToken (/spark/api_service/shared/vendor/node_modules/oauth2-server/lib/grant.js:420:14)\n    at run (/spark/api_service/shared/vendor/node_modules/oauth2-server/lib/runner.js:15:14)\n    at /spark/api_service/shared/vendor/node_modules/oauth2-server/lib/runner.js:17:7\n    at /spark/api_service/shared/vendor/node_modules/oauth2-server/lib/grant.js:395:3\n    at OAuth2ServerModel.generateExpiresTime (/spark/api_service/releases/4652fd0e26bcc03a81d0d06f2e3bb73178d0be37/lib/OAuth2ServerModel.js:246:3)\n    at Grant.generateExpiresTime (/spark/api_service/shared/vendor/node_modules/oauth2-server/lib/grant.js:387:13)\n    at run (/spark/api_service/shared/vendor/node_modules/oauth2-server/lib/runner.js:15:14)\n    at /spark/api_service/shared/vendor/node_modules/oauth2-server/lib/runner.js:17:7\n    at /spark/api_service/shared/vendor/node_modules/oauth2-server/lib/grant.js:375:5\n    at Object.ondone (/spark/api_service/shared/vendor/node_modules/oauth2-server/lib/token.js:55:5)",
    "headers": {
      "Cache-Control": "no-store",
      "Pragma": "no-cache"
    },
    "code": 503,
    "error": "server_error",
    "error_description": "server_error"
  }
}

Re-running that exact line, I saw a successful message indicating that the request still somehow succeeded:

{
  "ok": false,
  "code": 400,
  "error": "customer_exists"
}

Running that request for the client without the scope, I immediately received a success response with an access token. So basically, I think there may be something up with simple authentication, though it’s worth noting that the requests above were actually from the docs for two-legged auth. There may be a good reason that simple auth says “Specific implementation details coming soon”.

1 Like

Thanks for your reply @indraastra
I haven’t actually created a customer using Curl. However I have done it using the Chrome Rest API Client. (see above screenshot).
Although it isn’t clear in the screenshot, this POST was successful and returned the redirect_uri with access token appended as desired. I just haven’t managed to replicate this from an ajax call.

Ah, sorry, I missed that you were using a web OAuth client, not an installed one. I think your CORS issues are a result of the redirect URI you are using (particle.io/setup by any chance? :P), not anything to do with your request or authentication.

This HTTP request/response service has very permissible CORS headers, so you can use it until you have your own web page set up to handle the client access token:

https://httpbin.org/

Try creating a new client with -d redirect_uri=https://httpbin.org/get and re-running your $.post example above with that client id. I’ve tried it and it should work.

My redirect URI is actually pointing toward my own website e.g. myCompany.io/setup.
The requests are coming from my computer, running the HTML & JS locally.

I’ve tried many of those services, requestb.in, mockbin etc. and I can’t even post generic content using ajax from my localhost. Always either hangs or comes back with the No-Access-Control-Allow-Origin message…

Thanks for your PM @indraastra! (I’m replying here in case anyone else can benefit from the discussion).
I’ll keep your code private, for your peace of mind. I really appreciate it thanks.

So yes, with your OAuth set to redirect to httpbin, there seemed to be no problems, and my post was successful. I substituted my information back into your code and was greeted with the usual CORS related error:
(I’ve blacked out the redirect_uri as my company hasn’t fully gone public yet)

So, I obviously need to be redirecting to my own setup page. Will this issue go away if I’m hosting both account creation and setup on the same server? I’ll try it now.

1 Like

It looks like CORS issues are to be expected unless the redirecter and the redirectee share a domain:

https://code.google.com/p/chromium/issues/detail?id=154967

Unfortunately, the redirecter is api.particle.io, so I’m not sure if your idea will work. I think you’ll have to allow cross-origin requests on myCompany.io/setup (or whatever the redirect target is) for this to work out, but @jeiden would know better.

Thanks @indraastra, yes my lack of knowledge on CORS means I’ve had a bit of a try everything approach. But it looks like you’re correct in your understanding. My idea didn’t work.
I’ll do some research on allowing cross origin requests. I’m a bit surprised no one has posted about the same problem yet but perhaps they figured it out on their own. Thanks again for your help!

So from some quick research it seems we can add something to the htaccess file on the host server.

Header set Access-Control-Allow-Origin: http://myCompany.io

However, since my public facing webserver is NGINX, I’ll need to add the code from the following website to the nginx.conf file I believe:
http://enable-cors.org/server_nginx.html

Before I do this, @jeiden or @zachary could you let me know I haven’t gone completely off track? :smile:

1 Like

@G65434_2 thanks for helping pave the way for others here. I need to discuss with some other engineers here, but I believe that you’ll need to use localhost tunneling + separate OAuth clients for each environment/domain to avoid this CORS issue – one for your local development and another for your production domain.

I’m pretty sure that the issue lies in the fact that you are running a development server on localhost but are redirecting to an http, public-facing URL. You need to “tunnel” your local development environment to be exposed as a public URL.

The one I’ve used before is ngrok: https://ngrok.com/.

Once you have tunneled your local development environment to be served on a public URL, your development server will be running on a URL like http://92832de0.ngrok.io.

Then, you can use this URL as the base for your redirect_uri when creating an OAuth client for development purposes. For instance, if the first step after customer creation was /setup, you would set the redirect_uri to http://92832de0.ngrok.io.

Does this make sense? You should get in the habit of creating separate OAuth clients for each domain that your app would be running on, and swap out those clients for each environment.

3 Likes

I’m thinking that when you serve mycompany.io/setup that you’ll have to set

Access-Control-Allow-Origin: api.particle.io

or, to be more permissive, change api.particle.io to *.