Add support for auth0

This commit is contained in:
2023-01-24 19:55:16 -05:00
parent b9f008603a
commit 86ca9f1ecb
40 changed files with 8273 additions and 39304 deletions

48
src/auth0/auth0_c_api.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include "auth0_c_api.h"
#include "jwt/jwt.hpp"
#include "iostream"
#include "cjson/cJSON.h"
int auth0_verify_jwt(const char *secret_str, const char *token, const char *audience) {
using namespace jwt::params;
jwt::jwt_object object;
try {
object = jwt::decode(
token,
algorithms({"RS256"}),
secret(secret_str),
verify(true)
);
} catch (const jwt::TokenExpiredError& e) {
return AUTH0_ERR_EXPIRED;
} catch (const jwt::SignatureFormatError& e) {
return AUTH0_ERR_SIG_FORMAT;
} catch (const jwt::DecodeError& e) {
return AUTH0_ERR_DECODE;
} catch (const jwt::VerificationError& e) {
return AUTH0_ERR_VERIFICATION;
}
std::stringstream buf;
buf << object.payload();
std::string json_payload_str = buf.str();
cJSON *payload = cJSON_Parse(json_payload_str.c_str());
bool audience_ok = false;
cJSON *aud;
cJSON_ArrayForEach(aud, cJSON_GetObjectItem(payload, "aud")) {
if (aud != nullptr && strcmp(aud->valuestring, audience) == 0) {
audience_ok = true;
}
}
cJSON_Delete(payload);
if (!audience_ok) {
return AUTH0_ERR_AUDIENCE;
}
return AUTH0_OK;
}

21
src/auth0/auth0_c_api.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef SIST2_AUTH0_C_API_H
#define SIST2_AUTH0_C_API_H
#include "stdlib.h"
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
#define AUTH0_OK (0)
#define AUTH0_ERR_EXPIRED (1)
#define AUTH0_ERR_SIG_FORMAT (2)
#define AUTH0_ERR_DECODE (3)
#define AUTH0_ERR_VERIFICATION (4)
#define AUTH0_ERR_AUDIENCE (5)
EXTERNC int auth0_verify_jwt(const char *secret, const char *token, const char* audience);
#endif

View File

@@ -501,6 +501,37 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
args->tag_auth_enabled = FALSE;
}
if (args->auth0_public_key_path != NULL || args->auth0_audience != NULL || args->auth0_client_id ||
args->auth0_domain) {
if (args->auth0_public_key_path == NULL) {
fprintf(stderr, "Missing --auth0-public-key-file argument");
return 1;
}
if (args->auth0_audience == NULL) {
fprintf(stderr, "Missing --auth0-audience argument");
return 1;
}
if (args->auth0_client_id == NULL) {
fprintf(stderr, "Missing --auth0-client-id argument");
return 1;
}
if (args->auth0_domain == NULL) {
fprintf(stderr, "Missing --auth0-domain argument");
return 1;
}
}
if (args->auth0_public_key_path != NULL) {
if (load_external_file(args->auth0_public_key_path, &args->auth0_public_key) != 0) {
return 1;
}
args->auth0_enabled = TRUE;
} else {
args->auth0_enabled = FALSE;
}
args->index_count = argc - 1;
args->indices = argv + 1;

View File

