mirror of
https://github.com/simon987/sist2.git
synced 2025-12-11 14:38:54 +00:00
Add support for auth0
This commit is contained in:
48
src/auth0/auth0_c_api.cpp
Normal file
48
src/auth0/auth0_c_api.cpp
Normal 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
21
src/auth0/auth0_c_api.h
Normal 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
|
||||
31
src/cli.c
31
src/cli.c
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
10
src/main.c
10
src/main.c
@@ -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)"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/*")) {
|
||||
|
||||
10
src/web/static_generated.c
vendored
10
src/web/static_generated.c
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user