From 2aa029bc408ac4179c2d02c02f0b116373716b73 Mon Sep 17 00:00:00 2001 From: lorol Date: Sat, 9 May 2020 16:55:57 -0400 Subject: [PATCH] Cookie Authentication including WS part taken from ayushsharma82 ideas https://github.com/me-no-dev/ESPAsyncWebServer/pull/684 For Websocket added: void handleHandshake(AwsHandshakeHandler handler) For EventSource added: void authorizeConnect(ArAuthorizeConnectHandler cb); Auth example and modifications. Tested on ESP8266 and ESP32 platforms See SmartSwitch.ino --- examples/SmartSwitch/README.md | 8 +- examples/SmartSwitch/SmartSwitch.ino | 161 ++++++++++++++---- examples/SmartSwitch/data/acefull.js.gz | Bin 119986 -> 119986 bytes .../SmartSwitch/data/login/favicon.ico.gz | Bin 0 -> 5806 bytes examples/SmartSwitch/data/login/index.htm | 20 +++ examples/SmartSwitch/data/worker-css.js.gz | Bin 35483 -> 35483 bytes examples/SmartSwitch/data/worker-html.js.gz | Bin 47406 -> 47406 bytes .../SmartSwitch/data/worker-javascript.js.gz | Bin 47729 -> 47729 bytes src/AsyncEventSource.cpp | 10 ++ src/AsyncEventSource.h | 3 + src/AsyncWebSocket.cpp | 11 ++ src/AsyncWebSocket.h | 7 + src/edit.htm.gz.h | 2 +- 13 files changed, 185 insertions(+), 37 deletions(-) create mode 100644 examples/SmartSwitch/data/login/favicon.ico.gz create mode 100644 examples/SmartSwitch/data/login/index.htm diff --git a/examples/SmartSwitch/README.md b/examples/SmartSwitch/README.md index fd8cd40..4ce4e9a 100644 --- a/examples/SmartSwitch/README.md +++ b/examples/SmartSwitch/README.md @@ -4,14 +4,16 @@ ## SmartSwitch * Remote Temperature Control application with schedule (example car block heater or battery charger) -* Based on ESP_AsyncFSBrowser example +* Based on ESP_AsyncFSBrowser example with ACE editor * Wide browser compatibility, no extra server-side needed * HTTP server and WebSocket, single port * Standalone, no JS dependencies for the browser from Internet (I hope), ace editor included * Added ESPAsyncWiFiManager -* Fallback to an own WIFI_AP, no Internet to sync but by a browser the clock can be set once * Real Time (NTP) w/ Time Zones * Memorized settings to EEPROM * Multiple clients can be connected at same time, they see each other' requests +* Base Authentication of the editor, static content, WS +* Or Cookie Authentication including WS part, need lib src changes taken from https://github.com/me-no-dev/ESPAsyncWebServer/pull/684 * Default credentials smart:switch -* Use latest ESP8266 or ESP32 core(from GitHub) +* Use latest ESP8266 ESP32 cores from GitHub + diff --git a/examples/SmartSwitch/SmartSwitch.ino b/examples/SmartSwitch/SmartSwitch.ino index 0307cd2..9609e9d 100644 --- a/examples/SmartSwitch/SmartSwitch.ino +++ b/examples/SmartSwitch/SmartSwitch.ino @@ -16,9 +16,21 @@ Use latest ESP core lib (from Github) #define USE_WFM // to use ESPAsyncWiFiManager //#define DEL_WFM // delete Wifi credentials stored //(use once then comment and flash again), also HTTP /erase-wifi can do the same live - -#define USE_AUTH_STAT // .setAuthentication also for static (editor always requires auth) -//#define USE_AUTH_WS // .setAuthentication also for ws, broken for Safari iOS + +// AUTH COOKIE uses only the password, Base uses both +#define http_username "smart" +#define http_password "switch" + +//See https://github.com/me-no-dev/ESPAsyncWebServer/pull/684 +#define USE_AUTH_COOKIE +#define MY_COOKIE_FULL "LLKQ=7;max-age=31536000;" +#define MY_COOKIE_DEL "LLKQ=" +#define MY_COOKIE "LLKQ=7" + +#ifndef USE_AUTH_COOKIE + #define USE_AUTH_STAT //Base Auth for stat, /commands and SPIFFSEditor + //#define USE_AUTH_WS //Base Auth also for WS, not very supported +#endif #include #ifdef ESP32 @@ -55,7 +67,10 @@ Use latest ESP core lib (from Github) // DHT #define DHTTYPE DHT22 // DHT 11 // DHT 22, AM2302, AM2321 // DHT 21, AM2301 -#define DHTPIN 4 //D2 +#define DHTPIN 4 //D2 + +#define DHT_T_CORR -0.5 //Temperature offset compensation of the sensor (can be -) +#define DHT_H_CORR 1.5 //Humidity offset compensation of the sensor DHT dht(DHTPIN, DHTTYPE); @@ -80,9 +95,7 @@ AsyncWebSocket ws("/ws"); const char* ssid = "MYROUTERSSD"; const char* password = "MYROUTERPASSWD"; #endif -const char* hostName = "smartsw"; -const char* http_username = "smart"; -const char* http_password = "switch"; + const char* hostName = "smartsw32"; // RTC static timeval tv; @@ -91,6 +104,15 @@ static time_t now; // HW I/O const int btnPin = 0; //D3 const int ledPin = 2; //D4 + +#ifdef ESP32 + #define LED_ON 0x1 + #define LED_OFF 0x0 +#elif defined(ESP8266) + #define LED_ON 0x0 + #define LED_OFF 0x1 +#endif + int btnState = HIGH; // Globals @@ -101,7 +123,7 @@ float t = 0; float h = 0; bool udht = false; bool heat_enabled_prev = false; -int ledState; +int ledState = LED_OFF; struct EE_bl { byte memid; //here goes the EEMARK stamp @@ -178,30 +200,32 @@ void showTime() } if (heat_enabled_prev) { // smart control (delayed one cycle) - if (((t - HYST) < ee.tempe)&&(ledState == HIGH)) { // OFF->ON once - ledState = LOW; + if (((t - HYST) < ee.tempe)&&(ledState == LED_OFF)) { // OFF->ON once + ledState = LED_ON; digitalWrite(ledPin, ledState); // apply change ws.textAll("led,ledon"); } - if ((((t + HYST) > ee.tempe)&&(ledState == LOW))||(!heat_enabled)) { // ON->OFF once, also turn off at end of period. - ledState = HIGH; + if ((((t + HYST) > ee.tempe)&&(ledState == LED_ON))||(!heat_enabled)) { // ON->OFF once, also turn off at end of period. + ledState = LED_OFF; digitalWrite(ledPin, ledState); // apply change ws.textAll("led,ledoff"); } - Serial.printf(ledState ? "LED OFF" : "LED ON"); + + Serial.printf(ledState == LED_ON ? "LED ON" : "LED OFF"); Serial.print(F(", Smart enabled\n")); } heat_enabled_prev = heat_enabled; //update } void updateDHT(){ - h = dht.readHumidity(); - t = dht.readTemperature(); //Celsius or dht.readTemperature(true) for Fahrenheit - if (isnan(h) || isnan(t)) { + float h1 = dht.readHumidity(); + float t1 = dht.readTemperature(); //Celsius or dht.readTemperature(true) for Fahrenheit + if (isnan(h1) || isnan(t1)) { Serial.print(F("Failed to read from DHT sensor!")); - h = 0; // debug w/o sensor - t = 0; - } + } else { + h = h1 + DHT_H_CORR; + t = t1 + DHT_T_CORR; + } } void analogSample() @@ -216,7 +240,7 @@ void checkPhysicalButton() if (btnState != LOW) { // btnState is used to avoid sequential toggles ledState = !ledState; digitalWrite(ledPin, ledState); - if (ledState) ws.textAll("led,ledoff"); + if (ledState == LED_OFF) ws.textAll("led,ledoff"); else ws.textAll("led,ledon"); } btnState = LOW; @@ -241,6 +265,16 @@ void mytimer() { } } +#ifdef USE_AUTH_COOKIE +bool myHandshake(AsyncWebServerRequest *request){ // false will 401 + if (request->hasHeader("Cookie")){ + String cookie = request->header("Cookie"); + if (cookie.indexOf(MY_COOKIE) != -1) return true; + else return false; + } else return false; +} +#endif + // server void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){ if(type == WS_EVT_CONNECT){ @@ -252,7 +286,7 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT Serial.printf("[%u] Connected from %d.%d.%d.%d\n", client->id(), ip[0], ip[1], ip[2], ip[3]); showTime(); analogSample(); - if (ledState) ws.textAll("led,ledoff"); + if (ledState == LED_OFF) ws.textAll("led,ledoff"); else ws.textAll("led,ledon"); ws.printfAll("Now,Setting,%02d:%02d,%02d:%02d,%+2.1f", ee.hstart, ee.mstart, ee.hstop, ee.mstop, ee.tempe); @@ -279,11 +313,11 @@ void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventT } if(data[0] == 'L') { // LED if(data[1] == '1') { - ledState = LOW; + ledState = LED_ON; ws.textAll("led,ledon"); // for others } else if(data[1] == '0') { - ledState = HIGH; + ledState = LED_OFF; ws.textAll("led,ledoff"); } digitalWrite(ledPin, ledState); // apply change @@ -444,31 +478,74 @@ void setup(){ #ifdef USE_AUTH_WS ws.setAuthentication(http_username,http_password); #endif + +#ifdef USE_AUTH_COOKIE + ws.handleHandshake(myHandshake); +#endif + ws.onEvent(onWsEvent); server.addHandler(&ws); #ifdef ESP32 + #ifdef USE_AUTH_STAT server.addHandler(new SPIFFSEditor(SPIFFS, http_username,http_password)); + #elif defined(USE_AUTH_COOKIE) + server.addHandler(new SPIFFSEditor(SPIFFS)).setFilter(myHandshake); + #endif #elif defined(ESP8266) + #ifdef USE_AUTH_STAT server.addHandler(new SPIFFSEditor(http_username,http_password)); + #elif defined(USE_AUTH_COOKIE) + server.addHandler(new SPIFFSEditor()).setFilter(myHandshake); + #endif #endif - - server.on("/free-ram", HTTP_GET, [](AsyncWebServerRequest *request){ // direct request->answer - request->send(200, "text/plain", String(ESP.getFreeHeap())); + +#ifdef USE_AUTH_COOKIE + server.on("/lg2n", HTTP_POST, [](AsyncWebServerRequest *request){ // cookie test + if((request->hasParam("pa2w",true) && (String(request->getParam("pa2w",true)->value().c_str()) == String(http_password)))||(request->hasParam("lg0f",true))){ + AsyncWebServerResponse *response = request->beginResponse(301); + response->addHeader("Location", "/"); + response->addHeader("Cache-Control", "no-cache"); + if(request->hasParam("lg0f",true)) response->addHeader("Set-Cookie", MY_COOKIE_DEL); + else response->addHeader("Set-Cookie", MY_COOKIE_FULL); + request->send(response); + } else request->send(200, "text/plain","Wrong Password!"); }); +#endif +// below paths need individual auth //////////////////////////////////////////////// - server.on("/get-time", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/free-ram", HTTP_GET, [](AsyncWebServerRequest *request){ // direct request->answer +#ifdef USE_AUTH_STAT + if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); +#endif + request->send(200, "text/plain", String(ESP.getFreeHeap())); +#ifdef USE_AUTH COOKIE + }).setFilter(myHandshake); +#else + }); +#endif + + server.on("/get-time", HTTP_GET, [](AsyncWebServerRequest *request){ +#ifdef USE_AUTH_STAT + if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); +#endif if(request->hasParam("btime")){ time_t rtc = (request->getParam("btime")->value()).toInt(); timeval tv = { rtc, 0 }; settimeofday(&tv, nullptr); } request->send(200, "text/plain","Got browser time ..."); +#ifdef USE_AUTH COOKIE + }).setFilter(myHandshake); +#else }); - +#endif server.on("/hw-reset", HTTP_GET, [](AsyncWebServerRequest *request){ +#ifdef USE_AUTH_STAT + if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); +#endif request->onDisconnect([]() { #ifdef ESP32 ESP.restart(); @@ -477,9 +554,16 @@ void setup(){ #endif }); request->send(200, "text/plain","Restarting ..."); +#ifdef USE_AUTH COOKIE + }).setFilter(myHandshake); +#else }); +#endif server.on("/erase-wifi", HTTP_GET, [](AsyncWebServerRequest *request){ +#ifdef USE_AUTH_STAT + if(!request->authenticate(http_username, http_password)) return request->requestAuthentication(); +#endif request->onDisconnect([]() { WiFi.disconnect(true); #ifdef ESP32 @@ -489,12 +573,23 @@ void setup(){ #endif }); request->send(200, "text/plain","Erasing WiFi data ..."); - }); - -#ifdef USE_AUTH_STAT - server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm").setAuthentication(http_username,http_password); +#ifdef USE_AUTH COOKIE + }).setFilter(myHandshake); #else - server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm"); + }); +#endif + +// above paths need individual auth //////////////////////////////////////////////// + +#ifdef USE_AUTH_COOKIE + server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm").setFilter(myHandshake); + server.serveStatic("/", SPIFFS, "/login/").setDefaultFile("index.htm").setFilter(!myHandshake); +#else + #ifdef USE_AUTH_STAT + server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm").setAuthentication(http_username,http_password); + #else + server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm"); + #endif #endif server.onNotFound([](AsyncWebServerRequest *request){ // nothing known diff --git a/examples/SmartSwitch/data/acefull.js.gz b/examples/SmartSwitch/data/acefull.js.gz index e08df275219565d93939df9642c0198e32044e3b..00cdd5f934f68062b7484b87eedd6b1d99bf0815 100644 GIT binary patch delta 21 dcmdnAl6})kc6Rx04vsA0?TzeP*%?<~0RU2g2Yvtm delta 21 dcmdnAl6})kc6Rx04i0ax4UOzu*%?<~0RU412af;% diff --git a/examples/SmartSwitch/data/login/favicon.ico.gz b/examples/SmartSwitch/data/login/favicon.ico.gz new file mode 100644 index 0000000000000000000000000000000000000000..9e605465c6e22500502ba34a11be2488bc508355 GIT binary patch literal 5806 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 + + + + Login + + + + + +
+

