auth sessions

This commit is contained in:
SimonFJ20 2025-03-12 12:24:31 +01:00
parent 81ec1a6469
commit 3978ed8204
8 changed files with 173 additions and 16 deletions

View File

@ -21,6 +21,10 @@ void route_get_products_all(HttpCtx* ctx);
void route_post_user_register(HttpCtx* ctx);
void route_post_auth_login(HttpCtx* ctx);
void route_post_auth_logout(HttpCtx* ctx);
const Session* header_session(HttpCtx* ctx);
const Session* middleware_session(HttpCtx* ctx);
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
{ \
@ -47,8 +51,7 @@ void route_post_auth_login(HttpCtx* ctx);
#define RESPOND_SERVER_ERROR(HTTP_CTX) \
RESPOND_JSON(HTTP_CTX, 500, "{\"ok\":false,\"msg\":\"server error\"}")
__attribute__((unused))
static inline void ___include_user(void)
__attribute__((unused)) static inline void ___include_user(void)
{
RESPOND((HttpCtx*)0, 200, "text/html", "")
}

View File

@ -41,13 +41,46 @@ void route_post_auth_login(HttpCtx* ctx)
goto l2_return;
}
session_vec_remove_user_id(&cx->sessions, user.id);
char* token = str_random(64);
session_vec_add(&cx->sessions, user.id, token);
sessions_remove(&cx->sessions, user.id);
Session* session = sessions_add(&cx->sessions, user.id);
RESPOND_JSON(ctx, 200, "{\"ok\":true,\"token\":\"%s\"}", token);
RESPOND_JSON(ctx, 200, "{\"ok\":true,\"token\":\"%s\"}", session->token);
l2_return:
user_destroy(&user);
l0_return:
auth_login_req_destroy(&req);
}
void route_post_auth_logout(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
const Session* session = header_session(ctx);
if (!session) {
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
return;
}
sessions_remove(&cx->sessions, session->user_id);
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
}
const Session* header_session(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
if (!http_ctx_req_headers_has(ctx, "Session-Token")) {
return NULL;
}
const char* token = http_ctx_req_headers_get(ctx, "Session-Token");
// session expiration should be handled here
return sessions_find(&cx->sessions, token);
}
// Returns NULL AND responds if no valid session is found.
const Session* middleware_session(HttpCtx* ctx)
{
const Session* session = header_session(ctx);
if (!session) {
RESPOND_JSON(ctx, 200, "{\"ok\":false,\"msg\":\"unauthorized\"}");
return NULL;
}
return session;
}

View File

