Integrating Homegraph api to my smarthome - actions-on-google

I am currently creating a smarthome automation. I am using Laravel PHP in my cloud. I am done with Oauth2 Authentication and sending commands from Google Home App to my devices (I used MQTT to communicate with my devices).
I have already implemented successfully SYNC, QUERY and EXECUTE. and now i want to synchronize the rooms and structures for which i have to use the Homegraph API. Is there somebody who can guide me in intergrating the Homegraph APi to my Smarthome because i am facing problem here in JWT authentication? My app is based on laravel so i am not able to follow the sample codes. I tried like this:
$jsonFile = json_decode(file_get_contents('./key.json'), true);
$client = new \GuzzleHttp\Client();
$payload = [
"iss" => $jsonFile['client_email'],
"scope" => "https://www.googleapis.com/auth/homegraph",
"aud" => "https://accounts.google.com/o/oauth2/token",
"iat" => date("h:i:sa"),
"exp" => date("h:i:sa", strtotime('+1 hour'))
];
$jwt = JWT::encode($payload, $jsonFile["private_key"], 'RS256');
$header = array("Authorization" => "Bearer " . $jwt, "Content-Type" => "application/json");
try {$response = $client->get( "https://accounts.google.com/o/oauth2/token", [
"headers" => $header]);
} catch (\Exception $e) {
dd($e);
}
I am getting an error of
Client error: `GET https://accounts.google.com/o/oauth2/token` resulted in a `404 Not Found` response:
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, w (truncated...)
i am following this http post link

Using Google APIs Client Library for PHP, should allow you to integrate directly w/ the Homegraph API from a PHP based Smart Home fulfilment without having to handcraft HTTP request and authorization.
It can leverages Google Application Default credentials to authorize the application using a service account key:
putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json');
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope("https://www.googleapis.com/auth/homegraph");
It includes generated bindings for the Homegraph API:
$homegraphService = new Google_Service_HomeGraphService($client);
$homegraphService->devices->reportStateAndNotification(...);
And generated types for all Homegraph API request and response payload, for example implementing Report State would look like this:
$request = new Google_Service_HomeGraphService_ReportStateAndNotificationRequest();
$request->setAgentUserId("placeholder-user-id");
$request->setRequestId("placeholder-request-id");
$payload = new Google_Service_HomeGraphService_StateAndNotificationPayload();
$devices = new Google_Service_HomeGraphService_ReportStateAndNotificationDevice();
$devices->setStates(array(
"placeholder-device-id" => array(
"placeholder-state-key" => ...
)
));
$payload->setDevices($devices);
$request->setPayload($payload);
$homegraphService->devices->reportStateAndNotification($request));

Related

Facebook webhook verification response structure

This is my first time posting the question so please feel to provide feedback to improve the question.
Facebook webhook mentions that the endpoint should be first verified before the webhook endpoint can receive any event notifications.
The docs for Verification Request does not provide a response structure for the API. It simply tells us to send back the hub.challenge parameter.
As I am using NodeJS, I am trying with the code below. However, it does not verify the webhook from facebook dashboard.
How should we send back the response to the verify the webhook?
app.get('/webhook', (req, res) => {
const challenge = req.query['hub.challenge'];
const verify_token = req.query['hub.verify_token'];
if (verify_token === process.env.FACEBOOK_VERIFICATION_TOKEN) {
return res.status(200).send({message: "Success", challenge: challenge});
}
return res.status(400).send({message: "Bad request!"});
})
The verification endpoint of Facebook requires the response Content-Type to be text/html. This is not mentioned on the docs;they should have provided a structure. You can set the header to use text/html explicitly.
However, when you are using express, you can directly return just the challenge value.
app.get('/webhook', (req, res) => {
const challenge = req.query['hub.challenge'];
const verify_token = req.query['hub.verify_token'];
if (verify_token === process.env.FACEBOOK_VERIFICATION_TOKEN) {
return res.status(200).send(challenge); // Just the challenge
}
return res.status(400).send({message: "Bad request!"});
})
If you are using fastify set:
res.header('Content-Type', 'text/html; charset=utf-8');
return res.send('' + challenge);

Bearer Tokens in C++Builder/FMX REST Functionality?