+

Password

+
+ +

+ +
+ + \ No newline at end of file diff --git a/examples/SmartSwitch/data/worker-css.js.gz b/examples/SmartSwitch/data/worker-css.js.gz index 6e826b6cc0669fdfe8c91ea7135fe25431513434..36849854f5e68ac7c3f494cc8f1af284f760dc17 100644 GIT binary patch delta 18 acmbO|m1*`=CU*I54vtLW?Hk#rbO8W4UaddInterestingHeader(F("Last-Event-ID")); + request->addInterestingHeader("Cookie"); return true; } @@ -340,6 +345,11 @@ void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){ if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) { return request->requestAuthentication(); } + if(_authorizeConnectHandler != NULL){ + if(!_authorizeConnectHandler(request)){ + return request->send(401); + } + } request->send(new AsyncEventSourceResponse(this)); } diff --git a/src/AsyncEventSource.h b/src/AsyncEventSource.h index b097fa6..a350e7f 100644 --- a/src/AsyncEventSource.h +++ b/src/AsyncEventSource.h @@ -49,6 +49,7 @@ class AsyncEventSource; class AsyncEventSourceResponse; class AsyncEventSourceClient; typedef std::function ArEventHandlerFunction; +typedef std::function ArAuthorizeConnectHandler; class AsyncEventSourceMessage { private: @@ -100,6 +101,7 @@ class AsyncEventSource: public AsyncWebHandler { String _url; LinkedList _clients; ArEventHandlerFunction _connectcb; + ArAuthorizeConnectHandler _authorizeConnectHandler; public: AsyncEventSource(const String& url); ~AsyncEventSource(); @@ -107,6 +109,7 @@ class AsyncEventSource: public AsyncWebHandler { const char * url() const { return _url.c_str(); } void close(); void onConnect(ArEventHandlerFunction cb); + void authorizeConnect(ArAuthorizeConnectHandler cb); void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0); size_t count() const; //number clinets connected size_t avgPacketsWaiting() const; diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp index 04e1a6f..9ce9385 100644 --- a/src/AsyncWebSocket.cpp +++ b/src/AsyncWebSocket.cpp @@ -1146,6 +1146,7 @@ void AsyncWebSocket::binaryAll(const __FlashStringHelper *message, size_t len){ const char __WS_STR_CONNECTION[] PROGMEM = { "Connection" }; const char __WS_STR_UPGRADE[] PROGMEM = { "Upgrade" }; const char __WS_STR_ORIGIN[] PROGMEM = { "Origin" }; +const char __WS_STR_COOKIE[] PROGMEM = { "Cookie" }; const char __WS_STR_VERSION[] PROGMEM = { "Sec-WebSocket-Version" }; const char __WS_STR_KEY[] PROGMEM = { "Sec-WebSocket-Key" }; const char __WS_STR_PROTOCOL[] PROGMEM = { "Sec-WebSocket-Protocol" }; @@ -1155,6 +1156,7 @@ const char __WS_STR_UUID[] PROGMEM = { "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" }; #define WS_STR_CONNECTION FPSTR(__WS_STR_CONNECTION) #define WS_STR_UPGRADE FPSTR(__WS_STR_UPGRADE) #define WS_STR_ORIGIN FPSTR(__WS_STR_ORIGIN) +#define WS_STR_COOKIE FPSTR(__WS_STR_COOKIE) #define WS_STR_VERSION FPSTR(__WS_STR_VERSION) #define WS_STR_KEY FPSTR(__WS_STR_KEY) #define WS_STR_PROTOCOL FPSTR(__WS_STR_PROTOCOL) @@ -1171,6 +1173,7 @@ bool AsyncWebSocket::canHandle(AsyncWebServerRequest *request){ request->addInterestingHeader(WS_STR_CONNECTION); request->addInterestingHeader(WS_STR_UPGRADE); request->addInterestingHeader(WS_STR_ORIGIN); + request->addInterestingHeader(WS_STR_COOKIE); request->addInterestingHeader(WS_STR_VERSION); request->addInterestingHeader(WS_STR_KEY); request->addInterestingHeader(WS_STR_PROTOCOL); @@ -1185,6 +1188,14 @@ void AsyncWebSocket::handleRequest(AsyncWebServerRequest *request){ if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())){ return request->requestAuthentication(); } +////////////////////////////////////////// + if(_handshakeHandler != nullptr){ + if(!_handshakeHandler(request)){ + request->send(401); + return; + } + } +////////////////////////////////////////// AsyncWebHeader* version = request->getHeader(WS_STR_VERSION); if(version->value().toInt() != 13){ AsyncWebServerResponse *response = request->beginResponse(400); diff --git a/src/AsyncWebSocket.h b/src/AsyncWebSocket.h index 5b03ace..f06af2c 100644 --- a/src/AsyncWebSocket.h +++ b/src/AsyncWebSocket.h @@ -237,6 +237,7 @@ class AsyncWebSocketClient { void _onData(void *pbuf, size_t plen); }; +typedef std::function AwsHandshakeHandler; typedef std::function AwsEventHandler; //WebServer Handler implementation that plays the role of a socket server @@ -248,6 +249,7 @@ class AsyncWebSocket: public AsyncWebHandler { AsyncWebSocketClientLinkedList _clients; uint32_t _cNextId; AwsEventHandler _eventHandler; + AwsHandshakeHandler _handshakeHandler; bool _enabled; AsyncWebLock _lock; @@ -316,6 +318,11 @@ class AsyncWebSocket: public AsyncWebHandler { _eventHandler = handler; } + // Handshake Handler + void handleHandshake(AwsHandshakeHandler handler){ + _handshakeHandler = handler; + } + //system callbacks (do not call) uint32_t _getNextId(){ return _cNextId++; } void _addClient(AsyncWebSocketClient * client); diff --git a/src/edit.htm.gz.h b/src/edit.htm.gz.h index c2d7f53..3f352e0 100644 --- a/src/edit.htm.gz.h +++ b/src/edit.htm.gz.h @@ -2,7 +2,7 @@ //File: edit.htm.gz, Size: 4408 #define edit_htm_gz_len 4408 const uint8_t edit_htm_gz[] PROGMEM = { -0x1F,0x8B,0x08,0x08,0x52,0x4A,0xB0,0x5E,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5, +0x1F,0x8B,0x08,0x08,0x42,0x13,0xB7,0x5E,0x02,0x00,0x65,0x64,0x69,0x74,0x2E,0x68,0x74,0x6D,0x00,0xB5, 0x1A,0x0B,0x5B,0xDB,0x36,0xF0,0xAF,0x18,0x6F,0x63,0xF6,0xE2,0x38,0x0E,0xA5,0xAC,0x73,0x30,0x2C,0x50, 0x56,0xFA,0x02,0x4A,0x42,0x3B,0xCA,0xD8,0x3E,0xC5,0x56,0x62,0x15,0x5B,0xF6,0x2C,0x99,0x40,0xB3,0xFC, 0xF7,0x9D,0x24,0x3F,0x43,0xE8,0x1E,0xDF,0xD6,0x6E,0x8D,0xA4,0xD3,0x9D,0xEE,0x4E,0xF7,0x54,0xB2,0xBB,