Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright 2016-2026 Hristo Gochkov, Mathieu Carbou, Emil Muratov, Will Miles
3
 
4
//
5
// Authentication and authorization middlewares
6
//
7
 
8
#include <Arduino.h>
9
#if defined(ESP32) || defined(LIBRETINY)
10
#include <AsyncTCP.h>
11
#include <WiFi.h>
12
#elif defined(ESP8266)
13
#include <ESP8266WiFi.h>
14
#include <ESPAsyncTCP.h>
15
#elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
16
#include <RPAsyncTCP.h>
17
#include <WiFi.h>
18
#endif
19
 
20
#include <ESPAsyncWebServer.h>
21
 
22
static AsyncWebServer server(80);
23
 
24
// basicAuth
25
static AsyncAuthenticationMiddleware basicAuth;
26
static AsyncAuthenticationMiddleware basicAuthHash;
27
 
28
// simple digest authentication
29
static AsyncAuthenticationMiddleware digestAuth;
30
static AsyncAuthenticationMiddleware digestAuthHash;
31
 
32
static AsyncAuthenticationMiddleware bearerAuthSharedKey;
33
static AsyncAuthenticationMiddleware bearerAuthJWT;
34
 
35
// complex authentication which adds request attributes for the next middlewares and handler
36
static AsyncMiddlewareFunction complexAuth([](AsyncWebServerRequest *request, ArMiddlewareNext next) {
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 {
42
    return request->requestAuthentication();
43
  }
44
 
45
  if (request->getAttribute("user") == "Mathieu") {
46
    request->setAttribute("role", "staff");
47
  } else {
48
    request->setAttribute("role", "user");
49
  }
50
 
51
  next();
52
});
53
 
54
static AsyncAuthorizationMiddleware authz([](AsyncWebServerRequest *request) {
55
  return request->getAttribute("role") == "staff";
56
});
57
 
58
void setup() {
59
  Serial.begin(115200);
60
 
61
#if ASYNCWEBSERVER_WIFI_SUPPORTED
62
  WiFi.mode(WIFI_AP);
63
  WiFi.softAP("esp-captive");
64
#endif
65
 
66
  // basic authentication
67
  basicAuth.setUsername("admin");
68
  basicAuth.setPassword("admin");
69
  basicAuth.setRealm("MyApp");
70
  basicAuth.setAuthFailureMessage("Authentication failed");
71
  basicAuth.setAuthType(AsyncAuthType::AUTH_BASIC);
72
  basicAuth.generateHash();  // precompute hash (optional but recommended)
73
 
74
  // basic authentication with hash
75
  basicAuthHash.setUsername("admin");
76
  basicAuthHash.setPasswordHash("YWRtaW46YWRtaW4=");  // BASE64(admin:admin)
77
  basicAuthHash.setRealm("MyApp");
78
  basicAuthHash.setAuthFailureMessage("Authentication failed");
79
  basicAuthHash.setAuthType(AsyncAuthType::AUTH_BASIC);
80
 
81
  // digest authentication
82
  digestAuth.setUsername("admin");
83
  digestAuth.setPassword("admin");
84
  digestAuth.setRealm("MyApp");
85
  digestAuth.setAuthFailureMessage("Authentication failed");
86
  digestAuth.setAuthType(AsyncAuthType::AUTH_DIGEST);
87
  digestAuth.generateHash();  // precompute hash (optional but recommended)
88
 
89
  // digest authentication with hash
90
  digestAuthHash.setUsername("admin");
91
  digestAuthHash.setPasswordHash("f499b71f9a36d838b79268e145e132f7");  // MD5(user:realm:pass)
92
  digestAuthHash.setRealm("MyApp");
93
  digestAuthHash.setAuthFailureMessage("Authentication failed");
94
  digestAuthHash.setAuthType(AsyncAuthType::AUTH_DIGEST);
95
 
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
 
126
  // basic authentication method
127
  // curl -v -u admin:admin  http://192.168.4.1/auth-basic
128
  server
129
    .on(
130
      "/auth-basic", HTTP_GET,
131
      [](AsyncWebServerRequest *request) {
132
        request->send(200, "text/plain", "Hello, world!");
133
      }
134
    )
135
    .addMiddleware(&basicAuth);
136
 
137
  // basic authentication method with hash
138
  // curl -v -u admin:admin  http://192.168.4.1/auth-basic-hash
139
  server
140
    .on(
141
      "/auth-basic-hash", HTTP_GET,
142
      [](AsyncWebServerRequest *request) {
143
        request->send(200, "text/plain", "Hello, world!");
144
      }
145
    )
146
    .addMiddleware(&basicAuthHash);
147
 
148
  // digest authentication
149
  // curl -v -u admin:admin --digest  http://192.168.4.1/auth-digest
150
  server
151
    .on(
152
      "/auth-digest", HTTP_GET,
153
      [](AsyncWebServerRequest *request) {
154
        request->send(200, "text/plain", "Hello, world!");
155
      }
156
    )
157
    .addMiddleware(&digestAuth);
158
 
159
  // digest authentication with hash
160
  // curl -v -u admin:admin --digest  http://192.168.4.1/auth-digest-hash
161
  server
162
    .on(
163
      "/auth-digest-hash", HTTP_GET,
164
      [](AsyncWebServerRequest *request) {
165
        request->send(200, "text/plain", "Hello, world!");
166
      }
167
    )
168
    .addMiddleware(&digestAuthHash);
169
 
170
  // test digest auth custom authorization middleware
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
174
  server
175
    .on(
176
      "/auth-custom", HTTP_GET,
177
      [](AsyncWebServerRequest *request) {
178
        String buffer = "Hello ";
179
        buffer.concat(request->getAttribute("user"));
180
        buffer.concat(" with role: ");
181
        buffer.concat(request->getAttribute("role"));
182
        request->send(200, "text/plain", buffer);
183
      }
184
    )
185
    .addMiddlewares({&complexAuth, &authz});
186
 
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
 
213
  server.begin();
214
}
215
 
216
// not needed
217
void loop() {
218
  delay(100);
219
}