Tutorial for a simple web app

Hi everyone

I’ve been working on a web app for my product and a few have been asking for example code etc. so I thought I’d put up a quick tutorial on what I’ve done. I’ll mainly focus on the parts that I had difficulty with, otherwise it will be a novel! I just want to say however that I’m a hardware engineer, not a web developer so please don’t take any of this as gospel, it’s just what’s worked for me and it may not now, or in the future be recommended practise! Also the majority of this is work is adapted from awesome people like @bko, @mebrunet and many others, I’m mainly compiling it here for convenience.

The following is for a web app using ‘simple authentication’ and it’s usage of part cloud API and part JS SDK is because the JS SDK doesn’t currently offer all the features required of a full web app.

Part 1 - Creating a OAuth client ID

A client ID is used in this case for creating new customers for your organisation
Please read this first Authentication & Security

Creating your client ID only needs to be done once per app, make sure you have CURL installed, linux should by default:

curl -X POST -H "Authorization: Bearer myaccesstoken" -d name=myappname -d
type=web -d redirect_uri=https://myredirectaddress.com -d organization=myorgname -d scope=create_customer
https://api.particle.io/v1/clients

Most of the above is self explanatory, your redirect_uri is the address that the form will direct the user to once their account has been successfully created. The API will also include the users access token as a hash after your address e.g. https://myredirectpage.com/#usersNewAccessToken
Ideally this would be set to the address of your softAP page because most users will want to claim their new device after creating an account. The API requires a HTTPS address for this (probably because it passes the access token with the URI) meaning it can’t be the address of your softAP page (as far as I know the softAP page can’t currently have HTTPS because it needs to access the non-secure photon IP address).
What I’ve done for now is just have it redirect to my secure dashboard page and then I remove the hash. It shows a message showing that the account has been created and offers a link to the non secure softAP page. It’s all a bit of a roundabout way of doing this but I couldn’t think of another way just yet. Doesn’t seem much point in logging the user in straight after they have created an account as no devices will show up in their dashboard. I’d be interested to hear of better ways of doing this :smile:

Part 2 - HTML registration form

To create a user, you’ll need to make a POST via a form:

<form action="https://api.particle.io/v1/orgs/myorgname/customers" method="post">
    <input type="hidden" name="response_type" value="token" />
    <input type="hidden" name="client_id" value="myappname" />
    <div class="form-group">
        <input type="email" class="form-control" name="email" placeholder="Email">
    </div>
    <div class="form-group">
        <input type="password" class="form-control" name="password" placeholder="Password">
    </div>
    <input type="submit" class="btn btn-lg btn-primary btn-block" value="Register" id="registration-submit">
 </form>

All you need to do above is substitute “myappname” for the app name you chose in step 1 and substitute myorgname for your organisation name as setup in Particle. (Some of the above classes are bootstrap specific)

Part 3 - SoftAP Page

There’s a couple of options here, there’s a really nice page setup by @msolters which as far as I know will be there for a while http://photonsoftap.meteor.com (you could add this address to your redirect_uri, although it won’t handle claiming).

The option I chose was to use the great work from @mebrunet who has put a link up of a working page + JS library here: https://drive.google.com/file/d/0B4V2vkt-SuU9NHdhRmlfTWFkUG8/view
I modified his HTML a bit and added my own CSS and branding etc. + a few extra functions in the JS which I’ll explain later.

Part 4 - Claiming

I spent a bit of time trying to decide the best ‘work flow’ for my app. In the end I decided to keep my softAP page unsecured (I used a wildcard SSL so can chose which subdomains are HTTPS enabled). My login/dashboard page however has SSL enabled. What this means is that my softAP page redirects to my dashboard page with device ID appended to the address after WiFi setup and prompts the user to login, after which the device ID is used to complete the claiming process. This means that the device ID is the only piece of information that is transferred unsecurely, all login credentials are managed on the SSL enabled dashboard page. To do this I used this piece of JS added to the softAP JS library linked above (the redirect function is called via HTML when the user clicks a button after they have reconnected to their local network)

function redirect(){
   location.href='https://mydashboard.com/#'+deviceID.value;
}

The first thing that shows up on my dashboard page is a login section, I log the user in using the JS SDK:
(InputEmail and Password are ID’s for inputs on the HTML page)

var email=document.getElementById("InputEmail");
var password=document.getElementById("Password");
spark.login({username: email.value, password: password.value});

After this I use the following which is run after a successful login event:

spark.on('login', function(err, body) {
   console.log('API call completed on Login event:', body);
   console.log(err);

   if(window.location.hash) {
     var claimID = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
     var jqxhr = $.ajax({
     type: "POST",
     url: "https://api.particle.io/v1/devices",
     data: {
       access_token: body.accessToken,
       id: claimID
     }
    })
    .done(function() {
       alert('Your device was claimed successfully!');
    })
    .fail(function() {
       alert('There was a problem claiming your device.');
    });
    }

//Other code to show/hide various login related HTML elements etc. can go here
}

The important part of the above is the jquery ajax POST call to claim the device, it uses the access token returned from the ‘body’ object and the device ID extracted from the search bar (which was appended by the softap page in the last step).

I’ll post a few more things in the next post so this one doesn’t get too long :smile:

6 Likes

Part 5 - Reading Information from Device

I initially used the ‘device.getVariable’ function from the JS SDK, called every few seconds using a JS timer, however I eventually found this to be too unreliable. When the device went offline, requests would stack up with no response and the page didn’t seem to recover after the device came back online again. The online parameter from the device body can be used to stop getVariable calls if the device is offline but from my experience there was a delay of around 30 seconds or so before the Particle servers knew the device had gone offline. By that time there were enough getVariable calls stacked up to mess things up.

I’ve since been using SSE’s (Server Sent Events) and have had better success.
bko has a nice tutorial of this here: Using Spark.publish() with Simple JSON Data
It’s well explained so I won’t repost my code but it has made my web app more reliable.
One issue here is that IE11 doesn’t support SSE and as such will show an error in the console when it tries to create the EventSource. I’m still trying to decide whether I’ll add a third party event source library or just ask customers to install a decent browser (not sure how that will go down :confused:)

Part 6 - Removing a Device

For some reason I couldn’t find this in the docs for the Cloud API but @jvanier helped me out here. The following can be used to remove a device from the logged in user

function removeDevice(){
   var jqxhr = $.ajax({
   type: "DELETE",
   url: "https://api.particle.io/v1/devices/"+deviceID,
   data: {
       access_token: accessToken,
   }
   })
   .done(function() {
   alert('This device has been removed from your account');
   })
   .fail(function() {
    alert('There was a problem removing this device from your account.');
   });
}

Note, you’ll probably want to use something nicer than the standard JS alert, I’ve just included it here to show the basics of it. I use Toastr which does nice messages which time out meaning the user doesn’t have to click out of a popup http://codeseven.github.io/toastr/demo.html

7 Likes

Both claiming and removing is available in JS SDK, have you tried it ?

Is there a reason you ask customer to login after softAP process?
The token you get after implicit customer registration should be good after softAP if saved in cookie or session.

PS: thanks for doing all the work, your posts proven to be very valuable in my own quest.

Hi @sparkly

I have separate softAP and login/dashboard pages as it’s not possible (at least it wasn’t when I wrote this) to have a secure softAP setup page, as the IP connection to the photon isn’t secure. I’m not a web expert but you’re probably right, I’m sure it is possible to save the access token in the session and use it again to login. I haven’t bothered with this yet as softAP setup is usually only done once (or at least not often).

Yes, I too lost the fight with HTTP Strict Transport Security in browser and had to move SoftAP into a separate domain to keep it non https, but the token saved to cookies in secure page at redirect_uri location is available to scripts after customer emerges from SoftAP.

Did you learn the purpose of Generate a customer scoped to a customer for your organization? I suspect it’s for customer login but everybody seem to use the regular end point instead. In your tutorial you refer to “spark.login” which from what I understand is not organization specific.

@G65434_2, been some time since your post - but can you help me understand further your login page?

I am getting the below error. I am using the product name as the org name - since in console there is nothing specifically noted as my organization name.

I am trying one legged auth with the assigned Client ID and secret.

{
  "ok": false,
  "error": "Organization not found for user's role."
}

Would you be willing to share sample code, like what mebrunet did with the softap?

Hi @dancingpearl

Sorry for the delay, have you got this sorted yet? If not, I’ll have a look for you.

Thanks for the response, I think right now my issue is creating a “fake” customer and figuring out the organization name. From there I think I can finish the web app.

I tried creating a customer via the command line and that didin’t work nor was going to console.

How did you create a test customer? I have 3 photons that are part of my product line and have been successfully flashed. All I can see is that I can remove those devices and claim them under myself as an individual or reclaim them to the product. Nothing where I can make a photon belong to a created customer.

Hi again

I think part of your confusion (and mine for a start) was that Particle have recently changed the way 'organization' is used.
See here:

1 Like

I assume you have setup a client ID in the console right?
Under the authentication section under your product settings you should have created a client ID for your product, with ‘create customer’ scope and a redirect to the page you want the customer to be redirected to after you’ve created their account.

Thank you for the org information. I have updated my URL.

Correct, I created a client ID and redirect page.

Hopefully, with the correct URL, that is the only change I needed.

1 Like