From 121682c006a7627c1f6975385688deb5c95c0c61 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Mon, 16 Jan 2023 21:17:44 +0000 Subject: [PATCH] Implement user login. --- src/Routes/RouteRegister.c | 25 +++++- src/User.c | 165 ++++++++++++++++++++++++++++++++++--- src/include/User.h | 16 +++- 3 files changed, 192 insertions(+), 14 deletions(-) diff --git a/src/Routes/RouteRegister.c b/src/Routes/RouteRegister.c index 7a06791..8c346c5 100644 --- a/src/Routes/RouteRegister.c +++ b/src/Routes/RouteRegister.c @@ -202,11 +202,32 @@ ROUTE_IMPL(RouteRegister, args) user = UserCreate(db, username, password); response = HashMapCreate(); - HashMapSet(response, "user_id", JsonValueString(StrConcat(4, "@", UserGetName(user), ":", args->matrixArgs->config->serverName))); + HashMapSet(response, "user_id", JsonValueString(StrConcat(4, "@", + UserGetName(user), ":", args->matrixArgs->config->serverName))); HttpResponseStatus(args->context, HTTP_OK); if (!inhibitLogin) { - /* TODO: Log in user here and attach auth info to response */ + UserLoginInfo *loginInfo = UserLogin(user, password, deviceId, + initialDeviceDisplayName, refreshToken); + + HashMapSet(response, "access_token", + JsonValueString(loginInfo->accessToken)); + HashMapSet(response, "device_id", + JsonValueString(loginInfo->deviceId)); + + if (refreshToken) + { + HashMapSet(response, "expires_in_ms", + JsonValueInteger(loginInfo->tokenLifetime)); + HashMapSet(response, "refresh_token", + JsonValueString(loginInfo->refreshToken)); + } + + /* + * Don't need to free members; they're attached to the JSON response, + * they will be freed after the response is sent. + */ + Free(loginInfo); } Log(lc, LOG_INFO, "Registered user '%s'", UserGetName(user)); diff --git a/src/User.c b/src/User.c index cfd9ee0..b302cc2 100644 --- a/src/User.c +++ b/src/User.c @@ -186,22 +186,128 @@ UserCreate(Db * db, char *name, char *password) hash = Sha256(tmpstr); Free(tmpstr); HashMapSet(json, "salt", JsonValueString(salt)); - HashMapSet(json, "hash", JsonValueString(hash)); + HashMapSet(json, "password", JsonValueString(hash)); - HashMapSet(json, "created_on", JsonValueInteger(ts)); - HashMapSet(json, "last_updated", JsonValueInteger(ts)); + HashMapSet(json, "createdOn", JsonValueInteger(ts)); + HashMapSet(json, "deactivated", JsonValueBoolean(0)); return user; } -void -UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName) +UserLoginInfo * +UserLogin(User * user, char *password, char *deviceId, char *deviceDisplayName, + int withRefresh) { - /* TODO: Implement login */ - (void) user; - (void) password; - (void) deviceId; - (void) deviceDisplayName; + DbRef *atRef; + DbRef *rtRef = NULL; + + HashMap *devices; + HashMap *device; + + UserLoginInfo *result; + + if (!user || !password) + { + return NULL; + } + + if (!UserCheckPassword(user, password)) + { + return NULL; + } + + result = Malloc(sizeof(UserLoginInfo)); + if (!result) + { + return NULL; + } + + result->refreshToken = NULL; + result->tokenLifetime = 0; + + /* Generate an access token */ + result->accessToken = StrRandom(64); + atRef = DbCreate(user->db, 3, "tokens", "access", result->accessToken); + + HashMapSet(DbJson(atRef), "user", JsonValueString(StrDuplicate(user->name))); + + if (withRefresh) + { + unsigned long ts = UtilServerTs(); + + result->tokenLifetime = 1000 * 60 * 60 * 24 * 7; /* 1 Week */ + + result->refreshToken = StrRandom(64); + rtRef = DbCreate(user->db, 3, "tokens", "refresh", result->refreshToken); + + HashMapSet(DbJson(rtRef), "refreshes", + JsonValueString(StrDuplicate(result->accessToken))); + HashMapSet(DbJson(atRef), "expires", JsonValueInteger(ts + result->tokenLifetime)); + DbUnlock(user->db, rtRef); + } + + if (!deviceId) + { + result->deviceId = StrRandom(10); + } + else + { + result->deviceId = StrDuplicate(deviceId); + } + + devices = JsonValueAsObject(HashMapGet(DbJson(user->ref), "devices")); + if (!devices) + { + devices = HashMapCreate(); + HashMapSet(DbJson(user->ref), "devices", JsonValueObject(devices)); + } + + device = JsonValueAsObject(HashMapGet(devices, result->deviceId)); + + if (device) + { + JsonValue *val; + + val = HashMapDelete(device, "accessToken"); + if (val) + { + DbDelete(user->db, 3, "tokens", "access", JsonValueAsString(val)); + JsonValueFree(val); + } + + val = HashMapDelete(device, "refreshToken"); + if (val) + { + DbDelete(user->db, 3, "tokens", "refresh", JsonValueAsString(val)); + JsonValueFree(val); + } + } + else + { + device = HashMapCreate(); + HashMapSet(devices, StrDuplicate(result->deviceId), JsonValueObject(device)); + + if (deviceDisplayName) + { + HashMapSet(device, "displayName", + JsonValueString(StrDuplicate(deviceDisplayName))); + } + + } + + if (result->refreshToken) + { + HashMapSet(device, "refreshToken", + JsonValueString(StrDuplicate(result->refreshToken))); + } + + HashMapSet(device, "accessToken", + JsonValueString(StrDuplicate(result->accessToken))); + + HashMapSet(DbJson(atRef), "device", JsonValueString(StrDuplicate(result->deviceId))); + DbUnlock(user->db, atRef); + + return result; } char * @@ -209,3 +315,42 @@ UserGetName(User * user) { return user ? user->name : NULL; } + +int +UserCheckPassword(User * user, char *password) +{ + HashMap *json; + + char *storedHash; + char *salt; + + char *hashedPwd; + char *tmp; + + int result; + + if (!user || !password) + { + return 0; + } + + json = DbJson(user->ref); + + storedHash = JsonValueAsString(HashMapGet(json, "password")); + salt = JsonValueAsString(HashMapGet(json, "salt")); + + if (!storedHash || !salt) + { + return 0; + } + + tmp = StrConcat(2, password, salt); + hashedPwd = Sha256(tmp); + Free(tmp); + + result = strcmp(hashedPwd, storedHash) == 0; + + Free(hashedPwd); + + return result; +} diff --git a/src/include/User.h b/src/include/User.h index d047519..a4bbc40 100644 --- a/src/include/User.h +++ b/src/include/User.h @@ -28,6 +28,14 @@ typedef struct User User; +typedef struct UserLoginInfo +{ + char *accessToken; + char *refreshToken; + char *deviceId; + long tokenLifetime; +} UserLoginInfo; + extern int UserValidate(char *, char *); @@ -46,10 +54,14 @@ extern User * extern int UserUnlock(User *); -extern void - UserLogin(User *, char *password, char *deviceId, char *deviceDisplayName); +extern UserLoginInfo * +UserLogin(User *, char *password, char *deviceId, char *deviceDisplayName, + int withRefresh); extern char * UserGetName(User *); +extern int + UserCheckPassword(User *, char *); + #endif /* TELODENDRIA_USER_H */