Basic HTTP authentication webserver with buttons

Hello, I am new to programming. I have been searching for an example to make a basic authentication web server like the one I attached below. The issue I am running into is how to modify the authenticated page to add a toggle push button to trigger GPIO2 output. Can someone help me get started on where I would start modifying the script to get those changes made?

Thanks!

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

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

ESP8266WebServer server(80);

//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") == "admin" && server.arg("PASSWORD") == "admin") {
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache");
server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
server.send(301);
Serial.println("Log in Successful");
return;
}
msg = "Wrong username/password! try again.";
Serial.println("Log in Failed");
}
String content = "To log in, please use : admin/admin
";
content += "User:
";
content += "Password:
";
content += "" + msg + "
";

server.send(200, "text/html", content);
}

//root page can be accessed only if authentification is ok
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 = "

hello, you successfully connected to esp8266!


";
if (server.hasHeader("User-Agent")) {
content += "the user agent used is : " + server.header("User-Agent") + "

";
}
content += "You can access this page until you <a href="/login?DISCONNECT=YES">disconnect";
server.send(200, "text/html", content);
}

//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);
}

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());

server.on("/", handleRoot);
server.on("/login", handleLogin);
server.on("/inline", {
server.send(200, "text/plain", "this works without need of authentification");
});

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) {
server.handleClient();
}

First: Use code tags for all code! (that's the </> button in the editor)

I have been searching for an example to make a basic authentication web server like the one I attached below.

The posted code does not implement basic authentication but a simple form of cookie-based session management.

The issue I am running into is how to modify the authenticated page to add a toggle push button to trigger GPIO2 output.

Add a form with a button to the HTML code sent in the handleRoot() routine and add a location that handles the form action. If that button is the only thing you'll ever add, make the button a link and simply add a location to handle it. I don't understand what "trigger" exactly means in above sentence. Is that just a short HIGH state on the output or a toggling of the state or something completely different?

Trigger would mean to turn that output HIGH

slvrsky07:
Trigger would mean to turn that output HIGH

And how do you get it LOW again? Or is your application a one time shot?

I guess I was going to play around with the code to get it to get the button to momentarily trigger high when the button was pressed for about 2 seconds then return to low.

If there's an easier way than having the cookie type authentication I would be open to that.

Also here is the code attached the way I believe you wanted it attached.

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

const char* ssid = "C02J2LZFDHJF";
const char* password = "";

ESP8266WebServer server(80);

//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") == "admin" &&  server.arg("PASSWORD") == "admin") {
      server.sendHeader("Location", "/");
      server.sendHeader("Cache-Control", "no-cache");
      server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
      server.send(301);
      Serial.println("Log in Successful");
      return;
    }
    msg = "Wrong username/password! try again.";
    Serial.println("Log in Failed");
  }
  String content = "<html><body><form action='/login' method='POST'>To log in, please use : admin/admin
";
  content += "User:<input type='text' name='USERNAME' placeholder='user name'>
";
  content += "Password:<input type='password' name='PASSWORD' placeholder='password'>
";
  content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "
";
  
  server.send(200, "text/html", content);
}

//root page can be accessed only if authentification is ok
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 = "<html><body><H2>hello, you successfully connected to esp8266!</H2>
";
  if (server.hasHeader("User-Agent")) {
    content += "the user agent used is : " + server.header("User-Agent") + "

";
  }
  content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
  server.send(200, "text/html", content);
}

//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);
}

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());


  server.on("/", handleRoot);
  server.on("/login", handleLogin);
  server.on("/inline", []() {
    server.send(200, "text/plain", "this works without need of authentification");
  });

  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) {
  server.handleClient();
}

Also here is the code attached the way I believe you wanted it attached.

You didn't added the changes I suggested. handleRoot() is still exactly the same as in the first post. It just sends a heading line which is more or less a hello world. You also didn't add the other location (that's the lines with calls to server.on()).

Just in case you expected us to do your job: You cannot post an example sketch and let us do all the programming needed for your project. We help you to get on the right path but we're not the free programming service.

If the above description of what you should do is to complicated for you, you should start with easier tasks to get the basics you need for your project. So if you don't know the basics of HTML ask Google for tutorials about that, there are thousands of them. Learn what a URL is, what parts it has and how they are used on the server. Once you got that knowledge you'll understand what I wrote above and what changes in code it needs.

pylon:
First: Use code tags for all code! (that's the </> button in the editor)

I was attaching it the way you suggested in your 1st post. And no I haven't got around to trying to make the changes yet. Also NO, I not expect you to write code for me as you somewhat rudely applied "Just in case you expected us to do your job: You cannot post an example sketch and let us do all the programming needed for your project. We help you to get on the right path but we're not the free programming service."

I just simply asked where I needed to add the html part. Thanks for your previous answers.

I just simply asked where I needed to add the html part.

I already wrote that you should add the HTML code in the handleRoot() routine. I apologize if my words were to rude for you. We find more often than we like posters that get some example code from a website and expect us to finish the application to their desires.

I'll wait to see your changes to the above code in the direction I drafted. If you get stuck post the code you have and all the output it produces, we'll find a way to guide you through.

So long story short, something happened to my account here so I created a new one. Yesterday I had some time to research and work on my code. I did a little bit of a change on my coding but it is working correctly for what I need it to do. Thanks for your help.

#include <SPI.h>
#include <ESP8266WiFi.h>
const char* ssid     = "xxxxxx";
const char* password = "xxxxxx";
IPAddress ip(192, 168, 2, 2);



// (port 80 is default for HTTP):
WiFiServer server(80);
//char userPass[200];
//char header[500];
String header;
//int bufferSize = 0;
String output5State = "off";



// Assign output variables to GPIO pins
const int output5 = 5;


void setup() {
Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(5, OUTPUT);
  
  
  // Set outputs to LOW
  digitalWrite(output5, LOW);
  

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {

  // listen for incoming clients
  WiFiClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        header += c;
  
        if (c == '\n' && currentLineIsBlank) {

          //parse headers
          //YWRtaW46cGFzc3dvcmQ= = 'admin:password' (user:password) base64 encode
    
          Serial.print(header);
          
          // Simpler just to find the credential string
          // send a standard http response header
          if(header.indexOf("YWRtaW46cGFzc3dvcmQ=") >= 0) {
            //successful login
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: close");  // the connection will be closed after completion of the response
            //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
            client.println();
           
              if(header.indexOf("GET / HTTP/1.1") >= 0) {
                 client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #77878A;}</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>Garage Door Opener</h1>");
            client.println("<body><p>Push button to open/close door</p>");
            // Display current state, and ON/OFF buttons for GPIO 5  
             {
              
          
          
          client.println("<p><a href='/trigger' target='inlineframe'><button class=\"button button2\">PRESS</button></a></p>"); 
          
          client.println("<IFRAME name=inlineframe style='display:none'>");          
          client.println("</IFRAME>");

               
            } 
              } 
              
             if (header.indexOf("GET /trigger") >= 0) {
              Serial.println("GPIO 5 triggered");
              output5State = "on";
              digitalWrite(5, HIGH);
               Serial.println("High");
              delay(1000);
              digitalWrite(5, LOW);
               Serial.println("Low");
               
                
               
            }
            
            client.println("</body></html>");
          } else {
            
            // wrong user/pass
            //client.println("HTTP/1.0 401 Authorization Required");
            client.println("HTTP/1.1 401 Unauthorized");
            client.println("WWW-Authenticate: Basic realm=\"Secure\"");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<html>You are not authenticated.. GO AWAY!</html>"); // popup message for incorrect password.

          }
          
          header = "";
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

Attached are screen shots. Login to the webserver, not authenticated, authenticated.

Pylon, do you by chance know how to add a disconnect button? I've seen a few examples like ones the "simple authentication."

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;

and this would be the link to that on the code

content += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">disconnect</a></body></html>";
  server.send(200, "text/html", content);

Since my code isn't set up the same is it possible to add something like that?
Thanks

Since my code isn't set up the same is it possible to add something like that?

That logout code assumes that cookies are used for authentication. The code you posted last as your code does an extremely simple version of basic authentication. With that method a logout isn't possible as the browser sends the credentials with every request again.
You can trick some browser into forgetting the credentials by simply sending the 401 reply even if the sent credentials are OK. Keep in mind that not all browsers will forget the credentials after receiving a 401 response code so you might have to test with your environment and target client devices.

That didnt work for my application. Is a cookie type authentication more secure than the base64 code type? If so I could work on redoing my webserver code to work with the cookie.

That didnt work for my application. Is a cookie type authentication more secure than the base64 code type?

No.

With which browsers did you test? How did you test? Are you sure you implemented it correctly? What about posting the code?

Why is a logout so important for you? I don't see any need for a logout functionality in a well designed web application. Even if the application is called in an internet café a user can simply quit the browser once she's done. The password storage functionality of modern browsers are triggered by both versions so that doesn't make a difference.

Logout is important to prevent other users to click something by mistake by keeping the window up on their phone. Anyways I got the cookie type authentication working with the same appearance as my previous webserver. Thanks for all your help!

Logout is important to prevent other users to click something by mistake by keeping the window up on their phone.

I personally think that closing the windows is much easier than to click on some logout button in that window. I know that logout functions are trendy for at least 10 years but it's a feature only people ask for that doesn't know how stuff works (no offends). Technically there's no need for a logout, at least I never found a reason for it in my 25 years of Internet programming experience.

@slvrsky I was looking for something like that in the past a few days with no avail. Fortunately I found your thread today. I tried what your code. It works just fine. I learned a lot during the process also. Thank you very much for your effort.
I agree logout is important as you said. However, it may be better it automatically logout after the browser's the web page is closed. Alternatively, it may be proper to logout after a certain period of inactivity.
Thanks again!

I’m glad it helped. I have a few more types of web servers since I have posted this code. I now have a RPI 3b as my main web server and a bunch of esp8266s doing small tasks around my house. If you get stuck on something again, maybe I have one doing something similar.

Your work is indeed very helpful. I am not a code developer even thought I can handle computer and electronics pretty well. I may be able to implement and test codes have been developed. However my ability to develop new code/software is very limited. I am o.k. with c/c++ but not with HTML.
I think the only remaining issue with your implementation right now is how to logout, either automatically or by using a button. I think it is probably easier to let it automatically disconnect after a few minutes of inactivity. Then it will require another log in if needed. Do you have any suggestions how to implement this?
For the garage door opener, I like UI shown in "https://www.instructables.com/id/Smart-Phone-WiFi-Controlled-Garage-Door-Opener-Wit/". I will try to see if I port their UI to your code. Again, if it deals with HTML, it might be difficult for me.

I’ll post one I have made for a friends garage door tomorrow for you.