Adding refresh token processing.

This commit is contained in:
stephb9959
2022-04-25 22:55:37 -07:00
parent bdda1aff35
commit 6e72c28b3e
10 changed files with 147 additions and 8 deletions

2
build
View File

@@ -1 +1 @@
43
45

View File

@@ -66,6 +66,7 @@ components:
- 11 # BAD_MFA_TRANSACTION
- 12 # MFA_FAILURE
- 13 # SECURITY_SERVICE_UNREACHABLE
- 14 # CANNOT REFRESH TOKEN
ErrorDetails:
type: string
ErrorDescription:

View File

@@ -45,6 +45,7 @@ namespace OpenWifi {
int AuthService::Start() {
Logger().notice("Starting...");
TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60);
RefreshTokenLifeSpan_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.refresh_token.lifespan", 90 * 24 * 60 * 600);
HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5);
AccessPolicy_ = MicroService::instance().ConfigPath("openwifi.document.policy.access", "/wwwassets/access_policy.html");
@@ -62,7 +63,83 @@ namespace OpenWifi {
Logger().notice("Stopping...");
}
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired )
bool AuthService::RefreshUserToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI) {
try {
std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
return false;
}
uint64_t RevocationDate=0;
std::string UserId;
if(StorageService()->UserTokenDB().GetToken(CallToken, UI.webtoken, UserId, RevocationDate) && UI.webtoken.refresh_token_==RefreshToken) {
auto now = OpenWifi::Now();
// Create a new token
auto NewToken = GenerateTokenHMAC( UI.webtoken.access_token_, CUSTOM);
auto NewRefreshToken = RefreshToken;
if(now - UI.webtoken.lastRefresh_ < RefreshTokenLifeSpan_) {
NewRefreshToken = GenerateTokenHMAC( UI.webtoken.refresh_token_, CUSTOM);
UI.webtoken.lastRefresh_ = now;
}
StorageService()->UserTokenDB().RefreshToken(CallToken, NewToken, NewRefreshToken, UI.webtoken.lastRefresh_ );
UI.webtoken.access_token_ = NewToken;
UI.webtoken.refresh_token_ = NewRefreshToken;
return true;
}
return false;
} catch (...) {
}
return false;
}
bool AuthService::RefreshSubToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI) {
try {
std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
return false;
}
uint64_t RevocationDate=0;
std::string UserId;
if(StorageService()->SubTokenDB().GetToken(CallToken, UI.webtoken, UserId, RevocationDate) && UI.webtoken.refresh_token_==RefreshToken) {
auto now = OpenWifi::Now();
// Create a new token
auto NewToken = GenerateTokenHMAC( UI.webtoken.access_token_, CUSTOM);
auto NewRefreshToken = RefreshToken;
if(now - UI.webtoken.lastRefresh_ < RefreshTokenLifeSpan_) {
NewRefreshToken = GenerateTokenHMAC( UI.webtoken.refresh_token_, CUSTOM);
UI.webtoken.lastRefresh_ = now;
}
StorageService()->SubTokenDB().RefreshToken(CallToken, NewToken, NewRefreshToken, UI.webtoken.lastRefresh_ );
UI.webtoken.access_token_ = NewToken;
UI.webtoken.refresh_token_ = NewRefreshToken;
return true;
}
return false;
} catch (...) {
}
return false;
}
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired )
{
std::lock_guard Guard(Mutex_);
Expired = false;

View File

@@ -113,6 +113,9 @@ namespace OpenWifi{
inline const std::string & GetSubPasswordPolicy() const { return SubPasswordPolicy_; }
inline const std::string & GetSubAccessPolicy() const { return SubAccessPolicy_; }
bool RefreshUserToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI);
bool RefreshSubToken(Poco::Net::HTTPServerRequest & Request, const std::string & RefreshToken, SecurityObjects::UserInfoAndPolicy & UI);
private:
Poco::SHA2Engine SHA2_;
@@ -125,8 +128,9 @@ namespace OpenWifi{
std::regex PasswordValidation_;
std::regex SubPasswordValidation_;
uint64_t TokenAging_ = 30 * 24 * 60 * 60;
uint64_t TokenAging_ = 15 * 24 * 60 * 60;
uint64_t HowManyOldPassword_=5;
uint64_t RefreshTokenLifeSpan_ = 90 * 24 * 60 * 60 ;
class SHA256Engine : public Poco::Crypto::DigestEngine
{

View File

@@ -60,9 +60,22 @@ namespace OpenWifi {
auto userId = GetS(RESTAPI::Protocol::USERID, Obj);
auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj);
auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj);
auto refreshToken = GetS("refresh_token", Obj);
auto grant_type = GetParameter("grant_type");
Poco::toLowerInPlace(userId);
if(!refreshToken.empty() && grant_type == "refresh_token") {
SecurityObjects::UserInfoAndPolicy UInfo;
if(AuthService()->RefreshUserToken(*Request, refreshToken, UInfo)) {
Poco::JSON::Object Answer;
UInfo.webtoken.to_json(Answer);
return ReturnObject(Answer);
} else {
return UnAuthorized(RESTAPI::Errors::InvalidCredentials, CANNOT_REFRESH_TOKEN);
}
}
if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS, false)) {
Logger_.information(fmt::format("POLICY-REQUEST({}): Request.", Request->clientAddress().toString()));
Poco::JSON::Object Answer;

View File

@@ -113,6 +113,8 @@ namespace OpenWifi::SecurityObjects {
field_to_json(Obj,"userMustChangePassword",userMustChangePassword);
field_to_json(Obj,"errorCode", errorCode);
Obj.set("aclTemplate",AclTemplateObj);
field_to_json(Obj,"errorCode", errorCode);
field_to_json(Obj,"lastRefresh", lastRefresh_);
}
bool WebToken::from_json(const Poco::JSON::Object::Ptr &Obj) {
@@ -129,6 +131,7 @@ namespace OpenWifi::SecurityObjects {
field_from_json(Obj, "created", created_);
field_from_json(Obj, "username", username_);
field_from_json(Obj, "userMustChangePassword",userMustChangePassword);
field_from_json(Obj,"lastRefresh", lastRefresh_);
return true;
} catch (...) {
std::cout << "Cannot parse: WebToken" << std::endl;
@@ -588,6 +591,7 @@ namespace OpenWifi::SecurityObjects {
field_to_json(Obj,"expires",expires);
field_to_json(Obj,"idleTimeout",idleTimeout);
field_to_json(Obj,"revocationDate",revocationDate);
field_to_json(Obj,"lastRefresh", lastRefresh);
}
bool Token::from_json(const Poco::JSON::Object::Ptr &Obj) {
@@ -600,6 +604,7 @@ namespace OpenWifi::SecurityObjects {
field_from_json(Obj,"expires",expires);
field_from_json(Obj,"idleTimeout",idleTimeout);
field_from_json(Obj,"revocationDate",revocationDate);
field_from_json(Obj,"lastRefresh", lastRefresh);
return true;
} catch(...) {
std::cout << "Cannot parse: Token" << std::endl;

View File

@@ -41,6 +41,7 @@ namespace OpenWifi {
uint64_t idle_timeout_=0;
AclTemplate acl_template_;
uint64_t created_=0;
uint64_t lastRefresh_=0;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(const Poco::JSON::Object::Ptr &Obj);
@@ -292,6 +293,7 @@ namespace OpenWifi {
uint64_t expires=0;
uint64_t idleTimeout=0;
uint64_t revocationDate=0;
uint64_t lastRefresh=0;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(const Poco::JSON::Object::Ptr &Obj);

View File

@@ -104,7 +104,8 @@ namespace OpenWifi {
RATE_LIMIT_EXCEEDED,
BAD_MFA_TRANSACTION,
MFA_FAILURE,
SECURITY_SERVICE_UNREACHABLE
SECURITY_SERVICE_UNREACHABLE,
CANNOT_REFRESH_TOKEN
};
class AppServiceRegistry {
@@ -652,7 +653,8 @@ namespace OpenWifi::Utils {
[[nodiscard]] inline bool ValidUUID(const std::string &UUID) {
if(UUID.size()>36)
return false;
return (std::all_of(UUID.begin(),UUID.end(),[](auto i){return i=='-' || std::isxdigit(i);}));
uint dashes=0;
return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0);
}
[[nodiscard]] inline std::vector<std::string> Split(const std::string &List, char Delimiter=',' ) {

View File

@@ -26,7 +26,8 @@ namespace OpenWifi {
ORM::Field{"created", ORM::FieldType::FT_BIGINT},
ORM::Field{"expires", ORM::FieldType::FT_BIGINT},
ORM::Field{"idleTimeOut", ORM::FieldType::FT_BIGINT},
ORM::Field{"revocationDate", ORM::FieldType::FT_BIGINT}
ORM::Field{"revocationDate", ORM::FieldType::FT_BIGINT},
ORM::Field{"lastRefresh", ORM::FieldType::FT_BIGINT}
};
static ORM::IndexVec MakeIndices(const std::string &shortname) {
@@ -53,6 +54,17 @@ namespace OpenWifi {
return CreateRecord(T);
}
bool BaseTokenDB::Upgrade([[maybe_unused]] uint32_t from, uint32_t &to) {
std::vector<std::string> Statements{
"alter table " + TableName_ + " add column lastRefresh BIGINT default 0;"
};
RunScript(Statements);
to = 1;
return true;
return true;
}
bool BaseTokenDB::GetToken(std::string &Token, SecurityObjects::WebToken &WT, std::string & UserId, uint64_t &RevocationDate) {
SecurityObjects::Token T;
@@ -64,6 +76,7 @@ namespace OpenWifi {
WT.created_ = T.created;
WT.expires_in_ = T.expires;
WT.idle_timeout_ = T.idleTimeout;
WT.lastRefresh_ = T.lastRefresh;
RevocationDate = T.revocationDate;
UserId = T.userName;
return true;
@@ -71,6 +84,22 @@ namespace OpenWifi {
return false;
}
bool BaseTokenDB::RefreshToken(const std::string &OldToken, const std::string &NewToken, const std::string &NewRefreshToken, uint64_t LastRefresh ) {
SecurityObjects::Token T;
if(GetRecord("token", OldToken, T)) {
T.token = NewToken;
T.refreshToken = NewRefreshToken;
T.lastRefresh = LastRefresh;
T.created = OpenWifi::Now();
UpdateRecord("token",OldToken,T);
Cache_->Delete("token",OldToken);
Cache_->UpdateCache(T);
return true;
}
return false;
}
bool BaseTokenDB::IsTokenRevoked(std::string &Token) {
SecurityObjects::Token T;
@@ -163,6 +192,7 @@ template<> void ORM::DB<OpenWifi::TokenRecordTuple,
U.expires = T.get<5>();
U.idleTimeout = T.get<6>();
U.revocationDate = T.get<7>();
U.lastRefresh = T.get<8>();
}
template<> void ORM::DB< OpenWifi::TokenRecordTuple,
@@ -176,4 +206,5 @@ template<> void ORM::DB< OpenWifi::TokenRecordTuple,
T.set<5>(U.expires);
T.set<6>(U.idleTimeout);
T.set<7>(U.revocationDate);
T.set<8>(U.lastRefresh);
}

View File

@@ -28,7 +28,8 @@ namespace OpenWifi {
uint64_t, // Created = 0;
uint64_t, // Expires = 0;
uint64_t, // IdleTimeOut = 0;
uint64_t // RevocationDate = 0;
uint64_t, // RevocationDate = 0;
uint64_t // lastRefresh
> TokenRecordTuple;
typedef std::vector <TokenRecordTuple> TokenRecordTupleList;
@@ -41,7 +42,6 @@ namespace OpenWifi {
void Create(const SecurityObjects::Token &R) override;
bool GetFromCache(const std::string &FieldName, const std::string &Value, SecurityObjects::Token &R) override;
void Delete(const std::string &FieldName, const std::string &Value) override;
private:
std::mutex Mutex_;
std::unique_ptr<Poco::ExpireLRUCache<std::string,SecurityObjects::Token>> CacheByToken_;
@@ -60,6 +60,10 @@ namespace OpenWifi {
bool CleanExpiredTokens();
bool RevokeAllTokens( std::string & UserName );
bool GetToken(std::string &Token, SecurityObjects::WebToken &WT, std::string & UserId, uint64_t &RevocationDate);
bool RefreshToken(const std::string &OldToken, const std::string &NewToken, const std::string &NewRefreshToken, uint64_t LstRefresh );
inline uint32_t Version() override { return 1;}
bool Upgrade(uint32_t from, uint32_t &to) override;
private:
};