Skip to content
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

Creating HTTP Server with Basic Authentication (IDFGH-3720) #5646

Closed
MihirShahLNM opened this issue Jul 26, 2020 · 3 comments
Closed

Creating HTTP Server with Basic Authentication (IDFGH-3720) #5646

MihirShahLNM opened this issue Jul 26, 2020 · 3 comments
Labels
Type: Feature Request Feature request for IDF

Comments

@MihirShahLNM
Copy link

I need to create an HTTP server (or HTTPS), with basic authentication (username & password). I do not want to use Open-SSL Certification.

I get examples to connect as an HTTP Client with basic authentication...

sp_http_client_config_t config = {
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
.event_handler = _http_event_handler,
.auth_type = HTTP_AUTH_TYPE_BASIC,

As I investigated further with structure of "httpd_config_t" .........

**
* Custom session opening callback.
*
* Called on a new session socket just after accept(), but before reading any data.
*
* This is an opportunity to set up e.g. SSL encryption using global_transport_ctx
* and the send/recv/pending session overrides.
*
* If a context needs to be maintained between these functions, store it in the session using
* httpd_sess_set_transport_ctx() and retrieve it later with httpd_sess_get_transport_ctx()
*
* Returning a value other than ESP_OK will immediately close the new socket.
*/
httpd_open_func_t open_fn;


/**
* @brief Function prototype for opening a session.
*
* Called immediately after the socket was opened to set up the send/recv functions and
* other parameters of the socket.
*
* @param[in] hd server instance
* @param[in] sockfd session socket file descriptor
* @return
* - ESP_OK : On success
* - Any value other than ESP_OK will signal the server to close the socket immediately
*/
typedef esp_err_t (*httpd_open_func_t)(httpd_handle_t hd, int sockfd);

I think "open_fn" can help, but don't know how.

Can anyone help me create an HTTP server with Username & Password as authentication method.

@MihirShahLNM MihirShahLNM added the Type: Feature Request Feature request for IDF label Jul 26, 2020
@github-actions github-actions bot changed the title Creating HTTP Server with Basic Authentication Creating HTTP Server with Basic Authentication (IDFGH-3720) Jul 26, 2020
@lhespress
Copy link
Collaborator

@MihirShahLNM
Please add the follow code to the path esp_idf/examples/protocols/http_server/simple/main.c and verify it.

#include "esp_tls_crypto.h"
#include <esp_http_server.h>

/* A simple example that demonstrates how to create GET and POST
 * handlers for the web server.
 */

static const char *TAG = "example";

typedef struct {
    char    *username;
    char    *password;
} basic_auth_info_t;

#define HTTPD_401      "401 UNAUTHORIZED"           /*!< HTTP Response 401 */

static char *http_auth_basic(const char *username, const char *password)
{
    int out;
    char *user_info = NULL;
    char *digest = NULL;
    size_t n = 0;
    asprintf(&user_info, "%s:%s", username, password);
    esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info));
    digest = calloc(1, 6 + n + 1);
    strcpy(digest, "Basic ");
    esp_crypto_base64_encode((unsigned char *)digest + 6, n, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
    free(user_info);
    return digest;
}

/* An HTTP GET handler */
static esp_err_t basie_auth_get_handler(httpd_req_t *req)
{
    char *buf = NULL;
    size_t buf_len = 0;
    basic_auth_info_t *basic_auth_info = req->user_ctx;

    buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
    if (buf_len > 1) {
        buf = calloc(1, buf_len);
        if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) {
            ESP_LOGI(TAG, "Found header => Authorization: %s", buf);
        } else {
            ESP_LOGE(TAG, "No auth value received");
        }

        char *auth_credentials = http_auth_basic(basic_auth_info->username, basic_auth_info->password);
        if (strncmp(auth_credentials, buf, buf_len)) {
            ESP_LOGE(TAG, "Not authenticated");
            httpd_resp_set_status(req, HTTPD_401);
            httpd_resp_set_type(req, "application/json");
            httpd_resp_set_hdr(req, "Connection", "keep-alive");
            httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"Hello\"");
            httpd_resp_send(req, NULL, 0);
        } else {
            ESP_LOGI(TAG, "Authenticated!");
            char *basic_auth_resp = NULL;
            httpd_resp_set_status(req, HTTPD_200);
            httpd_resp_set_type(req, "application/json");
            httpd_resp_set_hdr(req, "Connection", "keep-alive");
            asprintf(&basic_auth_resp, "{\"authenticated\": true,\"user\": \"%s\"}", basic_auth_info->username);
            httpd_resp_send(req, basic_auth_resp, strlen(basic_auth_resp));
            free(basic_auth_resp);
        }
        free(auth_credentials);
        free(buf);
    } else {
        ESP_LOGE(TAG, "No auth header received");
        httpd_resp_set_status(req, HTTPD_401);
        httpd_resp_set_type(req, "application/json");
        httpd_resp_set_hdr(req, "Connection", "keep-alive");
        httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"Hello\"");
        httpd_resp_send(req, NULL, 0);
    }

    return ESP_OK;
}

static httpd_uri_t basic_auth = {
    .uri       = "/basic_auth",
    .method    = HTTP_GET,
    .handler   = basie_auth_get_handler,
};

static void httpd_register_basic_auth(httpd_handle_t server)
{
    basic_auth_info_t *basic_auth_info = calloc(1, sizeof(basic_auth_info_t));
    basic_auth_info->username = "ESP32";
    basic_auth_info->password = "ESP32Webserver";
    
    basic_auth.user_ctx = basic_auth_info;
    httpd_register_uri_handler(server, &basic_auth);
}

and call it in start_webserver function:

static httpd_handle_t start_webserver(void)
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &hello);
        httpd_register_uri_handler(server, &echo);
        httpd_register_uri_handler(server, &ctrl);
        httpd_register_basic_auth(server);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

@MihirShahLNM
Copy link
Author

Thanks. Let me check.

@Lisa999
Copy link

Lisa999 commented Dec 3, 2020

This is only available in master, in release/4.2 branch:
fatal error: esp_tls_crypto.h: No such file or directory
After fixing this (get from master), it is working!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Feature Request Feature request for IDF
Projects
None yet
Development

No branches or pull requests

3 participants