Hardware components | ||||||
![]() |
| × | 1 |
- 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.
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 FlowStep 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)
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.
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
- Google: https://console.developers.google.com/. Credentials are Client ID and Client Secret
- Facebook: https://developers.facebook.com/apps. Credentials are APP ID and Client Token
Step 2: Implement code for devices
see code in code part
<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.258,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,272.691z"/></svg>
</div>
</div>
<span class="sign-in-button-text">Device Login with Facebook</span>
</div>
</div>
<br><br>
</div>
</div>
</body>
</html>
<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>
<?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", "xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.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;
}
?>
Comments
Please log in or sign up to comment.