@ -1,6 +1,7 @@
#include "http_server.h"
#include "http_server_internal.h"
#include "str_util.h"
#include <ctype.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>
@ -358,10 +359,19 @@ static inline int parse_header(
return -1;
}
if (method_str.len >= 8) {
fprintf(stderr, "error: malformed http method\n");
return -1;
}
char normalized_method[8] = "";
for (size_t i = 0; i < method_str.len; ++i) {
normalized_method[i] = (char)toupper(method_str.ptr[i]);
}
Method method;
if (strncmp(method_str.ptr, "GET", method_str.len) == 0) {
if (strncmp(normalized_method, "GET", method_str.len) == 0) {
method = Method_GET;
} else if (strncmp(method_str.ptr, "POST", method_str.len) == 0) {
} else if (strncmp(normalized_method, "POST", method_str.len) == 0) {
method = Method_POST;
} else {
fprintf(stderr, "error: unrecognized http method '%.*s'\n",

View File

@ -46,6 +46,7 @@ int main(void)
http_server_post(server, "/api/users/register", route_post_user_register);
http_server_post(server, "/api/auth/login", route_post_auth_login);
http_server_post(server, "/api/auth/logout", route_post_auth_logout);
http_server_get(server, "/", route_get_index);
http_server_post(server, "/set_number", route_post_set_number);

View File

@ -1,28 +1,54 @@
#include "session.h"
#include "str_util.h"
#include <stdlib.h>
void session_construct(Session* session, int64_t user_id)
{
char* token = str_random(64);
size_t token_hash = str_fast_hash(token);
*session = (Session) { user_id, token, token_hash };
}
void session_destroy(Session* session)
{
free(session->token);
*session = (Session) {
.user_id = 0,
.token = NULL,
.token_hash = 0,
};
}
void session_vec_remove_user_id(SessionVec* vec, int64_t user_id)
void sessions_remove(SessionVec* vec, int64_t user_id)
{
for (size_t i = 0; i < vec->size; ++i) {
if (vec->data[i].user_id == user_id) {
session_destroy(&vec->data[i]);
vec->data[i] = (Session) { 0, NULL };
}
}
}
void session_vec_add(SessionVec* vec, int64_t user_id, char* token)
Session* sessions_add(SessionVec* vec, int64_t user_id)
{
for (size_t i = 0; i < vec->size; ++i) {
if (vec->data[i].user_id == 0) {
vec->data[i] = (Session) { user_id, token };
return;
session_construct(&vec->data[i], user_id);
return &vec->data[i];
}
}
session_vec_push(vec, (Session) { user_id, token });
Session session;
session_construct(&session, user_id);
session_vec_push(vec, session);
return &vec->data[vec->size - 1];
}
const Session* sessions_find(SessionVec* vec, const char* token)
{
size_t token_hash = str_fast_hash(token);
for (size_t i = 0; i < vec->size; ++i) {
if (vec->data[i].token_hash == token_hash) {
return &vec->data[i];
}
}
return NULL;
}

View File

@ -6,11 +6,14 @@
typedef struct {
int64_t user_id;
char* token;
size_t token_hash;
} Session;
void session_construct(Session* session, int64_t user_id);
void session_destroy(Session* session);
DEFINE_VEC(Session, SessionVec, session_vec, 16)
void session_vec_remove_user_id(SessionVec* vec, int64_t user_id);
void session_vec_add(SessionVec* vec, int64_t user_id, char* token);
void sessions_remove(SessionVec* vec, int64_t user_id);
Session* sessions_add(SessionVec* vec, int64_t user_id);
const Session* sessions_find(SessionVec* vec, const char* token);

View File

@ -164,6 +164,85 @@ bool str_hash_equal(const char* hash, const char* input)
return hashdata_is_equal(data, input);
}
static inline uint64_t chibihash64__load32le(const uint8_t* p)
{
return (uint64_t)p[0] << 0 | (uint64_t)p[1] << 8 | (uint64_t)p[2] << 16
| (uint64_t)p[3] << 24;
}
static inline uint64_t chibihash64__load64le(const uint8_t* p)
{
return chibihash64__load32le(p) | (chibihash64__load32le(p + 4) << 32);
}
static inline uint64_t chibihash64__rotl(uint64_t x, int n)
{
return (x << n) | (x >> (-n & 63));
}
static inline uint64_t chibihash64(
const void* keyIn, ptrdiff_t len, uint64_t seed)
{
const uint8_t* p = (const uint8_t*)keyIn;
ptrdiff_t l = len;
const uint64_t K = UINT64_C(0x2B7E151628AED2A7); // digits of e
uint64_t seed2
= chibihash64__rotl(seed - K, 15) + chibihash64__rotl(seed - K, 47);
uint64_t h[4] = { seed, seed + K, seed2, seed2 + (K * K ^ K) };
// depending on your system unrolling might (or might not) make things
// a tad bit faster on large strings. on my system, it actually makes
// things slower.
// generally speaking, the cost of bigger code size is usually not
// worth the trade-off since larger code-size will hinder inlinability
// but depending on your needs, you may want to uncomment the pragma
// below to unroll the loop.
// #pragma GCC unroll 2
for (; l >= 32; l -= 32) {
for (int i = 0; i < 4; ++i, p += 8) {
uint64_t stripe = chibihash64__load64le(p);
h[i] = (stripe + h[i]) * K;
h[(i + 1) & 3] += chibihash64__rotl(stripe, 27);
}
}
for (; l >= 8; l -= 8, p += 8) {
h[0] ^= chibihash64__load32le(p + 0);
h[0] *= K;
h[1] ^= chibihash64__load32le(p + 4);
h[1] *= K;
}
if (l >= 4) {
h[2] ^= chibihash64__load32le(p);
h[3] ^= chibihash64__load32le(p + l - 4);
} else if (l > 0) {
h[2] ^= p[0];
h[3] ^= p[l / 2] | ((uint64_t)p[l - 1] << 8);
}
h[0] += chibihash64__rotl(h[2] * K, 31) ^ (h[2] >> 31);
h[1] += chibihash64__rotl(h[3] * K, 31) ^ (h[3] >> 31);
h[0] *= K;
h[0] ^= h[0] >> 31;
h[1] += h[0];
uint64_t x = (uint64_t)len * K;
x ^= chibihash64__rotl(x, 29);
x += seed;
x ^= h[1];
x ^= chibihash64__rotl(x, 15) ^ chibihash64__rotl(x, 42);
x *= K;
x ^= chibihash64__rotl(x, 13) ^ chibihash64__rotl(x, 31);
return x;
}
uint64_t str_fast_hash(const char* input)
{
return chibihash64(input, sizeof(char*), 0x80085);
}
char* str_random(size_t length)
{
char* string = calloc(length + 1, sizeof(char));

View File

@ -38,6 +38,8 @@ DEFINE_VEC(char*, RawStrVec, rawstr_vec, 8)
char* str_hash(const char* input);
bool str_hash_equal(const char* hash, const char* input);
uint64_t str_fast_hash(const char* input);
char* str_random(size_t length);
void str_util_test(void);