Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 72 additions & 10 deletions examples/Auth/Auth.ino
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,30 @@ static AsyncAuthenticationMiddleware basicAuthHash;
static AsyncAuthenticationMiddleware digestAuth;
static AsyncAuthenticationMiddleware digestAuthHash;

static AsyncAuthenticationMiddleware bearerAuthSharedKey;
static AsyncAuthenticationMiddleware bearerAuthJWT;

// complex authentication which adds request attributes for the next middlewares and handler
static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
if (!request->authenticate("user", "password")) {
if (request->authenticate("Mathieu", "password")) {
request->setAttribute("user", "Mathieu");
} else if (request->authenticate("Bob", "password")) {
request->setAttribute("user", "Bob");
} else {
return request->requestAuthentication();
}

// add attributes to the request for the next middlewares and handler
request->setAttribute("user", "Mathieu");
request->setAttribute("role", "staff");
if (request->hasParam("token")) {
request->setAttribute("token", request->getParam("token")->value().c_str());
if (request->getAttribute("user") == "Mathieu") {
request->setAttribute("role", "staff");
} else {
request->setAttribute("role", "user");
}

next();
});

static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
return request->getAttribute("token") == "123";
return request->getAttribute("role") == "staff";
});

void setup() {
Expand Down Expand Up @@ -87,6 +93,36 @@ void setup() {
digestAuthHash.setAuthFailureMessage("Authentication failed");
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);

// bearer authentication with shared key
bearerAuthSharedKey.setAuthType(AsyncAuthType::AUTH_BEARER);
bearerAuthSharedKey.setToken("shared-secret-key");

// bearer authentication with a JWT token
bearerAuthJWT.setAuthType(AsyncAuthType::AUTH_BEARER);
bearerAuthJWT.setAuthentificationFunction([](AsyncWebServerRequest *request) {
const String &token = request->authChallenge();
// 1. decode base64 token
// 2. decrypt token
const String &decrypted = "..."; // TODO
// 3. validate token (check signature, expiration, etc)
bool valid = token == "<token>" || token == "<another token>";
if (!valid) {
return false;
}
// 4. extract user info from token and set request attributes
if (token == "<token>") {
request->setAttribute("user", "Mathieu");
request->setAttribute("role", "staff");
return true; // return true if token is valid, false otherwise
}
if (token == "<another token>") {
request->setAttribute("user", "Bob");
request->setAttribute("role", "user");
return true; // return true if token is valid, false otherwise
}
return false;
});

// basic authentication method
// curl -v -u admin:admin http://192.168.4.1/auth-basic
server
Expand Down Expand Up @@ -132,9 +168,9 @@ void setup() {
.addMiddleware(&digestAuthHash);

// test digest auth custom authorization middleware
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=123 => OK
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=456 => 403
// curl -v --digest -u user:FAILED http://192.168.4.1/auth-custom?token=456 => 401
// curl -v --digest -u Mathieu:password http://192.168.4.1/auth-custom => OK
// curl -v --digest -u Bob:password http://192.168.4.1/auth-custom => 403
// curl -v --digest -u any:password http://192.168.4.1/auth-custom => 401
server
.on(
"/auth-custom", HTTP_GET,
Expand All @@ -148,6 +184,32 @@ void setup() {
)
.addMiddlewares({&complexAuth, &authz});

// Bearer authentication with a shared key
// curl -v -H "Authorization: Bearer shared-secret-key" http://192.168.4.1/auth-bearer-shared-key => OK
server
.on(
"/auth-bearer-shared-key", HTTP_GET,
[](AsyncWebServerRequest *request) {
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddleware(&bearerAuthSharedKey);

// Bearer authentication with a JWT token
// curl -v -H "Authorization: Bearer <token>" http://192.168.4.1/auth-bearer-jwt => OK
// curl -v -H "Authorization: Bearer <another token>" http://192.168.4.1/auth-bearer-jwt => 403 Forbidden
// curl -v -H "Authorization: Bearer invalid-token" http://192.168.4.1/auth-bearer-jwt => 401 Unauthorized
server
.on(
"/auth-bearer-jwt", HTTP_GET,
[](AsyncWebServerRequest *request) {
Serial.println("User: " + request->getAttribute("user"));
Serial.println("Role: " + request->getAttribute("role"));
request->send(200, "text/plain", "Hello, world!");
}
)
.addMiddlewares({&bearerAuthJWT, &authz});

server.begin();
}

Expand Down
39 changes: 39 additions & 0 deletions src/ESPAsyncWebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,17 @@ class AsyncWebServerRequest {
}
void requestAuthentication(AsyncAuthType method, const char *realm = nullptr, const char *_authFailMsg = nullptr);

// detected Authentication type from "Authorization" request header during request parsing
AsyncAuthType authType() const {
return _authMethod;
}

// raw value of "Authorization" request header after the auth type
// For example, for header "Authorization: Bearer <token>", <token> is the value returned
const String &authChallenge() const {
return _authorization;
}

// IMPORTANT: this method is for internal use ONLY
// Please do not use it!
// It can be removed or modified at any time without notice
Expand Down Expand Up @@ -774,10 +785,35 @@ class AsyncMiddlewareChain {
// AsyncAuthenticationMiddleware is a middleware that checks if the request is authenticated
class AsyncAuthenticationMiddleware : public AsyncMiddleware {
public:
const String &username() const {
return _username;
}
const String &credentials() const {
return _credentials;
}
const String &realm() const {
return _realm;
}
const String &authFailureMessage() const {
return _authFailMsg;
}
bool isHash() const {
return _hash;
}
AsyncAuthType authType() const {
return _authMethod;
}

void setUsername(const char *username);
void setPassword(const char *password);
void setPasswordHash(const char *hash);

// can be used for Bearer token authentication with a static shared secret
void setToken(const char *token);
void setAuthentificationFunction(std::function<bool(AsyncWebServerRequest *request)> func) {
_authcFunc = func;
}

void setRealm(const char *realm) {
_realm = realm;
}
Expand Down Expand Up @@ -820,6 +856,9 @@ class AsyncAuthenticationMiddleware : public AsyncMiddleware {
AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE;
String _authFailMsg;
bool _hasCreds = false;
std::function<bool(AsyncWebServerRequest *request)> _authcFunc = [this](AsyncWebServerRequest *request) {
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
};
};

using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest *request)>;
Expand Down
26 changes: 14 additions & 12 deletions src/Middleware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ void AsyncAuthenticationMiddleware::setPasswordHash(const char *hash) {
_hasCreds = _username.length() && _credentials.length();
}

void AsyncAuthenticationMiddleware::setToken(const char *token) {
_credentials = token;
_hash = _credentials.length();
_hasCreds = _credentials.length();
}

bool AsyncAuthenticationMiddleware::generateHash() {
// ensure we have all the necessary data
if (!_hasCreds) {
Expand Down Expand Up @@ -120,19 +126,15 @@ bool AsyncAuthenticationMiddleware::generateHash() {
}

bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest *request) const {
if (_authMethod == AsyncAuthType::AUTH_NONE) {
return true;
}

if (_authMethod == AsyncAuthType::AUTH_DENIED) {
return false;
}

if (!_hasCreds) {
return true;
switch (_authMethod) {
case AsyncAuthType::AUTH_NONE: return true;
case AsyncAuthType::AUTH_DENIED: return false;
case AsyncAuthType::AUTH_BEARER: return _authcFunc(request);
case AsyncAuthType::AUTH_OTHER: return _authcFunc(request);
case AsyncAuthType::AUTH_BASIC: return !_hasCreds || _authcFunc(request);
case AsyncAuthType::AUTH_DIGEST: return !_hasCreds || _authcFunc(request);
default: return false;
}

return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
}

void AsyncAuthenticationMiddleware::run(AsyncWebServerRequest *request, ArMiddlewareNext next) {
Expand Down