mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-02 14:32:56 +07:00
109 lines
4.3 KiB
C
109 lines
4.3 KiB
C
// Copyright (c) 2026 Cesanta Software Limited
|
|
// All rights reserved
|
|
//
|
|
// HTTP server example. It issues and verifies JWT Bearer tokens.
|
|
|
|
#include "mongoose.h"
|
|
|
|
static const char *s_listen_on = "http://0.0.0.0:8000";
|
|
static const char *s_secret = "change-me";
|
|
enum { TOKEN_LEN = 1024, CLAIMS_LEN = 256, JWT_LEN = 1024 };
|
|
static const char *s_ec_private_key =
|
|
"-----BEGIN EC PRIVATE KEY-----\n"
|
|
"MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n"
|
|
"AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n"
|
|
"4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n"
|
|
"-----END EC PRIVATE KEY-----\n";
|
|
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
static size_t verify_es256(struct mg_str token, char *claims, size_t len) {
|
|
uint8_t key[32], pub[64];
|
|
struct mg_jwt_opts opts = {.public_key = pub};
|
|
// This keeps the example self-contained. A real verifier would usually keep
|
|
// only the issuer public key, selected by kid, not the signing private key.
|
|
if (mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)) !=
|
|
32 ||
|
|
mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) != 1) {
|
|
return 0;
|
|
}
|
|
return mg_jwt_verify_es256(token, &opts, claims, len);
|
|
}
|
|
#endif
|
|
|
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
|
if (ev == MG_EV_HTTP_MSG) {
|
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
char user[1], *token, *claims, *jwt;
|
|
token = (char *) mg_calloc(1, TOKEN_LEN);
|
|
claims = (char *) mg_calloc(1, CLAIMS_LEN);
|
|
jwt = (char *) mg_calloc(1, JWT_LEN);
|
|
if (token == NULL || claims == NULL || jwt == NULL) {
|
|
mg_http_reply(c, 500, "", "OOM\n");
|
|
mg_free(token);
|
|
mg_free(claims);
|
|
mg_free(jwt);
|
|
return;
|
|
}
|
|
mg_http_creds(hm, user, sizeof(user), token, TOKEN_LEN);
|
|
|
|
if (mg_match(hm->uri, mg_str("/login-hs256"), NULL)) {
|
|
struct mg_jwt_opts opts = {.kid = mg_str("hmac-key-1"),
|
|
.secret = mg_str(s_secret)};
|
|
size_t n;
|
|
mg_snprintf(claims, CLAIMS_LEN, "{%m:%m,%m:%m}", MG_ESC("sub"),
|
|
MG_ESC("admin"), MG_ESC("scope"),
|
|
MG_ESC("dash:read dash:write"));
|
|
opts.claims = mg_str(claims);
|
|
n = mg_jwt_sign_hs256(&opts, jwt, JWT_LEN);
|
|
mg_http_reply(c, n == 0 ? 500 : 200, "Content-Type: application/json\r\n",
|
|
"{%m:%m}\n", MG_ESC("token"), MG_ESC(n == 0 ? "" : jwt));
|
|
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
} else if (mg_match(hm->uri, mg_str("/login-es256"), NULL)) {
|
|
uint8_t key[32];
|
|
struct mg_jwt_opts opts = {.kid = mg_str("ec-key-1"),
|
|
.private_key = key};
|
|
size_t keysz, n;
|
|
keysz =
|
|
mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key));
|
|
// ES256 uses a raw 64-byte r || s signature. mg_jwt_sign_es256() signs it
|
|
// deterministically; applications can also hash the signing input and
|
|
// call mg_uecc_sign() directly if they need random-nonce ECDSA.
|
|
mg_snprintf(claims, CLAIMS_LEN, "{%m:%m,%m:%m}", MG_ESC("sub"),
|
|
MG_ESC("admin"), MG_ESC("scope"), MG_ESC("dash:read"));
|
|
opts.claims = mg_str(claims);
|
|
n = keysz == 32 ? mg_jwt_sign_es256(&opts, jwt, JWT_LEN) : 0;
|
|
mg_http_reply(c, n == 0 ? 500 : 200, "Content-Type: application/json\r\n",
|
|
"{%m:%m}\n", MG_ESC("token"), MG_ESC(n == 0 ? "" : jwt));
|
|
#endif
|
|
|
|
} else {
|
|
struct mg_jwt_opts opts = {.secret = mg_str(s_secret)};
|
|
if (mg_jwt_verify_hs256(mg_str(token), &opts, claims, CLAIMS_LEN) > 0) {
|
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n",
|
|
claims);
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
} else if (verify_es256(mg_str(token), claims, CLAIMS_LEN) > 0) {
|
|
mg_http_reply(c, 200, "Content-Type: application/json\r\n", "%s\n",
|
|
claims);
|
|
#endif
|
|
} else {
|
|
mg_http_reply(c, 401, "WWW-Authenticate: Bearer\r\n", "Unauthorized\n");
|
|
}
|
|
}
|
|
mg_free(token);
|
|
mg_free(claims);
|
|
mg_free(jwt);
|
|
}
|
|
}
|
|
|
|
int main(void) {
|
|
struct mg_mgr mgr;
|
|
mg_log_set(MG_LL_DEBUG);
|
|
mg_mgr_init(&mgr); // Initialise event manager
|
|
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
|
|
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
|
mg_mgr_free(&mgr);
|
|
return 0;
|
|
}
|