Click image for larger version  Name:	Capture.PNG Views:	1 Size:	211.9 KB ID:	1488
Purposes of Devices Login to Google and Facebook
  • get access token to use Google and Facebook API (e.g Livestream, Facbook Post...)
  • devices can also send access token Cloud. Cloud receives access token to identify owner of devices.


Demonstration





Hardware Components

PHPoC Blue or PHPoC Black or P4M-400



OAuth 2.0 for Devices


The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a user. It is specified in RFC-6749.

Recently, IETF has added an OAuth 2.0 extension draft, which allows IoT devices to login to the service provider. Google and Facebook has supported this extension.

Google Document: OAuth 2.0 for TV and Limited-Input Device Applications

Facebook Document: Facebook Login for Devices




Protocol Flow

Step 1: Request device and user codes

Step 2: Handle the authorization server response

The return value from authorization server will be used in the next steps

Step 3: Display the user_code and verification_url on a display component (e.g LCD, monitor, LED..)

Step 4: User Login:
  • open web browser on any device
  • access verification_url
  • input user_code
  • login to their account

Step 5: Poll authorization server

Device keeps polling authorization server until user logins

After user logins, authorization server will return access token and refresh token to devices

Step 6: Use token to call API ( in my code, get User Profile)






Remove the Need of Display Component by using PHPoC


As described in step 3 and 4 of Protocol Flow, we need:
  • Display component (such as LCD, Monitor...) to show authorization code and verification url to user
  • Web browser to login.

With PHPoC, we need to use ONLY web browser, helping reduce the cost. Authorization code and verification url is also send from device to web browser and shows to user.

This also bring more convenient by replacing manually input verification URL on web browser by clicking on a link.How To


Note that, we need to distinguish between your project account and user account.One project that you creates on developer account can allows a large number users to login. In development mode, the number of users is limited. Facebook allows only one users in development mode.

Step 1:Create a project on developer console and get credentials
Step 2: Implement code for devices

<index.php> - Home Page
PHP Code:
<html>
<head>
<title>PHPoC / <?echo system("uname -i")?></title>
<meta content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=0.5, width=device-width, user-scalable=yes" name="viewport">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" type="text/css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
body {font-family: Arial, Helvetica, sans-serif;}
.body-main {
    text-align: center;
    margin: auto;
    overflow:auto;
}
.center {
    margin: auto;
    position: absolute;
    -webkit-backface-visibility: hidden;
    left:0;
    right:0;
    text-align: center;
    top: 40%; transform: translateY(-50%);
}
.hearder {
    width: 100%;
    max-width:400px;
    color: #008B8B;
    padding: 5px;
    border-bottom: solid;
    margin-bottom: 5px;
    font-weight: bold;
    font-size: 120%;
    display: inline-block;
}
.sign-in-wrapper {
    -webkit-border-radius: 1px;
    border-radius: 1px;
    border: none;
    -webkit-box-shadow 0 2px 4px 0px rgba(0,0,0,.25): ;
    box-shadow: 0 2px 4px 0 rgba(0,0,0,.25);
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    -webkit-transition: background-color .218s,border-color .218s,box-shadow .218s;
    transition: background-color .218s,border-color .218s,box-shadow .218s;
    -webkit-user-select: none;
    -webkit-appearance: none;
    cursor: pointer;
    outline: none;
    overflow: hidden;
    position: relative;
    text-align: center;
    vertical-align: middle;
    white-space: nowrap;
    height:50px;
    width:300px;
    display: inline-block;
}

.sign-in-wrapper.google:hover {box-shadow: 0 0 15px rgba(66, 133, 244, 1);}
.sign-in-wrapper.facebook:hover {box-shadow: 0 0 15px rgba(59, 89, 152, 1);}
.sign-in-wrapper.google {
    background-color: #4285f4;
    color: #fff;
}
.sign-in-wrapper.facebook {
    background-color: #3B5998;
    color: #fff;
}
.sign-in-button-content-wrapper {
    border: 1px solid transparent;
    text-align: left;
    height: 100%;
    width: 100%;
}
.sign-in-button-icon {
    background-color: #fff;
    -webkit-border-radius: 1px;
    border-radius: 1px;
    float: left;
    padding:15px
}
.sign-in-button-image {
    width:18px;
    height:18px;
}
.sign-in-button-text {
    font-family: Roboto,arial,sans-serif;
    font-weight: 500;
    font-size:16px;
    letter-spacing: .21px;
    line-height:48px;
    margin-left: 6px;
    margin-right: 6px;
    vertical-align: top;
    padding-left: 16px;
}
</style>

</head>
<body>
<div class="body-main">
<div class="center">
<div class="hearder">Google, Facebook<br>Login for IoT Devices</div>
<br><br>

