Basic HTTP authentication webserver with buttons

Thanks. Looking forward to seeing it!

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>



const char* ssid = "SSID";
const char* password = "PASSWORD";

//USE IP ADDRESS 

ESP8266WebServer server(80);
WiFiClient client;

//Timer for email loop
long timer = millis();

String safety = "ACTIVE";
String door = "CLOSED";

unsigned long interval = 80000;    // the time we need to wait
unsigned long previousMillis = 0;

//website triggers for I/O
void handletrigger();
void handlesafety();

//Check if header is present and correct
bool is_authentified() {
  Serial.println("Enter is_authentified");
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
    if (cookie.indexOf("ESPSESSIONID=1") != -1) {
      Serial.println("Authentification Successful");
      return true;
    }
  }
  Serial.println("Authentification Failed");
  return false;
}

//login page, also called for disconnect
void handleLogin() {
  String msg;
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
  }
  if (server.hasArg("DISCONNECT")) {
    Serial.println("Disconnection");
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
    server.send(301);
    return;
  }
  if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
    if (server.arg("USERNAME") == "USER" &&  server.arg("PASSWORD") == "PASSWORD" ) {
      server.sendHeader("Location", "/");
      server.sendHeader("Cache-Control", "no-cache");
      server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
      server.send(301);
      Serial.println("Log in Successful");
      return;
    }
    //Incorrect login area
    msg = "Wrong username/password..... NO ACCESS FOR YOU!!";
    
    Serial.println("Log in Failed");
  }

  //Login screen
  String content ="<!DOCTYPE html><html>";
  content += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  content += "<html><body><p><form action='/login' method='POST'><p>";
  content += "User:<input type='text' name='USERNAME' placeholder='Username'>
";
  content += "Password:<input type='password' name='PASSWORD' placeholder='Password'>
";
  content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "
";
  content += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}";
  //SAVE THIS FOR FUTURE.. content += "You also can go <a href='/inline'>here</a></body></html>";
  server.send(200, "text/html", content);
}

//root page can be accessed only if authentification is ok, MAIN PAGE
void handleRoot() {
  Serial.println("Enter handleRoot");
  String header;
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  String content = "<!DOCTYPE html><html>";
   content += "<meta http-equiv=refresh content= 2;/>";
  content += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  content += "<link rel=\"icon\" href=\"data:,\">";
  content += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}";
  content += ".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;";
  content += "text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}";
  content += ".button2 {background-color: #77878A;}</style></head>";
  content += "<body><h1></h1>";
  if (server.hasHeader("User-Agent"))  
 content += "<html><body><p></p>";
  content += "<html><body><strong><h1><p>GARAGE DOOR OPENER</strong></h1></p>";
  content += "<hr />";
  content += "<p>Door is - " + door + "</p>";
  content += "<html><body><p>TOGGLE DOOR SAFETY TO OPEN DOOR</p>";
  content += "<p>Door safety is - " + safety + "</p>";
  content += "<hr />";
  content += "<a><a href='/safety' target='inlineframe'><button class=\"button button2\">DOOR SAFETY</button></a>";
content += "
";

  content += "<a><a href='/trigger' target='inlineframe'><button class=\"button button2\">OPEN / CLOSE</button></a>";
  content += "<hr />";
    
  content += "<IFRAME name=inlineframe style='display:none'>";
  content += "</IFRAME>";
  content += "<p>&nbsp;</p>";
 
  content += "Please <strong><a href=\"/login?DISCONNECT=YES\">DISCONNECT</strong></a></body></html> when finished to prevent accidental triggering!";
  server.send(200, "text/html", content);
  content += "<p>&nbsp;</p>";
  content += "<p>&nbsp;</p>";
 
  
  server.send(200, "text/html", content);
  content += "<p>&nbsp;</p>";
  content += "<p>&nbsp;</p>";
}

//no need authentification
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

//I/O TRIGGERS VIA webpage 
void handletrigger() {
  String header;
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  if (safety == "OFF"){
  Serial.println("door relay trigger request");
  digitalWrite(5, HIGH);
  Serial.println("High");
  delay(1000);
  digitalWrite(5, LOW);
  Serial.println("Low");
  
  
 
}
}
void handlesafety() {
Serial.println("door safety request");
safety = "OFF";
  
}
void setup(void) {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  // Initialize the output variables as outputs
  pinMode(12, INPUT_PULLUP); //garage door sensor
  pinMode(5, OUTPUT);// garage door relay 
  digitalWrite(5, LOW);

safety = "ACTIVE";
door = "CLOSED";
  


  //EACH TRIGGER NEEDS THIS TO BE ADDED
  server.on("/", handleRoot);
  server.on("/trigger", handletrigger);
  server.on("/safety", handlesafety);
  server.on("/login", handleLogin);
  server.on("/inline", []() {
    server.send(200, "text/plain", "nothing here....");
  });


  server.onNotFound(handleNotFound);
  //here the list of headers to be recorded
  const char * headerkeys[] = {"User-Agent", "Cookie"} ;
  size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
  //ask server to track these headers
  server.collectHeaders(headerkeys, headerkeyssize );
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void)
//below to trigger email server
{
  server.handleClient();
  unsigned long currentMillis = millis();
  if (digitalRead (12) == LOW){
    door = "OPEN";}
  else {
  door = "CLOSED";}



  if (safety == "OFF") {
    if ((unsigned long)(currentMillis - previousMillis) >= interval) {
      Serial.println ("1 min has passed");
      safety = "ACTIVE";
      Serial.println ("door safety now active");
      previousMillis = currentMillis;

   }
  }
}