I have a server application running in node.js/Mongoose/MongoDB with a REST interface.
My client application is built in Embarcadero C++Builder/Firemonkey(FMX) and so far all is good with interacting with the node server using the embarcadero REST features (TRESTClient/TRESTRequest/TRESTResponse).
I recently added authentication to my server using JSON Web tokens and the user registration/login is working successfully, giving me back a bearer token using the following code:
const token = jwt.sign({sub: user.id}, process.env.JWT_SECRET, {expiresIn: '30d' })
Accessing data is implemented via express-jwt by sending a REST request with the bearer token. Postman makes it easy to send a request for data using a Bearer token (https://learning.postman.com/docs/sending-requests/authorization/#bearer-token), however I cannot find out how to do this seemingly simple task using Embarcadero's REST features.
I have tried using the Embarcadero REST OAUTH/OAUTH2/SIMPLE/BASIC authentication methods with the bearer token in the Access-Token and Request-Token fields and nothing seems to work.
How can this be done? I am sure this is something simple I am missing but there is next to no documentation I can find.
I figured out an answer for anyone else who is having trouble using authentication in C++Builder with REST:
Design-time method:
--> Setup TRESTClient, TRESTRequest, TRESTResponse
--> In TRESTRequest Params, create a new param with fields:
Name: Authorization, Value: Bearer XXXXXXXX (JWT String), Options: poDoNotEncode (this is the important part
Creating the REST client for authorization at runtime:
// initialize REST client
TRESTClient* pRESTClient = new TRESTClient(BASE_URL);
pRESTClient->ContentType = "application/json";
// connect REST request for querying server
TRESTRequest* pRESTRequest = new TRESTRequest(NULL);
pRESTRequest->Client = pRESTClient;
// connect REST response for receiving JSON from server
TRESTResponse* pRESTResponse = new TRESTResponse(NULL);
pRESTRequest->Response = pRESTResponse;
pRESTResponse->ContentType = "text/html";
// do authenticated query
pRESTRequest->Method = rmGET;
pRESTRequest->Resource = ROUTE_ITEMS;
pRESTRequest->ResourceSuffix = SUBROUTE_ITEMSUFFIX;
pRESTRequest->Params->Clear();
TRESTRequestParameter* param = pRESTRequest->Params->AddItem();
param->Name = "Authorization";
param->ContentType = ctNone;
param->Kind = pkHTTPHEADER;
param->Options << poDoNotEncode;
char temp[512];
sprintf(temp, "Bearer %s", JWT_TOKEN);
param->Value = (const char*)temp;
pRESTRequest->Execute();
The server response is then added to the TRESTResponse->Content field as JSON.
As a note, it is important to have the server configured with express-JWT (https://www.npmjs.com/package/express-jwt) for this to work properly with the following code managing the server (node.js):
app.use(jwt({
secret: process.env.JWT_SECRET,
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));

Slim 3 how to save JWT Token on local storage and use it in my routes for authentication

I want to implement jwt authentication for slim app, i followed tuupora's PRS7 jwt authentication middleware and its working fine when i use Postman because there are options to use header as "Authorization: Bearer tokenString" as here bellow when i request "/auth/ibice" route
these returned data are protected by the middleware-- screenshot
and am using the token string that returned when i request this route "/authtoken" as you see it bellow
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3d3cuYXNpZC5ydyIsImlhdCI6MTQ4Njk5MjcyNCwiZXhwIjoxNDg4Mjg4NzI0LCJjb250ZXh0Ijp7InVzZXIiOnsicGhvbmVubyI6IjA3ODQyMjY4OTUiLCJ1c2VyX2lkIjoiMSJ9fX0.1kFu4A16xxJriaRA9CccIJ3M9Bup06buK2LAh13Lzy4",
"user_id": "1"
}
this my middleware.php that protect all routes of "/auth/"
<?php
// Application middleware
$container["jwt"] = function ($container) {
return new StdClass;
};
$app->add(new \Slim\Middleware\JwtAuthentication([
"environment" => "HTTP_X_TOKEN",
"header" => "Authorization",
"path" => ["/auth"],
"passthrough" => ["/authtoken"],
"secret" => "your_secret_key",
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response->withStatus(401)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
},
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
}
]));
and my routes that i want to request with authorization header that is stored either from cookie or local storage but i have no idea how to do that!!
$app->group('/auth',function(){
$this->get('/admin','App\Controllers\apiController:login')->setName('admin');
//fetch ibice
$this->get('/ibice','App\Controllers\apiController:ibice')->setName('Ibice');
//fetch ibice by id
$this->get('/igice/{id}', 'App\Controllers\apiController:igice')->setName('igiceId');
//search ibice
$this->get('/igice/search/[{query}]', 'App\Controllers\apiController:igice_search')->setName('Igice Search');
//imitwe igize igice
$this->get('/igice/{id}/imitwe','App\Controllers\apiController:imitwe')->setName('Imitwe');
//ingingo ziherereye mumutwe runaka
$this->get('/umutwe/{id}/ingingo', 'App\Controllers\apiController:ingingoBundle')->setName('Ingingo.bundle');
//ingingo ziri mucyiciro runaka
$this->get('/ingingo/icyiciro/{id}', 'App\Controllers\apiController:allstuff')->setName('Icyiciro');
//kuzana ikibazo kimwe kiri mungingo runaka
$this->get('/ingingo/{ingingoid}/question/{id}', 'App\Controllers\apiController:question')->setName('One_Exercise');
//kuzana ibibazo byose biri mungingo
$this->get('/ingingo/{ingingoid}/questions', 'App\Controllers\apiController:questions')->setName('One_Exercise');
//check if the answer is True or False
$this->get('/question/{id}/check/[{query}]','App\Controllers\apiController:checkQuestions')->setName('Check_Questions');
//get questions ids from ingingo
$this->get('/question/{ingingoid}','App\Controllers\apiController:questionsIDs')->setName('Check_Questions');
});
please help me i have no idea how to do this !!
I have never used Slim before but Maybe You can use little Javascript to access localstorage bcz you can't access local storage with php (php works on server side) while localstorage is in browser(client side) here there steps you can do first get Auth token with php by hitting this /authtoken endpoint $app->get('/authtoken') then you need to json_decode returned json into php array then if suppose your php array containing token is $arr then you can you little javascript to save that token in localstorage likes this <script>localStorage.setItem('token', '<?php echo $arr['token'];?>');</script> then whenever you want to read it also you can use javascript to read it from localstorage
<?php
$token = "<script>document.write(localStorage.getItem('token'));</script>"; ?>

Facebook Auth with AngularJS and Django REST Framework

I am developing a SPA application with AngularJS which uses Django backend for the server. The way that I communicate with the server from the SPA is with django-rest-framework. So now I want to make authentication with facebook (google and twitter too) and I read a lot on this topic and found OAuth.io which is making the authetication on the client SPA side and python-social-auth which is doing the same thing but on the server side.
So currently I have only the client auth, my app is connecting to facebook (with OAuth.io) and login successfully. This process is returning access_token and then I am making a request to my API which have to login this user or create account for this user by given token and this part is not working. So I am not sure where I am wrong, maybe because there isn't a full tutorial about using python-social-auth so maybe I am missing something or.. I don't know..
So some code of this what I have:
On the SPA side: This is the connection with OAuth.io and is working because I am getting the access token. Then I have to make a request to my rest API. backend is 'facebook', 'google' or 'twitter'
OAuth.initialize('my-auth-code-for-oauthio');
OAuth.popup(backend, function(error, result) {
//handle error with error
//use result.access_token in your API request
var token = 'Token ' + result.access_token;
var loginPromise = $http({
method:'POST',
url: 'api-token/login/' + backend + '/',
headers: {'Authorization': token}});
loginPromise.success(function () {
console.log('Succeess');
});
loginPromise.error(function (result) {
console.log('error');
});
});
On the server in my settings.py I have added social plugin to the installed apps, template context preprocessors, some auth backends and that is my file:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
...,
'rest_framework',
'rest_framework.authtoken',
'api',
'social.apps.django_app.default',
'social'
)
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.core.context_processors.request",
"django.contrib.messages.context_processors.messages",
'social.apps.django_app.context_processors.backends',
'social.apps.django_app.context_processors.login_redirect',)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
SOCIAL_AUTH_FACEBOOK_KEY = 'key'
SOCIAL_AUTH_FACEBOOK_SECRET = 'secret'
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
AUTHENTICATION_BACKENDS = (
'social.backends.open_id.OpenIdAuth',
'social.backends.facebook.FacebookOAuth2',
'social.backends.facebook.FacebookAppOAuth',
'social.backends.google.GoogleOpenId',
'social.backends.google.GoogleOAuth2',
'social.backends.google.GoogleOAuth',
'social.backends.twitter.TwitterOAuth',
'django.contrib.auth.backends.ModelBackend',
)
In my views.py of the API I have the following (I found it here):
from django.contrib.auth.models import User, Group
from rest_framework import viewsets, generics
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions, parsers, renderers
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.decorators import api_view, throttle_classes
from social.apps.django_app.utils import strategy
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
model = Token
# Accept backend as a parameter and 'auth' for a login / pass
def post(self, request, backend):
serializer = self.serializer_class(data=request.DATA)
if backend == 'auth':
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'])
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
# Here we call PSA to authenticate like we would if we used PSA on server side.
user = register_by_access_token(request, backend)
# If user is active we get or create the REST token and send it back with user data
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id , 'name': user.username, 'userRole': 'user','token': token.key})
#strategy()
def register_by_access_token(request, backend):
backend = request.strategy.backend
user = request.user
user = backend._do_auth(
access_token=request.GET.get('access_token'),
user=user.is_authenticated() and user or None
)
return user
And finally I have these routes in urls.py:
...
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token'),
url(r'^api-token/login/(?P<backend>[^/]+)/$', views.ObtainAuthToken.as_view()),
url(r'^register/(?P<backend>[^/]+)/', views.register_by_access_token),
...
Everytime when I try to do auth, OAuth.io is working and the rqest to api returns
detail: "Invalid token"
I think that I missed something in the configuration of python-social-auth or I am doing everything wrong. So I will be glad if anyone has some ideas and want to help :)
Add the following line to your ObtainAuthToken class
authentication_classes = ()
and your error {"detail": "Invalid token"} will go away.
Here's why...
Your request contains the following header
Authorization: Token yourAccessToken
yet you have defined rest_framework.authentication.TokenAuthentication in DEFAULT_AUTHENTICATION_CLASSES.
Based on this Django thinks you want to perform token authentication as you have passed a Token in. It fails because this is an access token for facebook and doesn't exist in your django *_token database, hence the invalid token error. In your case all you need to do is tell Django not to use TokenAuthentication for this view.
FYI
Keep in mind you may encounter further errors as your code execution was halted before the post method of ObtainAuthToken executed. Personally when trying to step through your code I got the error
'DjangoStrategy' object has no attribute 'backend'
on
backend = request.strategy.backend
and resolved it by changing to
uri = ''
strategy = load_strategy(request)
backend = load_backend(strategy, backend, uri)
Additionally you should update your you register_by_access_token function as it doesn't line up with the working code from the blog you referenced. The blog author posted his latest code here. Your version doesn't pull the token out of the auth header which is required if you want to use it to auth with a third party like facebook.
Yea. Solved. The settings are not right and you need to add permissions.
REST_FRAMEWORK = {
# Use hyperlinked styles by default.
# Only used if the `serializer_class` attribute is not set on a view.
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.HyperlinkedModelSerializer',
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
and some info about pipeline:
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.social_auth.associate_by_email',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
I'm using tools just like you, but I provide my login/register/.... with
django-allauth package, and then use django-rest-auth for API handling.
You just need follow the installation instruction, then use them for your rest APIs.
Adding allauth and rest-auth to your INSTALLED_APPS:
INSTALLED_APPS = (
...,
'rest_framework',
'rest_framework.authtoken',
'rest_auth'
...,
'allauth',
'allauth.account',
'rest_auth.registration',
...,
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
)
Then add your custom urls:
urlpatterns = patterns('',
...,
(r'^auth/', include('rest_auth.urls')),
(r'^auth/registration/', include('rest_auth.registration.urls'))
)
Finally, add this line:
TEMPLATE_CONTEXT_PROCESSORS = (
...,
'allauth.account.context_processors.account',
'allauth.socialaccount.context_processors.socialaccount',
...
)
These two packages works like a charm, and you don't need to have concern about any type of login.registration, because allauth package handles both django model login and oAuth login.
I hope it helps

Download file from google cloud storage using ruby client gets HTTP redirect

I am trying to download a file from GCS using the Ruby client and Service Account authorization; here is my code:
def build_client
client = Google::APIClient.new()
key = Google::APIClient::PKCS12.load_key(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'notasecret')
service_account = Google::APIClient::JWTAsserter.new(
SERVICE_ACCOUNT_EMAIL,
'https://www.googleapis.com/auth/devstorage.full_control',
key)
client.authorization = service_account.authorize
client
end
client = build_client
storage = client.discovered_api('storage', 'v1beta2')
#Get (download) a specific object from a bucket
bucket_get_result = client.execute(
api_method: storage.objects.get,
parameters: {bucket: BUCKET, object: params[:file_name], alt: 'media'}
)
puts bucket_get_result.body
The result body is a redirect detail as follows:
<HTML>
<HEAD>
<TITLE>Temporary Redirect</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Temporary Redirect</H1>
The document has moved here.
</BODY>
</HTML>
...and then if I try to go to the redirect 'location' I get an Unauthorized HTTP. So, how can I download a file from GCS using the ruby client? https://developers.google.com/storage/docs/json_api/v1/objects/get says:
If you provide the URL parameter alt=media, then it will respond with the object data in the response body
The Google Ruby API client does not currently support downloading media.
However, you could always do the rest of the work yourself.
require 'httparty'
get_result = bucket_get_result = client.execute(
api_method: storage.objects.get,
parameters: {bucket: BUCKET, object: params[:file_name], :alt=>'media'})
url = get_result.response.env[:response_headers]['location']
token = "Bearer #{bucket_get_result.request.authorization.access_token}"
puts HTTParty.get(url, :headers => {"Authorization" => token}).body