Skip to content

Adds digest authentication example. #4112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 10, 2018
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

#include <ESP8266HTTPClient.h>

#define USE_SERIAL Serial
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest using Serial directly


ESP8266WiFiMulti WiFiMulti;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to use ESP8266WiFiMulti in this file? I know that some of the existing examples do that, but it would be great if the examples could be made orthogonal.


String _exractParam(String& authReq, const String& param, const char delimit){
int _begin = authReq.indexOf(param);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please load this sketch into Arduino IDE and use Tools > Auto Format feature to fix the indentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it is not a common practice to prefix function and field names with an underscore, when not implementing class members, at least in Arduino.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment about reformatting the sketch using Arduino IDE is still valid, by the way. Arduino sketches use 2 spaces for indentation, this file uses tabs.

if (_begin==-1) return "";
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length()));
}

void setup() {

USE_SERIAL.begin(9600);
// USE_SERIAL.setDebugOutput(true);

USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();

for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}

WiFi.mode(WIFI_STA);
WiFiMulti.addAP("Niligo-Prism-yyr9uma", "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to replace this with something generic, e.g. "wifi-ssid", "wifi-password", and move these settings to the top of the sketch, after includes.


}

void loop() {
// wait for WiFi connection
if((WiFiMulti.run() == WL_CONNECTED)) {

HTTPClient http;

USE_SERIAL.print("[HTTP] begin...\n");
// configure traged server and url


// http.begin("http://admin:[email protected]/api/state?power=1");
http.begin("http://192.168.100.1/api/state?power=0");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a URL of an existing service can be used?

For example, http://httpbin.org/ has a test endpoint which accept Digest Auth (/digest-auth/:qop/:user/:passwd/:algorithm).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's awesome thanks, I will use it. 🙌

/*
// or
// or
http.begin("http://192.168.1.12/test.html");
http.setAuthorization("dXNlcjpwYXN3b3Jk");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a comment could be added explaining the setAuthorization line? (E.g. how to generate the string)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must remove them, we do not need them anymore.

*/


const char *keys[] = {"WWW-Authenticate", "Server", "Connection"};
http.collectHeaders(keys, 3);

USE_SERIAL.print("[HTTP] GET...\n");
// start connection and send HTTP header
int httpCode = http.GET();

if (httpCode > 0) {
String authReq = http.header("WWW-Authenticate");
USE_SERIAL.println(authReq);

// file found at server
String payload = http.getString();
USE_SERIAL.println(payload);

String username = "admin";
String password = "admin";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These might also need to be moved to the top of the sketch, together with wifi SSID/password.


// extracting required parameters for RFC 2069 simpler Digest
// String _realm = _exractParam(authReq, "realm=\"", '"');
String _realm = "Protected";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why it says "extracting required parameters" and then the extractParam call is commented out?

String _nonce = _exractParam(authReq, "nonce=\"", '"');
// String _nonce = "2cadff7e2f160969";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this commented out line still needed? if not, please remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I removed them all.

String _opaque = _exractParam(authReq, "opaque=\"", '"');

// parameters for the RFC 2617 newer Digest
MD5Builder md5;
md5.begin();
md5.add(username + ":" + _realm + ":" + password); // md5 of the user:realm:user
md5.calculate();
String _H1 = md5.toString();

md5.begin();
md5.add(String("GET:") + String("/api/state?power=0"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth storing the URI part in a separate variable and using it both when making HTTP call and here, instead of duplicating.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I created separate variables for them

md5.calculate();
String _H2 = md5.toString();

md5.begin();
md5.add(_H1 + ":" + _nonce + ":" + "00000001" + ":" + "gDBuFY4s" + ":" + "auth" + ":" + _H2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the 00000001 and gDBuFY4s magic values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In digest authentication client must generate nonce count and client nonce we set them here manually but they can set by creating a random string.

md5.calculate();
String _response = md5.toString();

http.begin("http://192.168.100.1/api/state?power=0");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, use a variable for URI to avoid duplication.

USE_SERIAL.println(_nonce);
String Author = "Digest username=\"admin\", realm=\"Protected\", nonce=\"" + _nonce + "\", uri=\"/api/state?power=0\", algorithm=\"MD5\", qop=auth, nc=00000001, cnonce=\"gDBuFY4s\", response=\"" + _response + "\"\r\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest wrapping this part of the code (starting from extractParam calls) into a function which users can copy into their own sketch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

username and URI are duplicated here again, along with nonces...

USE_SERIAL.println(Author);
http.addHeader("Authorization", Author);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use lowercase names for local variables. Also, the name author is a bit confusing, should it be authorization?


http.GET();

payload = http.getString();
USE_SERIAL.println(payload);
} else {
USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}

http.end();
}

delay(10000);
}