Add Digest Web Authentication and make it default
accept htdigest formatted hashes as well
This commit is contained in:
parent
cdd1ced67a
commit
49b6046125
|
@ -35,9 +35,9 @@ script:
|
||||||
- arduino --board esp8266com:esp8266:generic --save-prefs
|
- arduino --board esp8266com:esp8266:generic --save-prefs
|
||||||
- arduino --get-pref sketchbook.path
|
- arduino --get-pref sketchbook.path
|
||||||
- build_sketches arduino $HOME/Arduino/libraries/ESPAsyncWebServer esp8266
|
- build_sketches arduino $HOME/Arduino/libraries/ESPAsyncWebServer esp8266
|
||||||
- arduino --board espressif:ESP31B:esp31b --save-prefs
|
# - arduino --board espressif:ESP31B:esp31b --save-prefs
|
||||||
- arduino --get-pref sketchbook.path
|
# - arduino --get-pref sketchbook.path
|
||||||
- build_sketches arduino $HOME/Arduino/libraries/ESPAsyncWebServer esp31b
|
# - build_sketches arduino $HOME/Arduino/libraries/ESPAsyncWebServer esp31b
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
|
|
|
@ -14,11 +14,11 @@ To use this library you need to have the latest git versions of either [ESP8266]
|
||||||
- When you send the response, you are immediately ready to handle other connections
|
- When you send the response, you are immediately ready to handle other connections
|
||||||
while the server is taking care of sending the response in the background
|
while the server is taking care of sending the response in the background
|
||||||
- Speed is OMG
|
- Speed is OMG
|
||||||
- Easy to use API, HTTP Basic Authentication, ChunkedResponse
|
- Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse
|
||||||
- Easily extendible to handle any type of content
|
- Easily extendible to handle any type of content
|
||||||
- Supports Continue 100
|
- Supports Continue 100
|
||||||
- Async WebSocket plugin offering different locations without extra servers or ports
|
- Async WebSocket plugin offering different locations without extra servers or ports
|
||||||
- Async EventSource (ServerSideEvents) plugin to send events to the browser
|
- Async EventSource (Server-Sent Events) plugin to send events to the browser
|
||||||
- URL Rewrite plugin for conditional and permanent url rewrites
|
- URL Rewrite plugin for conditional and permanent url rewrites
|
||||||
- ServeStatic plugin that supports cache, Last-Modified, default index and more
|
- ServeStatic plugin that supports cache, Last-Modified, default index and more
|
||||||
|
|
||||||
|
@ -668,7 +668,7 @@ client->binary(flash_binary, 4);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Async Event Source Plugin
|
## Async Event Source Plugin
|
||||||
The server includes EventSource (ServerSideEvents) plugin which can be used to send short text events to the browser.
|
The server includes EventSource (Server-Sent Events) plugin which can be used to send short text events to the browser.
|
||||||
Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol.
|
Difference between EventSource and WebSockets is that EventSource is single direction, text-only protocol.
|
||||||
|
|
||||||
|
|
||||||
|
@ -679,7 +679,7 @@ Difference between EventSource and WebSockets is that EventSource is single dire
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws
|
AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws
|
||||||
AsyncEventSource events("/events"); // event source (server side events)
|
AsyncEventSource events("/events"); // event source (Server-Sent events)
|
||||||
|
|
||||||
const char* ssid = "your-ssid";
|
const char* ssid = "your-ssid";
|
||||||
const char* password = "your-pass";
|
const char* password = "your-pass";
|
||||||
|
|
|
@ -129,6 +129,7 @@ class AsyncWebServerRequest {
|
||||||
String _contentType;
|
String _contentType;
|
||||||
String _boundary;
|
String _boundary;
|
||||||
String _authorization;
|
String _authorization;
|
||||||
|
bool _isDigest;
|
||||||
bool _isMultipart;
|
bool _isMultipart;
|
||||||
bool _isPlainPost;
|
bool _isPlainPost;
|
||||||
bool _expectingContinue;
|
bool _expectingContinue;
|
||||||
|
@ -188,9 +189,13 @@ class AsyncWebServerRequest {
|
||||||
bool multipart(){ return _isMultipart; }
|
bool multipart(){ return _isMultipart; }
|
||||||
const char * methodToString();
|
const char * methodToString();
|
||||||
|
|
||||||
bool authenticate(const char * username, const char * password);
|
|
||||||
|
//hash is the string representation of:
|
||||||
|
// base64(user:pass) for basic or
|
||||||
|
// user:realm:md5(user:realm:pass) for digest
|
||||||
bool authenticate(const char * hash);
|
bool authenticate(const char * hash);
|
||||||
void requestAuthentication();
|
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
|
||||||
|
void requestAuthentication(const char * realm = NULL, bool isDigest = true);
|
||||||
|
|
||||||
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
||||||
void addInterestingHeader(String name);
|
void addInterestingHeader(String name);
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
Asynchronous WebServer library for Espressif MCUs
|
||||||
|
|
||||||
|
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include "WebAuthentication.h"
|
||||||
|
#include <libb64/cencode.h>
|
||||||
|
#include "md5.h"
|
||||||
|
|
||||||
|
// Basic Auth hash = base64("username:password")
|
||||||
|
|
||||||
|
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
|
||||||
|
if(username == NULL || password == NULL || hash == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t toencodeLen = os_strlen(username)+os_strlen(password)+1;
|
||||||
|
size_t encodedLen = base64_encode_expected_len(toencodeLen);
|
||||||
|
if(strlen(hash) != encodedLen)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char *toencode = new char[toencodeLen+1];
|
||||||
|
if(toencode == NULL){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
||||||
|
if(encoded == NULL){
|
||||||
|
delete[] toencode;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sprintf(toencode, "%s:%s", username, password);
|
||||||
|
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
|
||||||
|
delete[] toencode;
|
||||||
|
delete[] encoded;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delete[] toencode;
|
||||||
|
delete[] encoded;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
|
||||||
|
md5_context_t _ctx;
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t * _buf = (uint8_t*)malloc(16);
|
||||||
|
if(_buf == NULL)
|
||||||
|
return false;
|
||||||
|
memset(_buf, 0x00, 16);
|
||||||
|
MD5Init(&_ctx);
|
||||||
|
MD5Update(&_ctx, data, len);
|
||||||
|
MD5Final(_buf, &_ctx);
|
||||||
|
for(i = 0; i < 16; i++) {
|
||||||
|
sprintf(output + (i * 2), "%02x", _buf[i]);
|
||||||
|
}
|
||||||
|
free(_buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String genRandomMD5(){
|
||||||
|
uint32_t r = RANDOM_REG32;
|
||||||
|
char * out = (char*)malloc(33);
|
||||||
|
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
||||||
|
return "";
|
||||||
|
String res = String(out);
|
||||||
|
free(out);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String stringMD5(String in){
|
||||||
|
char * out = (char*)malloc(33);
|
||||||
|
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||||
|
return "";
|
||||||
|
String res = String(out);
|
||||||
|
free(out);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
String generateDigestHash(const char * username, const char * password, const char * realm){
|
||||||
|
if(username == NULL || password == NULL || realm == NULL){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
char * out = (char*)malloc(33);
|
||||||
|
String res = String(username);
|
||||||
|
res.concat(":");
|
||||||
|
res.concat(realm);
|
||||||
|
res.concat(":");
|
||||||
|
String in = res;
|
||||||
|
in.concat(password);
|
||||||
|
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
|
||||||
|
return "";
|
||||||
|
res.concat(out);
|
||||||
|
free(out);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
String requestDigestAuthentication(const char * realm){
|
||||||
|
String header = "realm=\"";
|
||||||
|
if(realm == NULL)
|
||||||
|
header.concat("asyncesp");
|
||||||
|
else
|
||||||
|
header.concat(realm);
|
||||||
|
header.concat( "\", qop=\"auth\", nonce=\"");
|
||||||
|
header.concat(genRandomMD5());
|
||||||
|
header.concat("\", opaque=\"");
|
||||||
|
header.concat(genRandomMD5());
|
||||||
|
header.concat("\"");
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
|
||||||
|
if(username == NULL || password == NULL || header == NULL || method == NULL){
|
||||||
|
//os_printf("AUTH FAIL: missing requred fields\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String myHeader = String(header);
|
||||||
|
int nextBreak = myHeader.indexOf(", ");
|
||||||
|
if(nextBreak < 0){
|
||||||
|
//os_printf("AUTH FAIL: no variables\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String myUsername = String();
|
||||||
|
String myRealm = String();
|
||||||
|
String myNonce = String();
|
||||||
|
String myUri = String();
|
||||||
|
String myResponse = String();
|
||||||
|
String myQop = String();
|
||||||
|
String myNc = String();
|
||||||
|
String myCnonce = String();
|
||||||
|
|
||||||
|
myHeader += ", ";
|
||||||
|
do {
|
||||||
|
String avLine = myHeader.substring(0, nextBreak);
|
||||||
|
myHeader = myHeader.substring(nextBreak+2);
|
||||||
|
nextBreak = myHeader.indexOf(", ");
|
||||||
|
|
||||||
|
int eqSign = avLine.indexOf("=");
|
||||||
|
if(eqSign < 0){
|
||||||
|
//os_printf("AUTH FAIL: no = sign\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String varName = avLine.substring(0, eqSign);
|
||||||
|
avLine = avLine.substring(eqSign + 1);
|
||||||
|
if(avLine.startsWith("\"")){
|
||||||
|
avLine = avLine.substring(1, avLine.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(varName.equals("username")){
|
||||||
|
if(!avLine.equals(username)){
|
||||||
|
//os_printf("AUTH FAIL: username\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
myUsername = avLine;
|
||||||
|
} else if(varName.equals("realm")){
|
||||||
|
if(realm != NULL && !avLine.equals(realm)){
|
||||||
|
//os_printf("AUTH FAIL: realm\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
myRealm = avLine;
|
||||||
|
} else if(varName.equals("nonce")){
|
||||||
|
if(nonce != NULL && !avLine.equals(nonce)){
|
||||||
|
//os_printf("AUTH FAIL: nonce\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
myNonce = avLine;
|
||||||
|
} else if(varName.equals("opaque")){
|
||||||
|
if(opaque != NULL && !avLine.equals(opaque)){
|
||||||
|
//os_printf("AUTH FAIL: opaque\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if(varName.equals("uri")){
|
||||||
|
if(uri != NULL && !avLine.equals(uri)){
|
||||||
|
//os_printf("AUTH FAIL: uri\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
myUri = avLine;
|
||||||
|
} else if(varName.equals("response")){
|
||||||
|
myResponse = avLine;
|
||||||
|
} else if(varName.equals("qop")){
|
||||||
|
myQop = avLine;
|
||||||
|
} else if(varName.equals("nc")){
|
||||||
|
myNc = avLine;
|
||||||
|
} else if(varName.equals("cnonce")){
|
||||||
|
myCnonce = avLine;
|
||||||
|
}
|
||||||
|
} while(nextBreak > 0);
|
||||||
|
|
||||||
|
String ha1 = (passwordIsHash) ? String(password) : myUsername + ":" + myRealm + ":" + String(password);
|
||||||
|
String ha2 = String(method) + ":" + myUri;
|
||||||
|
String response = stringMD5(ha1) + ":" + myNonce + ":" + myNc + ":" + myCnonce + ":" + myQop + ":" + stringMD5(ha2);
|
||||||
|
|
||||||
|
if(myResponse.equals(stringMD5(response))){
|
||||||
|
//os_printf("AUTH SUCCESS\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//os_printf("AUTH FAIL: password\n");
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Asynchronous WebServer library for Espressif MCUs
|
||||||
|
|
||||||
|
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
|
||||||
|
This file is part of the esp8266 core for Arduino environment.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEB_AUTHENTICATION_H_
|
||||||
|
#define WEB_AUTHENTICATION_H_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
|
||||||
|
String requestDigestAuthentication(const char * realm);
|
||||||
|
bool checkDigestAuthentication(const char * header, const char * method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
|
||||||
|
|
||||||
|
//for storing hashed versions on the device that can be authenticated against
|
||||||
|
String generateDigestHash(const char * username, const char * password, const char * realm);
|
||||||
|
|
||||||
|
#endif
|
|
@ -19,8 +19,8 @@
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include <libb64/cencode.h>
|
|
||||||
#include "WebResponseImpl.h"
|
#include "WebResponseImpl.h"
|
||||||
|
#include "WebAuthentication.h"
|
||||||
|
|
||||||
#ifndef ESP8266
|
#ifndef ESP8266
|
||||||
#define os_strlen strlen
|
#define os_strlen strlen
|
||||||
|
@ -45,6 +45,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||||
, _contentType()
|
, _contentType()
|
||||||
, _boundary()
|
, _boundary()
|
||||||
, _authorization()
|
, _authorization()
|
||||||
|
, _isDigest(false)
|
||||||
, _isMultipart(false)
|
, _isMultipart(false)
|
||||||
, _isPlainPost(false)
|
, _isPlainPost(false)
|
||||||
, _expectingContinue(false)
|
, _expectingContinue(false)
|
||||||
|
@ -139,6 +140,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!_isPlainPost) {
|
if(!_isPlainPost) {
|
||||||
|
//check if authenticated before calling the body
|
||||||
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
||||||
_parsedLength += len;
|
_parsedLength += len;
|
||||||
} else {
|
} else {
|
||||||
|
@ -152,6 +154,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||||
|
|
||||||
if(_parsedLength == _contentLength){
|
if(_parsedLength == _contentLength){
|
||||||
_parseState = PARSE_REQ_END;
|
_parseState = PARSE_REQ_END;
|
||||||
|
//check if authenticated before calling handleRequest and request auth instead
|
||||||
if(_handler) _handler->handleRequest(this);
|
if(_handler) _handler->handleRequest(this);
|
||||||
else send(501);
|
else send(501);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +257,7 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
||||||
_url = u;
|
_url = u;
|
||||||
_addGetParams(g);
|
_addGetParams(g);
|
||||||
|
|
||||||
if(_temp.startsWith("HTTP/1.1"))
|
if(!_temp.startsWith("HTTP/1.0"))
|
||||||
_version = 1;
|
_version = 1;
|
||||||
|
|
||||||
_temp = String();
|
_temp = String();
|
||||||
|
@ -285,6 +288,9 @@ bool AsyncWebServerRequest::_parseReqHeader(){
|
||||||
} else if(name == "Authorization"){
|
} else if(name == "Authorization"){
|
||||||
if(value.startsWith("Basic")){
|
if(value.startsWith("Basic")){
|
||||||
_authorization = value.substring(6);
|
_authorization = value.substring(6);
|
||||||
|
} else if(value.startsWith("Digest")){
|
||||||
|
_isDigest = true;
|
||||||
|
_authorization = value.substring(7);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(_interestingHeaders->contains(name) || _interestingHeaders->contains("ANY")){
|
if(_interestingHeaders->contains(name) || _interestingHeaders->contains("ANY")){
|
||||||
|
@ -323,6 +329,7 @@ void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){
|
||||||
_itemBuffer[_itemBufferIndex++] = data;
|
_itemBuffer[_itemBufferIndex++] = data;
|
||||||
|
|
||||||
if(last || _itemBufferIndex == 1460){
|
if(last || _itemBufferIndex == 1460){
|
||||||
|
//check if authenticated before calling the upload
|
||||||
if(_handler)
|
if(_handler)
|
||||||
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
|
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
|
||||||
_itemBufferIndex = 0;
|
_itemBufferIndex = 0;
|
||||||
|
@ -463,6 +470,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||||
_addParam(new AsyncWebParameter(_itemName, _itemValue, true));
|
_addParam(new AsyncWebParameter(_itemName, _itemValue, true));
|
||||||
} else {
|
} else {
|
||||||
if(_itemSize){
|
if(_itemSize){
|
||||||
|
//check if authenticated before calling the upload
|
||||||
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||||
_itemBufferIndex = 0;
|
_itemBufferIndex = 0;
|
||||||
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
|
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
|
||||||
|
@ -520,6 +528,7 @@ void AsyncWebServerRequest::_parseLine(){
|
||||||
const char * response = "HTTP/1.1 100 Continue\r\n\r\n";
|
const char * response = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||||
_client->write(response, os_strlen(response));
|
_client->write(response, os_strlen(response));
|
||||||
}
|
}
|
||||||
|
//check handler for authentication
|
||||||
if(_contentLength){
|
if(_contentLength){
|
||||||
_parseState = PARSE_REQ_BODY;
|
_parseState = PARSE_REQ_BODY;
|
||||||
} else {
|
} else {
|
||||||
|
@ -700,38 +709,54 @@ void AsyncWebServerRequest::redirect(String url){
|
||||||
send(response);
|
send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){
|
||||||
bool AsyncWebServerRequest::authenticate(const char * username, const char * password){
|
|
||||||
if(_authorization.length()){
|
if(_authorization.length()){
|
||||||
char toencodeLen = os_strlen(username)+os_strlen(password)+1;
|
if(_isDigest)
|
||||||
char *toencode = new char[toencodeLen+1];
|
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
|
||||||
if(toencode == NULL){
|
else if(!passwordIsHash)
|
||||||
return false;
|
return checkBasicAuthentication(_authorization.c_str(), username, password);
|
||||||
}
|
else
|
||||||
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
return _authorization.equals(password);
|
||||||
if(encoded == NULL){
|
|
||||||
delete[] toencode;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sprintf(toencode, "%s:%s", username, password);
|
|
||||||
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && _authorization.equals(encoded)){
|
|
||||||
delete[] toencode;
|
|
||||||
delete[] encoded;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
delete[] toencode;
|
|
||||||
delete[] encoded;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServerRequest::authenticate(const char * hash){
|
bool AsyncWebServerRequest::authenticate(const char * hash){
|
||||||
return (_authorization.length() && (_authorization == String(hash)));
|
if(!_authorization.length() || hash == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(_isDigest){
|
||||||
|
String hStr = String(hash);
|
||||||
|
int separator = hStr.indexOf(":");
|
||||||
|
if(separator <= 0)
|
||||||
|
return false;
|
||||||
|
String username = hStr.substring(0, separator);
|
||||||
|
hStr = hStr.substring(separator + 1);
|
||||||
|
separator = hStr.indexOf(":");
|
||||||
|
if(separator <= 0)
|
||||||
|
return false;
|
||||||
|
String realm = hStr.substring(0, separator);
|
||||||
|
hStr = hStr.substring(separator + 1);
|
||||||
|
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_authorization.equals(hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::requestAuthentication(){
|
void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){
|
||||||
AsyncWebServerResponse * r = beginResponse(401);
|
AsyncWebServerResponse * r = beginResponse(401);
|
||||||
|
if(!isDigest && realm == NULL){
|
||||||
r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
|
r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
|
||||||
|
} else if(!isDigest){
|
||||||
|
String header = "Basic realm=\"";
|
||||||
|
header.concat(realm);
|
||||||
|
header.concat("\"");
|
||||||
|
r->addHeader("WWW-Authenticate", header);
|
||||||
|
} else {
|
||||||
|
String header = "Digest ";
|
||||||
|
header.concat(requestDigestAuthentication(realm));
|
||||||
|
r->addHeader("WWW-Authenticate", header);
|
||||||
|
}
|
||||||
send(r);
|
send(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue