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

Skip to content

Commit a11155f

Browse files
committed
Completing support for Bearer tokens which were initially half-supported
1 parent 1e6ade0 commit a11155f

File tree

3 files changed

+123
-22
lines changed

3 files changed

+123
-22
lines changed

examples/Auth/Auth.ino

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,30 @@ static AsyncAuthenticationMiddleware basicAuthHash;
2929
static AsyncAuthenticationMiddleware digestAuth;
3030
static AsyncAuthenticationMiddleware digestAuthHash;
3131

32+
static AsyncAuthenticationMiddleware bearerAuthSharedKey;
33+
static AsyncAuthenticationMiddleware bearerAuthJWT;
34+
3235
// complex authentication which adds request attributes for the next middlewares and handler
3336
static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
34-
if (!request->authenticate("user", "password")) {
37+
if (request->authenticate("Mathieu", "password")) {
38+
request->setAttribute("user", "Mathieu");
39+
} else if (request->authenticate("Bob", "password")) {
40+
request->setAttribute("user", "Bob");
41+
} else {
3542
return request->requestAuthentication();
3643
}
3744

38-
// add attributes to the request for the next middlewares and handler
39-
request->setAttribute("user", "Mathieu");
40-
request->setAttribute("role", "staff");
41-
if (request->hasParam("token")) {
42-
request->setAttribute("token", request->getParam("token")->value().c_str());
45+
if (request->getAttribute("user") == "Mathieu") {
46+
request->setAttribute("role", "staff");
47+
} else {
48+
request->setAttribute("role", "user");
4349
}
4450

4551
next();
4652
});
4753

4854
static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
49-
return request->getAttribute("token") == "123";
55+
return request->getAttribute("role") == "staff";
5056
});
5157

5258
void setup() {
@@ -87,6 +93,36 @@ void setup() {
8793
digestAuthHash.setAuthFailureMessage("Authentication failed");
8894
digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
8995

96+
// bearer authentication with shared key
97+
bearerAuthSharedKey.setAuthType(AsyncAuthType::AUTH_BEARER);
98+
bearerAuthSharedKey.setToken("shared-secret-key");
99+
100+
// bearer authentication with a JWT token
101+
bearerAuthJWT.setAuthType(AsyncAuthType::AUTH_BEARER);
102+
bearerAuthJWT.setAuthentificationFunction([](AsyncWebServerRequest *request) {
103+
const String& token = request->authChallenge();
104+
// 1. decode base64 token
105+
// 2. decrypt token
106+
const String& decrypted = "..."; // TODO
107+
// 3. validate token (check signature, expiration, etc)
108+
bool valid = token == "<token>" || token == "<another token>";
109+
if(!valid) {
110+
return false;
111+
}
112+
// 4. extract user info from token and set request attributes
113+
if(token == "<token>") {
114+
request->setAttribute("user", "Mathieu");
115+
request->setAttribute("role", "staff");
116+
return true; // return true if token is valid, false otherwise
117+
}
118+
if(token == "<another token>") {
119+
request->setAttribute("user", "Bob");
120+
request->setAttribute("role", "user");
121+
return true; // return true if token is valid, false otherwise
122+
}
123+
return false;
124+
});
125+
90126
// basic authentication method
91127
// curl -v -u admin:admin http://192.168.4.1/auth-basic
92128
server
@@ -132,9 +168,9 @@ void setup() {
132168
.addMiddleware(&digestAuthHash);
133169

134170
// test digest auth custom authorization middleware
135-
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=123 => OK
136-
// curl -v --digest -u user:password http://192.168.4.1/auth-custom?token=456 => 403
137-
// curl -v --digest -u user:FAILED http://192.168.4.1/auth-custom?token=456 => 401
171+
// curl -v --digest -u Mathieu:password http://192.168.4.1/auth-custom => OK
172+
// curl -v --digest -u Bob:password http://192.168.4.1/auth-custom => 403
173+
// curl -v --digest -u any:password http://192.168.4.1/auth-custom => 401
138174
server
139175
.on(
140176
"/auth-custom", HTTP_GET,
@@ -148,6 +184,32 @@ void setup() {
148184
)
149185
.addMiddlewares({&complexAuth, &authz});
150186

187+
// Bearer authentication with a shared key
188+
// curl -v -H "Authorization: Bearer shared-secret-key" http://192.168.4.1/auth-bearer-shared-key => OK
189+
server
190+
.on(
191+
"/auth-bearer-shared-key", HTTP_GET,
192+
[](AsyncWebServerRequest *request) {
193+
request->send(200, "text/plain", "Hello, world!");
194+
}
195+
)
196+
.addMiddleware(&bearerAuthSharedKey);
197+
198+
// Bearer authentication with a JWT token
199+
// curl -v -H "Authorization: Bearer <token>" http://192.168.4.1/auth-bearer-jwt => OK
200+
// curl -v -H "Authorization: Bearer <another token>" http://192.168.4.1/auth-bearer-jwt => 403 Forbidden
201+
// curl -v -H "Authorization: Bearer invalid-token" http://192.168.4.1/auth-bearer-jwt => 401 Unauthorized
202+
server
203+
.on(
204+
"/auth-bearer-jwt", HTTP_GET,
205+
[](AsyncWebServerRequest *request) {
206+
Serial.println("User: " + request->getAttribute("user"));
207+
Serial.println("Role: " + request->getAttribute("role"));
208+
request->send(200, "text/plain", "Hello, world!");
209+
}
210+
)
211+
.addMiddlewares({&bearerAuthJWT, &authz});
212+
151213
server.begin();
152214
}
153215

src/ESPAsyncWebServer.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,17 @@ class AsyncWebServerRequest {
356356
}
357357
void requestAuthentication(AsyncAuthType method, const char *realm = nullptr, const char *_authFailMsg = nullptr);
358358

359+
// detected Authentication type from "Authorization" request header during request parsing
360+
AsyncAuthType authType() const {
361+
return _authMethod;
362+
}
363+
364+
// raw value of "Authorization" request header after the auth type
365+
// For example, for header "Authorization: Bearer <token>", <token> is the value returned
366+
const String& authChallenge() const {
367+
return _authorization;
368+
}
369+
359370
// IMPORTANT: this method is for internal use ONLY
360371
// Please do not use it!
361372
// It can be removed or modified at any time without notice
@@ -774,10 +785,35 @@ class AsyncMiddlewareChain {
774785
// AsyncAuthenticationMiddleware is a middleware that checks if the request is authenticated
775786
class AsyncAuthenticationMiddleware : public AsyncMiddleware {
776787
public:
788+
const String &username() const {
789+
return _username;
790+
}
791+
const String &credentials() const {
792+
return _credentials;
793+
}
794+
const String &realm() const {
795+
return _realm;
796+
}
797+
const String &authFailureMessage() const {
798+
return _authFailMsg;
799+
}
800+
bool isHash() const {
801+
return _hash;
802+
}
803+
AsyncAuthType authType() const {
804+
return _authMethod;
805+
}
806+
777807
void setUsername(const char *username);
778808
void setPassword(const char *password);
779809
void setPasswordHash(const char *hash);
780810

811+
// can be used for Bearer token authentication with a static shared secret
812+
void setToken(const char *token);
813+
void setAuthentificationFunction(std::function<bool(AsyncWebServerRequest *request)> func) {
814+
_authcFunc = func;
815+
}
816+
781817
void setRealm(const char *realm) {
782818
_realm = realm;
783819
}
@@ -820,6 +856,7 @@ class AsyncAuthenticationMiddleware : public AsyncMiddleware {
820856
AsyncAuthType _authMethod = AsyncAuthType::AUTH_NONE;
821857
String _authFailMsg;
822858
bool _hasCreds = false;
859+
std::function<bool(AsyncWebServerRequest *request)> _authcFunc = [this](AsyncWebServerRequest *request) { return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash); };
823860
};
824861

825862
using ArAuthorizeFunction = std::function<bool(AsyncWebServerRequest *request)>;

src/Middleware.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ void AsyncAuthenticationMiddleware::setPasswordHash(const char *hash) {
8585
_hasCreds = _username.length() && _credentials.length();
8686
}
8787

88+
void AsyncAuthenticationMiddleware::setToken(const char *token) {
89+
_credentials = token;
90+
_hash = _credentials.length();
91+
_hasCreds = _credentials.length();
92+
}
93+
8894
bool AsyncAuthenticationMiddleware::generateHash() {
8995
// ensure we have all the necessary data
9096
if (!_hasCreds) {
@@ -120,19 +126,15 @@ bool AsyncAuthenticationMiddleware::generateHash() {
120126
}
121127

122128
bool AsyncAuthenticationMiddleware::allowed(AsyncWebServerRequest *request) const {
123-
if (_authMethod == AsyncAuthType::AUTH_NONE) {
124-
return true;
125-
}
126-
127-
if (_authMethod == AsyncAuthType::AUTH_DENIED) {
128-
return false;
129-
}
130-
131-
if (!_hasCreds) {
132-
return true;
129+
switch (_authMethod) {
130+
case AsyncAuthType::AUTH_NONE: return true;
131+
case AsyncAuthType::AUTH_DENIED: return false;
132+
case AsyncAuthType::AUTH_BEARER: return _authcFunc(request);
133+
case AsyncAuthType::AUTH_OTHER: return _authcFunc(request);
134+
case AsyncAuthType::AUTH_BASIC: return !_hasCreds || _authcFunc(request);
135+
case AsyncAuthType::AUTH_DIGEST: return !_hasCreds || _authcFunc(request);
136+
default: return false;
133137
}
134-
135-
return request->authenticate(_username.c_str(), _credentials.c_str(), _realm.c_str(), _hash);
136138
}
137139

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

0 commit comments

Comments
 (0)