Hi, Slvrsky
Thanks. It compiles with no problem at all.
Then, I made minor modifications so I can use it on the relay module with ESP-01. It works flawlessly.
Thank you very much for sharing.
Excellent work!

Relay module link: https://www.aliexpress.com/item/ESP8266-ESP-01S-ESP01-S-5V-WiFi-Relay-Module-ESP-01S-ESP01S-Things-Smart-Home-Remote/32887538479.html?spm=a2g0s.9042311.0.0.6d954c4dR5wiPa

Awesome. I also have one that sends an email when the door sensor is triggered too. Let me know if you run into anything else, I might have made one already.

Let me finish this one first. I will let you know if I need anything more Thank you for your offer though.
Regards!

Hi, Slvrsky:
As the garage door opener is working, I am interested in adding the email notification feature as you said last time. Can you share your code? Thanks.

For some reason I couldn't find the garage door one I made and I didn't feel like writing a new one so I attached the one for my smoke detector system. It should have what you need to do what you need to do. It's attached as a text file. I found using Gmail as your mail server doesn't work too well. Ones with less authentication work well.

smokeemail.txt (10.2 KB)

Thanks. I will take a look and let you know if I have any question.
One thing I am always confused is how the "server.handleClient()" works. I guess it is reading the data sent over the wifi then determine what to do, but not sure. Do you know where I can find references that explain its function?

I am not sure how that part works. My coding knowledge is pretty limited.

But you still made it work. Much better than me :grin: .

Hi, Slvrsky:
Eventually, I made the door open/close emailing work. I use the stmp2go as the mail server, which one do you use?
Thank you for your help.

Awesome. I use the business email server that came with my website.

Hi, Slvrsky:
I tried to add automatic logout after certain time in case I forgot to disconnect. I detected the time the server log in and tried to disconnect after a certain amount of time has passed. I added the following code at the end of the loop section:

unsigned long now = millis();
unsigned long time_since_login = now - login_time;
if ((logged_in)&&(time_since_login>=60000)) { // one minute
Serial.println ("1 min has passed since logged in.");
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
server.send(301);

logged_in = 0;
}

where login_time is the time mark set immediately after login. The "1 min has passed since logged in." statement was printed out so the timer worked fine. However, the disconnection didn't happen. Do you know why was that and how to make it work?
Thanks.

Can you post your whole code?

O.K. Here you go.

opener-auth-new_test-1.txt (8.13 KB)

I’ll try messing with it tomorrow or Thursday. I made one that will change the cookie in the void loop but I haven’t got the timer part working completely yet.

That's fine. Please take your time.
I bought the book/video course "". Their way to handle the login is quite interesting. Basically, every new login will need to enter username/password. If you are interested, I will find out if I can send you the code, to see if you can integrate it into your garage opener code.

Hi, Slvrsky:
I got a reply from the book author. He said it is fine for sharing my code based on his example. The code is attached. It works but the webpage will not reflash when the door switch state changes like in your code. I like its way to handle login, i.e., every new login will need to enter username and password. I hope you can find a way to integrate his login method with you code.

ppws_esp8266-1t.ino (5.8 KB)

Okay, I think I got your original code working correctly. The issue I think was the order of operations on the the disconnect bit. It also needs 1 min power up time before millis works correctly.

Add this to the global area:

String logged_in = "1";

Add the logged_in string to the handleLogin():

if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
if (server.arg("USERNAME") == "1" && server.arg("PASSWORD") == "1" ) {
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
server.send(301);
Serial.println("Log in Successful");
logged_in = "1";
return;

Lastly add this to the Void Loop:

if (((unsigned long)(currentMillis - previousMillis) >= 60000)&&(logged_in = "1")) {
server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
server.sendHeader("Location", "/login");
server.sendHeader("Cache-Control", "no-cache");
server.send(301);
logged_in = "0";
previousMillis = currentMillis;
}

The code you last posted that you got from that book has some good parts of it, I like how they added bootstrap in it. Otherwise its doing the same thing as my original code. I have made other webserver ESPs that are setup the same putting everything in the Void loop like they did. It works better for somethings doing it that way vs the way I sent you. I use both regularly.