<div class="sign-in-wrapper google" onclick="window.location='http://<?echo _SERVER("HTTP_HOST")?>/login.php?provider=google';">
    <div class="sign-in-button-content-wrapper">
        <div class="sign-in-button-icon">
            <div class="sign-in-button-image">
            <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 48 48" <g><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"></path><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"></path><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"></path><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"></path><path fill="none" d="M0 0h48v48H0z"></path></g></svg>
            </div>
        </div>
        <span class="sign-in-button-text">Device Login with Google</span>
    </div>
</div>
<br><br>

<div class="sign-in-wrapper facebook" onclick="window.location='http://<?echo _SERVER("HTTP_HOST")?>/login.php?provider=facebook';">
    <div class="sign-in-button-content-wrapper">
        <div class="sign-in-button-icon">
            <div class="sign-in-button-image">
            <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"><path style="fill:#385C8E;" d="M134.941,272.691h56.123v231.051c0,4.562,3.696,8.25  8,8.258,8.258h95.159 c4.562,0,8.258-3.696,8.258-8.258V273.78h64.519c4.195,0,7.725-3.148,8.204-7.315l9.799-85.061c0.269-2.34-0.472-4.684-2.038-6.44 c-1.567-1.757-3.81-2.763-6.164-2.763h-74.316V118.88c0-16.073,8.654-24.224,25.726-24.224c2.433,0,48.59,0,48.59,0 c4.562,0,8.258-3.698,8.258-8.258V8.319c0-4.562-3.696-8.258-8.258-8.258h-66.965C309.622,0.038,308.573,0,307.027,0 c-11.619,0-52.006,2.281-83.909,31.63c-35.348,32.524-30.434,71.465-29.26,78.217v62.352h-58.918c-4.562,0-8.258,3.696-8.258,8.258 v83.975C126.683,268.993,130.379,272.691,134.941,27  2.691z"/></svg>
            </div>
        </div>
        <span class="sign-in-button-text">Device Login with Facebook</span>
    </div>
</div>
<br><br>

</div>
</div>
</body>
</html>

