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 | 155 ++++++++++++++---- 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, 182 insertions(+), 34 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 fd8cd4061..4ce4e9ab3 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 0307cd29a..9609e9d06 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 - + +#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("/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){ + 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 COOKIE + }).setFilter(myHandshake); +#else }); +#endif + +// above paths need individual auth //////////////////////////////////////////////// -#ifdef USE_AUTH_STAT - server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm").setAuthentication(http_username,http_password); +#ifdef USE_AUTH_COOKIE + server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm").setFilter(myHandshake); + server.serveStatic("/", SPIFFS, "/login/").setDefaultFile("index.htm").setFilter(!myHandshake); #else - server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.htm"); + #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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 literal 0 HcmV?d00001 diff --git a/examples/SmartSwitch/data/login/index.htm b/examples/SmartSwitch/data/login/index.htm new file mode 100644 index 000000000..c522ea4f1 --- /dev/null +++ b/examples/SmartSwitch/data/login/index.htm @@ -0,0 +1,20 @@ + + + + + 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 b097fa623..a350e7ff9 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 04e1a6f59..9ce9385fe 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 5b03aceb9..f06af2cf8 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 c2d7f53fc..3f352e00a 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,