phpoc_man
Published © GPL3+

IoT Devices - Login to Google and Facebook via OAuth 2.0

After logging in, device get access token to use Google & Facebook API to livestream, add calendar. It also is owner's identifier on cloud.

IntermediateFull instructions provided250
IoT Devices - Login to Google and Facebook via OAuth 2.0

Things used in this project

Hardware components

PHPoC Module P4M-400
PHPoC Module P4M-400
PHPoC Blue or PHPoC Black can be used instead of P4M. If using P4M, we need to use along with P4M Evaluation Board
×1

Story

Read more

Code

index.php

PHP
home page
<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>

login.php

PHP
Display User Code, Verification URL, and User Profile after login
<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

PHP
Perform making request to Authorization Server
<?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;
}
?>

Credits

phpoc_man

phpoc_man

40 projects • 218 followers

Comments