@@ -77,6 +77,12 @@ typedef struct web_args {
char *lang;
char auth_user[256];
char auth_pass[256];
int auth0_enabled;
char *auth0_audience;
char *auth0_domain;
char *auth0_client_id;
char *auth0_public_key_path;
char *auth0_public_key;
int auth_enabled;
int tag_auth_enabled;
int index_count;

View File

@@ -105,6 +105,13 @@ typedef struct {
char *auth_pass;
int auth_enabled;
int tag_auth_enabled;
int auth0_enabled;
char *auth0_public_key;
char *auth0_audience;
char *auth0_domain;
char *auth0_client_id;
char *tagline;
struct index_t indices[256];
char lang[10];

View File

@@ -13,6 +13,7 @@
#include "web/serve.h"
#include "parsing/mime.h"
#include "parsing/parse.h"
#include "auth0/auth0_c_api.h"
#include <signal.h>
#include <unistd.h>
@@ -562,6 +563,11 @@ void sist2_web(web_args_t *args) {
WebCtx.tag_auth_enabled = args->tag_auth_enabled;
WebCtx.tagline = args->tagline;
WebCtx.dev = args->dev;
WebCtx.auth0_enabled = args->auth0_enabled;
WebCtx.auth0_public_key = args->auth0_public_key;
WebCtx.auth0_client_id = args->auth0_client_id;
WebCtx.auth0_domain = args->auth0_domain;
WebCtx.auth0_audience = args->auth0_audience;
strcpy(WebCtx.lang, args->lang);
for (int i = 0; i < args->index_count; i++) {
@@ -711,6 +717,10 @@ int main(int argc, const char *argv[]) {
OPT_STRING(0, "es-index", &common_es_index, "Elasticsearch index name. DEFAULT=sist2"),
OPT_STRING(0, "bind", &web_args->listen_address, "Listen on this address. DEFAULT=localhost:4090"),
OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
OPT_STRING(0, "auth0-audience", &web_args->auth0_audience, "API audience/identifier"),
OPT_STRING(0, "auth0-domain", &web_args->auth0_domain, "Application domain"),
OPT_STRING(0, "auth0-client-id", &web_args->auth0_client_id, "Application client ID"),
OPT_STRING(0, "auth0-public-key-file", &web_args->auth0_public_key_path, "Path to Auth0 public key file extracted from <domain>/pem"),
OPT_STRING(0, "tag-auth", &web_args->tag_credentials, "Basic auth in user:password format for tagging"),
OPT_STRING(0, "tagline", &web_args->tagline, "Tagline in navbar"),
OPT_BOOLEAN(0, "dev", &web_args->dev, "Serve html & js files from disk (for development)"),

View File

@@ -49,7 +49,7 @@
#include <ctype.h>
#include "git_hash.h"
#define VERSION "2.13.0"
#define VERSION "2.13.1"
static const char *const Version = VERSION;
#ifndef SIST_PLATFORM

View File

@@ -5,6 +5,7 @@
#include "static_generated.c"
#include "src/index/elastic.h"
#include "src/index/web.h"
#include "src/auth0/auth0_c_api.h"
#include <src/ctx.h>
@@ -342,6 +343,14 @@ void index_info(struct mg_connection *nc) {
cJSON_AddStringToObject(json, "sist2Hash", Sist2CommitHash);
cJSON_AddStringToObject(json, "lang", WebCtx.lang);
cJSON_AddBoolToObject(json, "dev", WebCtx.dev);
cJSON_AddBoolToObject(json, "auth0Enabled", WebCtx.auth0_enabled);
if (WebCtx.auth0_enabled) {
cJSON_AddStringToObject(json, "auth0Domain", WebCtx.auth0_domain);
cJSON_AddStringToObject(json, "auth0ClientId", WebCtx.auth0_client_id);
cJSON_AddStringToObject(json, "auth0Audience", WebCtx.auth0_audience);
}
#ifdef SIST_DEBUG
cJSON_AddBoolToObject(json, "debug", TRUE);
#else
@@ -588,6 +597,42 @@ int validate_auth(struct mg_connection *nc, struct mg_http_message *hm) {
return TRUE;
}
int check_auth0(struct mg_http_message *hm) {
struct mg_str *cookie = mg_http_get_header(hm, "Cookie");
if (cookie == NULL) {
LOG_WARNING("serve.c", "Unauthorized request (no auth cookie)")
return FALSE;
}
struct mg_str token = mg_str("");
char *token_str = NULL;
token = mg_http_get_header_var(*cookie, mg_str("sist2-auth0"));
if (token.len == 0) {
LOG_WARNING("serve.c", "Unauthorized request (no auth cookie)")
return FALSE;
}
token_str = malloc(token.len + 1);
strncpy(token_str, token.ptr, token.len);
*(token_str + token.len) = '\0';
int res = auth0_verify_jwt(
WebCtx.auth0_public_key,
token_str,
WebCtx.auth0_audience
);
free(token_str);
if (res != AUTH0_OK) {
LOG_WARNINGF("serve.c", "Unauthorized request (JWT validation error: %d)", res);
return FALSE;
}
return TRUE;
}
static void ev_router(struct mg_connection *nc, int ev, void *ev_data, UNUSED(void *fn_data)) {
if (ev == MG_EV_HTTP_MSG) {
@@ -606,20 +651,34 @@ static void ev_router(struct mg_connection *nc, int ev, void *ev_data, UNUSED(vo
if (mg_http_match_uri(hm, "/")) {
search_index(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/favicon.ico")) {
favicon(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/css/index.css")) {
style(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/css/chunk-vendors.css")) {
style_vendor(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/js/index.js")) {
javascript(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/js/chunk-vendors.js")) {
javascript_vendor(nc, hm);
} else if (mg_http_match_uri(hm, "/es")) {
search(nc, hm);
return;
} else if (mg_http_match_uri(hm, "/i")) {
index_info(nc);
return;
}
if (WebCtx.auth0_enabled && !check_auth0(hm)) {
mg_http_reply(nc, 403, HTTP_SERVER_HEADER HTTP_TEXT_TYPE_HEADER, "Unauthorized (auth0 error)");
return;
}
if (mg_http_match_uri(hm, "/es")) {
search(nc, hm);
} else if (mg_http_match_uri(hm, "/status")) {
status(nc);
} else if (mg_http_match_uri(hm, "/f/*")) {

File diff suppressed because one or more lines are too long