<login.php> - Display User Code, Verification URL, and User Profile after login
PHP Code:
<html>
<head>
<title>PHPoC / <?echo system("uname -i")?></title>
<meta content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=0.5, width=device-width, user-scalable=yes" name="viewport">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" type="text/css">
<style>
body { text-align:center; font-family: Graphik LCG Web, Graphik Arabic Web Regular, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, Lucida Grande, Sans-Serif;}
.center {
    margin: auto;
    position: absolute;
    -webkit-backface-visibility: hidden;
    left:0;
    right:0;
    text-align: center;
    top: 30%;
}
.hearder {
    width: 100%;
    max-width:400px;
    color: #008B8B;
    padding: 5px;
    border-bottom: solid;
    margin-bottom: 5px;
    font-weight: bold;
    font-size: 120%;
    display: inline-block;
}
.wc_text, .loader {
    display: inline-block;
    width: 100%;
    max-width:300px;
    line-height: 150%;
}
.code {
    font-family: "Courier New", Courier, monospace;
    font-size: 150%;
    font-weight: bold;
    color: #A52A2A;
}
.success {font-weight: bold; color: #A52A2A;}

/*loading icon*/
.lds-roller {
  display: inline-block;
  position: relative;
  width: 64px;
  height: 64px;
}
.lds-roller div {
  animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
  transform-origin: 32px 32px;
}
.lds-roller div:after {
  content: " ";
  display: block;
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #A52A2A;
  margin: -3px 0 0 -3px;
}
.lds-roller div:nth-child(1) {animation-delay: -0.036s;}
.lds-roller div:nth-child(1):after {top: 50px;left: 50px;}
.lds-roller div:nth-child(2) {animation-delay: -0.072s;}
.lds-roller div:nth-child(2):after {top: 54px;left: 45px;}
.lds-roller div:nth-child(3) {animation-delay: -0.108s;}
.lds-roller div:nth-child(3):after {top: 57px;left: 39px;}
.lds-roller div:nth-child(4) {animation-delay: -0.144s;}
.lds-roller div:nth-child(4):after {top: 58px;left: 32px;}
.lds-roller div:nth-child(5) {animation-delay: -0.18s;}
.lds-roller div:nth-child(5):after {top: 57px;left: 25px;}
.lds-roller div:nth-child(6) {animation-delay: -0.216s;}
.lds-roller div:nth-child(6):after {top: 54px;left: 19px;}
.lds-roller div:nth-child(7) {animation-delay: -0.252s;}
.lds-roller div:nth-child(7):after {top: 50px;left: 14px;}
.lds-roller div:nth-child(8) {animation-delay: -0.288s;}
.lds-roller div:nth-child(8):after {top: 45px;left: 10px;}
@keyframes lds-roller {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}
</style>
<script>
var ws;
var provider = '<?echo _GET("provider")?>';

function init()
{
    ws = new WebSocket("ws://<?echo _SERVER("HTTP_HOST")?>/login", "text.phpoc");

    ws.onopen = ws_onopen;
    ws.onclose = ws_onclose;
    ws.onmessage = ws_onmessage;
}

function ws_onopen()
{
    switch(provider)
    {
        case 'google':
        case 'facebook':
        case 'naver':
            if(ws && (ws.readyState == 1))
                ws.send(provider + '\r\n');

            break;
    }
}
function ws_onclose()
{
    alert('CANNOT connect to device. Please reload webpage');
    ws.onopen = null;
    ws.onclose = null;
    ws.onmessage = null;
    ws = null;
}

function ws_onmessage(e_msg)
{
    e_msg = e_msg || window.event; // MessageEvent

    var obj = JSON.parse(e_msg.data);
    var wc_text = document.getElementById('wc_text');

    if(obj.action == 'LOGIN')
    {
        wc_text.innerHTML  = 'Next, visit <a href="' + obj.verification_url + '" target="_blank">' + obj.verification_url + '</a> and enter this code:<br>';
        wc_text.innerHTML += '<span class="code">' + obj.user_code + '</span>';
    }
    else
    if(obj.action == 'SUCCESS')
    {
        document.getElementById('loader').style.display = 'none';
        wc_text.innerHTML  = '<span class="success">Success!</span><br>';
        wc_text.innerHTML += obj.name + ' (' + obj.email + '), ';
        wc_text.innerHTML += 'You are now logged in from PHPoC Device';

    }
}

window.onload = init;
</script>

</head>
<body>
<div class="center">
    <div class="hearder">Google, Facebook<br>Login for IoT Devices</div>
    <br><br>
    <div class="wc_text" id="wc_text"></div><br><br>
    <div class="loader" id="loader">
        <div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
    </div>
</div>

</body>
</html>

<task0.php> - Perform making request to Authorization Server
PHP Code:
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; 
// avoid php execution via http request

include_once "/lib/sn_tcp_ws.php";
include_once 
"/lib/sn_http_b2.php";
include_once 
"/lib/sn_json_b1.php";

define("GOOGLE_CLIENT_ID",     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps  .googleusercontent.com");
define("GOOGLE_CLIENT_SECRET""xxxxxxxxxxxxxxxxxxxxxxxx");
define("FACEBOOK_APP_ID""xxxxxxxxxxxxxxx");
define("FACEBOOK_CLIENT_TOKEN""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

function 
http($method$url$body)
{
    
http_req_header("Accept: */*");
    
http_req_header("Content-Type: application/x-www-form-urlencoded");

    
$resp_head http_request($method$url$body);

    
$resp_body "";

    if(
$resp_head !== "")
        
$rlen http_read_sync($resp_body);

    
http_close();

    
//echo "HTTP RESPONSE HEADER:\r\n$resp_head";
    //echo "HTTP RESPONSE BODY:\r\n$resp_body\r\n\r\n\r\n";

    
return $resp_body;
}

ws_setup(1"login""text.phpoc");

$is_logged_in false;
$rbuf "";

while(
1)
{
    if(
ws_state(1) == TCP_CONNECTED)
    {
        
$rlen ws_read_line(1$rbuf);

        if(
$rlen)
        {
            if(!
$is_logged_in)
            {
                
$provider rtrim($rbuf"\r\n");

                
// Step 1: Request device and user codes
                
if($provider == "google")
                {
                    
$url "https://accounts.google.com/o/oauth2/device/code";
                    
$body  "client_id=" GOOGLE_CLIENT_ID;
                    
$body .= "&scope=email";
                }
                else
                if(
$provider == "facebook")
                {
                    
$url "https://graph.facebook.com/v2.6/device/login";
                    
$body  "access_token="FACEBOOK_APP_ID "|" FACEBOOK_CLIENT_TOKEN;
                    
$body .= "&scope=email";
                }
                else
                    continue;

                
$response http("POST"$url$body);

                if(
$response)
                {
                    
// Step 2: Handle the authorization server response
                    
if($provider == "google")
                    {
                        
$device_code      json_text_value(json_search($response"device_code"));;
                        
$user_code        json_text_value(json_search($response"user_code"));
                        
$verification_url json_text_value(json_search($response"verification_url"));
                        
$expires_in       json_text_value(json_search($response"expires_in"));
                        
$interval         json_text_value(json_search($response"interval"));
                    }
                    else
                    if(
$provider == "facebook")
                    {
                        
$device_code      json_text_value(json_search($response"code"));;
                        
$user_code        json_text_value(json_search($response"user_code"));
                        
$verification_url json_text_value(json_search($response"verification_uri"));
                        
$expires_in       json_text_value(json_search($response"expires_in"));
                        
$interval         json_text_value(json_search($response"interval"));
                    }

                    
// Step 3: Display the user code
                    
if($provider == "google")
                        
$msg '{"provider": "google",';
                    else
                    if(
$provider == "facebook")
                        
$msg '{"provider": "facebook",';

                    
$msg .= '"action": "LOGIN",';
                    
$msg .= '"verification_url": "' $verification_url '",';
                    
$msg .= '"user_code": "' $user_code '"';
                    
$msg .= '}';

                    
ws_write(1$msg);
                    echo 
"Next, visit $verification_url on your desktop or smartphone and enter this code: $user_code\r\n";

                    
// Step 5: Poll authorization server
                    
if($provider == "google")
                    {
                        
$url "https://www.googleapis.com/oauth2/v4/token";
                        
$body  "client_id=" GOOGLE_CLIENT_ID;
                        
$body .= "&client_secret=" GOOGLE_CLIENT_SECRET;
                        
$body .= "&code=$device_code";
                        
$body .= "&grant_type=http://oauth.net/grant_type/device/1.0";
                    }
                    else
                    if(
$provider == "facebook")
                    {
                        
$url "https://graph.facebook.com/v2.6/device/login_status";
                        
$body  "access_token="FACEBOOK_APP_ID "|" FACEBOOK_CLIENT_TOKEN;
                        
$body .= "&code=$device_code";
                    }

                    
$loop_count = (int)$expires_in / (int)$interval;

                    for(
$i 0$i <= $loop_count$i++)
                    {
                        
//if(ws_state(1) != TCP_CONNECTED)
                            //break;

                        
sleep((int)$interval);

                        if((
$response http("POST"$url$body)))
                        {
                            if(
$provider == "google")
                            {
                                
$access_token json_text_value(json_search($response"access_token"));
                                
$id_token     json_text_value(json_search($response"id_token"));

                                if(
$access_token)
                                {
                                    
// Step 6: get User Profile
                                    /*
                                    NOTE: for Google, We don't need to make new request to get User Profile from server
                                    because the User Profile has been included in id_token
                                    */

                                    // echo $id_token;
                                    
$is_logged_in true;

                                    
$jwt explode("."$id_token);
                                    
$payload $jwt[1];
                                    
$payload_json system("base64 dec %1 url"$payload);

                                    
$oauth_uid json_text_value(json_search($payload_json"sub"));
                                    
$name      json_text_value(json_search($payload_json"name"));
                                    
$email     json_text_value(json_search($payload_json"email"));
                                }
                            }
                            else
                            if(
$provider == "facebook")
                            {
                                
$access_token json_text_value(json_search($response"access_token"));

                                
//echo $response, "\r\n";

                                
if($access_token)
                                {
                                    
// Step 6: get User Profile
                                    
$url  'https://graph.facebook.com/v3.2/me';
                                    
$url .= '?fields=id,name,email,picture';
                                    
$url .= "&access_token=$access_token";

                                    if((
$response http("GET"$url"")))
                                    {
                                        
$is_logged_in true;

                                        
$oauth_uid json_text_value(json_search($response"id"));
                                        
$name      json_text_value(json_search($response"name"));
                                        
$email     json_text_value(json_search($response"email"));
                                    }
                                }
                            }

                            if(
$is_logged_in)
                            {
                                
// Step 7: Confirm Successful Login
                                // Step 3: Display the user code
                                
if($provider == "google")
                                    
$msg '{"provider": "google",';
                                else
                                if(
$provider == "facebook")
                                    
$msg '{"provider": "facebook",';

                                
$msg .= '"action": "SUCCESS",';
                                
$msg .= '"name": "' $name '",';
                                
$msg .= '"email": "' $email '"';
                                
$msg .= '}';
                                
ws_write(1$msg);

                                echo 
"provider:",  $provider,  "\r\n";
                                echo 
"oauth_uid:"$oauth_uid"\r\n";
                                echo 
"name:",      $name,      "\r\n";
                                echo 
"email:",     $email,     "\r\n";

                                
// IMPLEMENT YOUR APPLICATION HERE
                                // NOTE: $access_token should be stored in FLASH MEMORY to handle the case of device reset

                                
break;
                            }
                        }
                    }
                }
            }
        }
    }
    else
        
$is_logged_in false;
}
?>