Objetivo
El objetivo de este post es compartir un experimento que estamos realizando en Charrúa para enviar datos con nuestro dispositivo IoT (Internet of Things) a una aplicación realizada con Laravel.
En primer lugar debes estar familiarizado con Laravel y el protocolo OAuth2 que será el utilizado para realizar las solicitudes de manera autenticada a nuestra API. El dispositivo que utilizaremos para este experimento será un Arduino MKR1000
y el entorno Arduino Web Editor.
Por si no conoces el entorno Arduino Web Editor, es lo que se utiliza con los nuevos modelos como el MKR1000 para programar y enviar el código al dispositivo mediante un ordenador.
El tipo de credencial que utilizaremos para enviar datos desde el dispositivo será Client Credentials Grant Token
. Es un tipo de credencial que normalmente se utiliza para autenticar una máquina con otra máquina (machine-to-machine). En este caso una de las máquinas sera nuestro dispositivo MKR1000 y la otrá será nuestra API.
Comenzaremos por crear nuestra aplicación Laravel:
laravel new IOTAuth
Hemos escogido IOTAuth
para el nombre de nuestra aplicación, esto lo dejamos a tu criterio. Si tienes problemas para instalar Laravel puedes consultar la documentación: https://laravel.com/docs/5.8/installation#installation
¿NECESITAS AYUDA CON TU WEB?
Conecta la base de datos
Deberás ir al archivo .env
en la raíz de tu aplicación y agregar las credenciales de tu base de datos. Recuerda sustituir los valores <nombre_bd>
, <usuario_bd>
y <contraseña_bd>
por los que hayas creado.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=nombre_bd
DB_USERNAME=usuario_bd
DB_PASSWORD=contraseña_bd
Instalaremos el paquete de autenticación OAuth2 para API’s – Passport
Laravel Passport es un paquete utilizado para añadir fácilmente autenticación a nuestra API. Este paquete provee una implementación total de OAuth2.
Para realizar la instalación seguiremos los pasos de la documentación.
Utilizaremos composer para la instalación del paquete, con el comando:
composer require laravel/passport
Realizaremos la migración:
php artisan migrate
La migración generará las tablas necesarias para que nuestra aplicación Laravel quede operativa. Entre ellas creará las tablas de passport y la tabla de usuarios.
Generación de claves e instalación:
Tendrás que ejecutar un comando encargado de crear las llaves de encriptación necesarias para generar los tokens de acceso. Este comando también generará dos clientes uno del tipo personal access
y otro del tipo password grant
. Ignoraremos por el momento estas credenciales ya que ninguna nos interesa, recordemos la que queremos utilizar es para la comunicación de máquina a máquina (Client Credentials Grant Token).
php artisan passport:install
Generación del cliente para el tipo de credencial deseada: Client Credentials Grant Tokens
Antes de que nuestra aplicación pueda generar tokens, debemos generar el cliente. Pues bien, ahora debemos ejecutar el siguiente comando:
php artisan passport:client --client
Este comando nos preguntará el nombre para nuestro nuevo cliente, nosotros utilizamos mkr1000_01
y al confirmar el nombre, nos dará como resultado en la propia consola el ID del cliente y una clave secreta.
What should we name the client? [Access ClientCredentials Grant Client]:
> mkr1000_01
New client created successfully.
Client ID: 3
Client secret: qV524tNf2ofA6VsQCaqyBAPVayXCj0i4nIup1b
Desde la documentación de Laravel (https://laravel.com/docs/5.8/passport#client-credentials-grant-tokens) nos dicen que para que esto funcione correctamente, debemos agregar el CheckClientCredentials
middleware a $routeMiddleware
en app/Http/Kernel.php
.
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
protected $routeMiddleware = [
'client' => CheckClientCredentials::class,
];
Para proteger una ruta, agregaremos el middleware «client», lo verás en el siguiente punto.
Creación y protección de la ruta/s
Iremos a nuestro archivo api.php
, es el encargado de gestionar y resolver las rutas de nuestra API. El archivo podemos encontrarlo en routes/api.php
.
Vamos a crear nuestra ruta de prueba, agregamos al archivo api.php
:
Route::middleware('client')->get('/protected-route', function (Request $request) {
return "Este texto certifica que has ingresado correctamente";
}
Lo que sucederá aquí será que cuando intentemos ir a la URL http://127.0.0.1/api/protected-route
el sistema nos obligue a estar autenticados y podamos ver la frase configurada «Este texto certifica que has ingresado correctamente».
Probar la API localmente
Para probar las rutas utilizaremos Postman, un software utilizado para interactuar con API’s.
Iniciar nuestro servidor con el comando:
php artisan serve
Podemos probar que nuestra aplicación funciona correctamente escribiendo en el navegador http://127.0.0.1:8080
Solicitar token de acceso:
Luego, debemos solicitarle a nuestra API un token de acceso, y de esta manera acceder a los recursos de manera autenticada. Para ello ejecutamos Postman, y vamos a configurar un nuevo request:
Tal como dice la documentación de passport, debemos agregar ciertos parámetros a nuestro request. El ejemplo a continuación es para solicitar el token con PHP, pero veremos que campos son necesarios para esta solicitud.
$guzzle = new GuzzleHttp\Client;
$response = $guzzle->post('http://127.0.0.1:8080/oauth/token', [
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '*',
],
]);
return json_decode((string) $response->getBody(), true)['access_token'];
Antes que nada, en Postman, haremos una prueba de nuestra URL protegida, simplemente haremos una solicitud GET
a http://127.0.0.1:8080/api/protected-route
y veremos que el resultado será un error de búsqueda de ruta (no nos esta mostrando la frase que esperábamos, vamos por buen camino)
En postman, configuraremos un POST
request a la URL http://127.0.0.1:8080/oauth/token
, adjuntando los parametros de formulario necesarios (grant_type
, client_id
, client_secret
y scope
).
Para nuestro ejemplo los valores serían:
'grant_type' => 'client_credentials',
'client_id' => '3',
'client_secret' => 'qV524tNf5ofA6VsQCaqyBAPVayXCj0y4nIup1b',
'scope' => '*',
El resultado de esta solicitud es una respuesta en JSON
desde nuestra API con el token de acceso, el tipo de token y el tiempo de vida o validez.
Un ejemplo de la respuesta podría ser:
{"token_type":"Bearer","expires_in":31622400,"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjVhOWIzZjk5MTk4NWViOTRmZTFkZDk5NTZiNmU1NjI4MjhlMTI5MTkyOTAxMGI5ZjdiYTBjNDQ1ODk2OTVkOGIwYTVjMjA5NTBhMDhiMTljIn0.eyJhdWQiOiIzIiwianRpIjoiNWEasd32324r32fqeaf5NGZlMWRkOTk1NmI2ZTU2MjgyOGUxMjkxOTI5MDEwYjlmN2JhMGM0NDU4OTY5NWQ4YjBhNWMyMDk1MGEwOGIxOWMiLCJpYXQiOjE1NTkzNzMzNTAsIm5iZiI6MTU1OTM3MzM1MCwiZXhwIjoxNTkwOTk1NzUwLCJzdWIiOiIiLCJzY29wZXMiOlsiKiJdfQ.erCdhJQP4pn-f28YA4mlZNUQca4B1JQTBO7T8AddqfkFJL_de-zFc_yyKnZoNioi6kqYiV8FkYg4QUtKT096d7ypB0UF_ii4ews15J95a_-asdasddG1tfw4F_98QaroKMH0YsPCma-KoR3St2Sz7Awu-tCLG_PGbpgGL_NsSCxeioFnwq8tKOASj7wTSnwaErUbSRQaf6MHGxLvqRrCsu_xoKHQyJHyYdt4Mwoh-z9d3ECKyEZjAQnPb9oCgZzHYFevtYq120llExdpxJqndtxt0CEh6y_tUBL3SlRwrsCOlcVBa83_YEiSL5PFonZ93X2o6fcTi__2VfhqJES2d1eh7YCBr8RfBxk_ll6HeFoafNZxqv9VZVLPmmcpcggg9dhKJtVXG7Tejib0mXMQFuUd3F065f29RYCGM6UuTyc4qgsrASUmLrHDH9QdlfItIqQGbjfI1U0I1_4CY9bUQll8asloqxpwtqSr6OP1q1s-P1Z65hGHWW79gPypnV1QFz31QTPhNG3db7R5XfE0v_dGNK_uPpqKsL3W5FC41cM6aBpeiOP8z1gyK697MTIHwXJTDDFvaXf65kOhbODKptDV6_1Wx2o1sKTWysqQbGMMGJnB2na613wCRd2ckL4pmrd6vwMdwyw0_Ds1ula5kl-QoQ9N8s1B7P0X1Jic"}
En este punto hemos hecho la solicitud mediante POST
y tenemos en nuestro poder el token deseado para realizar la solicitud a nuestra ruta protegida.
Acceder a la ruta protegida:
Crearemos una nueva solicitud en Postman, esta vez una del tipo GET
a la ruta protegida: http://127.0.0.1:8080/api/protected-route
y agregaremos una cabecera (header) Autorization
con el valor Bearer <aquí el token>
.
Si has seguido todos los pasos anteriores, al ejecutar esta solicitud deberías ver la frase deseada «Este texto certifica que has ingresado correctamente».
Cargar código a nuestro dispositivo MKR1000
Para esta sección vamos a utilizar el Arduino MKR1000, la librería WiFi101
y subiremos el código mediante el Arduino Web Editor
.
Arduino MKR1000 – https://store.arduino.cc/arduino-mkr1000
WiFi101 – https://www.arduino.cc/en/Reference/WiFi101
Arduino Web Editor – https://create.arduino.cc
Hemos modificado levemente el ejemplo de la librería llamado WiFiWebClient.ino
:
#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "myNetwork"; // your network SSID (name)
char pass[] = "myPassword"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
const char server[] = "example.com"; // name address for Google (using DNS)
const String api_route = "/api/protected-route"; // our api route
// get this from the api auth flow
const String access_token = "your-token-here";
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
WiFiClient client;
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWiFiStatus();
Serial.println("\nStarting connection to server...");
// if you get a connection, report back via serial:
if (client.connect(server, 80)) {
Serial.println("connected to server");
// Make a HTTP request:
client.println("GET " + api_route + " HTTP/1.1");
client.println("Host: " + String(server));
client.println("Authorization: Bearer " + access_token);
client.println("Connection: close");
client.println();
}
}
void loop() {
// if there are incoming bytes available
// from the server, read them and print them:
while (client.available()) {
char c = client.read();
Serial.write(c);
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting from server.");
client.stop();
// do nothing forevermore:
while (true);
}
}
void printWiFiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Puedes ver el gist aquí: https://gist.github.com/danielcharrua/b022bef64e906acd5a897c5b2915d3fa
Deberás modificar algunas lineas para que esto te funcione correctamente:
Línea 4 y 5 – completa con el nombre y la contraseña de tu red WiFi.
Línea 10 – completa con el dominio de tu API.
Línea 11 – completa con la ruta protegida a la que queremos acceder.
Línea 14 – completa con el token de acceso previamente solicitado con Postman.
Probar API y Arduino MKR en producción
Llegados a este punto, llevaremos nuestra aplicación Laravel a un servidor público. Esto lo debemos hacer ya que el Arduino ejecutará un cliente web, imaginemos como un dispositivo que se conecta a internet y se comunicará con nuestra API.
Sube tu aplicación Laravel:
Sube los archivos a un servidor, para seguir el ejemplo supongamos que subes tu aplicación Laravel a example.com. Instala los paquetes de composer, conecta la base de datos, corre las migraciones, instala Passport, genera las claves y genera el cliente nuevo (el mismo proceso que has hecho localmente).
composer install
php artisan migrate
php artisan passport:install
php artisan passport:client --client
Si introduces la URL en tu navegador deberías ver la pantalla inicial de Laravel, eso es buena señal, significa que nuestra aplicación está funcionando correctamente.
Prueba la API con Postman:
Hagamos una breve prueba con Postman para asegurar que nuestra API está funcionando correctamente. Solicita un token como lo hemos hecho en el paso 5.b.
Una vez tengas el nuevo token de acceso, lo vamos a cargar en nuestro código para el Arduino MKR1000.
Prueba la comunicación desde el Arduino con la API:
Hemos llegado al último paso, subir el código al Arduino y ver el resultado por el puerto serial del mismo. Debemos ver entre las líneas de respuesta de nuestro request, la frase objetivo que introdujimos en la ruta protegida.
Attempting to connect to SSID: myNetwork
Connected to wifi
SSID: myNetwork
IP Address: 192.168.2.92
signal strength (RSSI):-82 dBm
Starting connection to server...
connected to server
HTTP/1.1 200 OK
Date: Wed, 05 Jun 2019 11:08:00 GMT
Content-Type: text/html; charset=UTF-8
52
Este texto certifica que has ingresado correctamente
0
disconnecting from server.
Conclusión
Hemos creado una comunicación desde un Arduino MKR1000 de manera inalámbrica (WiFi) y autenticada con nuestra API en una aplicación Laravel.
Con esto podríamos enviar datos de manera segura y autenticada desde sensores directamente a nuestra aplicación Laravel.
Posibles ideas para mejoras futuras:
– Realizar las solicitudes con el protocolo https:// en vez de con http://
– Realizar el proceso completo desde el Arduino (solicitar token y utilizar el mismo en los requests).
Si tienes comentarios o ideas, puedes comentarlos debajo del artículo.
HAGAMOS DESPEGAR TU NEGOCIO ONLINE 🚀
Te ayudo a potenciar tu página web o tienda online con estos servicios.