|
@@ -1,6 +1,7 @@
|
|
|
#include "jwt_utils.hpp"
|
|
#include "jwt_utils.hpp"
|
|
|
#include "common/time_utils.hpp"
|
|
#include "common/time_utils.hpp"
|
|
|
#include "common/string_utils.hpp"
|
|
#include "common/string_utils.hpp"
|
|
|
|
|
+#include "common/uuid.hpp"
|
|
|
#include <openssl/hmac.h>
|
|
#include <openssl/hmac.h>
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/evp.h>
|
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/crypto.h>
|
|
@@ -17,7 +18,8 @@ nlohmann::json TokenPayload::toJson() const {
|
|
|
{"role", role},
|
|
{"role", role},
|
|
|
{"iat", issued_at},
|
|
{"iat", issued_at},
|
|
|
{"exp", expires_at},
|
|
{"exp", expires_at},
|
|
|
- {"type", token_type}
|
|
|
|
|
|
|
+ {"type", token_type},
|
|
|
|
|
+ {"jti", jti}
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -29,6 +31,10 @@ TokenPayload TokenPayload::fromJson(const nlohmann::json& j) {
|
|
|
payload.issued_at = j.value("iat", int64_t{0});
|
|
payload.issued_at = j.value("iat", int64_t{0});
|
|
|
payload.expires_at = j.value("exp", int64_t{0});
|
|
payload.expires_at = j.value("exp", int64_t{0});
|
|
|
payload.token_type = j.value("type", "");
|
|
payload.token_type = j.value("type", "");
|
|
|
|
|
+ // Missing on any token minted before jti existed - defaults to empty
|
|
|
|
|
+ // rather than being rejected, so every session issued before this change
|
|
|
|
|
+ // does not go instantly invalid.
|
|
|
|
|
+ payload.jti = j.value("jti", "");
|
|
|
return payload;
|
|
return payload;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -47,6 +53,10 @@ std::string JwtUtils::generateAccessToken(const std::string& user_id,
|
|
|
payload.issued_at = now;
|
|
payload.issued_at = now;
|
|
|
payload.expires_at = now + config_.access_token_lifetime_sec;
|
|
payload.expires_at = now + config_.access_token_lifetime_sec;
|
|
|
payload.token_type = "access";
|
|
payload.token_type = "access";
|
|
|
|
|
+ // Same collision as the refresh token: two access tokens minted for the
|
|
|
|
|
+ // same user in the same second would otherwise be identical too (e.g. a
|
|
|
|
|
+ // login followed immediately by a refresh).
|
|
|
|
|
+ payload.jti = common::UUID::generate();
|
|
|
|
|
|
|
|
return generateToken(payload);
|
|
return generateToken(payload);
|
|
|
}
|
|
}
|
|
@@ -63,6 +73,10 @@ std::string JwtUtils::generateRefreshToken(const std::string& user_id,
|
|
|
payload.issued_at = now;
|
|
payload.issued_at = now;
|
|
|
payload.expires_at = now + config_.refresh_token_lifetime_sec;
|
|
payload.expires_at = now + config_.refresh_token_lifetime_sec;
|
|
|
payload.token_type = "refresh";
|
|
payload.token_type = "refresh";
|
|
|
|
|
+ // A random UUID (backed by OpenSSL RAND_bytes, see common::UUID), not a
|
|
|
|
|
+ // counter or a finer-grained timestamp - the goal is a value nobody can
|
|
|
|
|
+ // predict or coincide with, not merely one with more digits.
|
|
|
|
|
+ payload.jti = common::UUID::generate();
|
|
|
|
|
|
|
|
return generateToken(payload);
|
|
return generateToken(payload);
|
|
|
}
|
|
}
|