fix login rediret retry error.
All checks were successful
Deploy / Build (push) Successful in 5m33s

This commit is contained in:
amass 2025-01-09 22:23:03 +08:00
parent 69cecf8022
commit b17b50b751
7 changed files with 99 additions and 63 deletions

View File

@ -1,6 +1,7 @@
#include "Session.h"
#include "BoostLog.h"
#include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/Identity.h>
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Dbo/WtJsonSqlTraits.h>
@ -11,7 +12,7 @@ std::unique_ptr<Wt::Dbo::SqlConnectionPool> sqlConnectionPool;
bool initialize(const std::string &path) {
try {
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(path);
connection->setProperty("show-queries", "true");
// connection->setProperty("show-queries", "true");
connection->setDateTimeStorage(Wt::Dbo::SqlDateTimeType::DateTime,
Wt::Dbo::backend::DateTimeStorage::PseudoISO8601AsText);
sqlConnectionPool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 10);
@ -73,6 +74,10 @@ void JsonSerializer::act(FieldRef<std::chrono::system_clock::time_point> field)
} // namespace Dbo
} // namespace Wt
User::User(const Wt::Auth::User &user) {
identity = user.identity(Wt::Auth::Identity::LoginName).toUTF8();
}
DBO_INSTANTIATE_TEMPLATES(User)
DBO_INSTANTIATE_TEMPLATES(Task)
DBO_INSTANTIATE_TEMPLATES(HomeBox::Item)

View File

@ -8,9 +8,13 @@ using AuthInfo = Wt::Auth::Dbo::AuthInfo<User>;
class User {
public:
User() = default;
User(const Wt::Auth::User &user);
template <class Action>
void persist(Action &a) {
}
std::string identity;
};
DBO_EXTERN_TEMPLATES(User);

View File

@ -119,36 +119,36 @@ Application::~Application() {
}
void Application::authEvent() {
auto app = Amass::Singleton<WebToolkit::Server>::instance();
auto &service = app->authService();
auto token = environment().getCookie(service.authTokenCookieName());
if (m_session->login().loggedIn()) {
const Wt::Auth::User &u = m_session->login().user();
LOG(info) << "User " << u.id() << " (" << u.identity(Wt::Auth::Identity::LoginName) << ")"
<< " logged in.";
auto app = Amass::Singleton<WebToolkit::Server>::instance();
auto &service = app->authService();
auto &env = environment();
auto token = env.getCookie(service.authTokenCookieName());
if (token == nullptr) {
Wt::Http::Cookie cookie(service.authTokenCookieName(), service.createAuthToken(u));
cookie.setDomain(service.authTokenCookieDomain());
cookie.setPath(AuthModel::CookiePath);
cookie.setExpires(Wt::WDateTime());
setCookie(cookie);
app->insertCookie(cookie.value(), u);
}
if (m_loginPage) {
if (m_navigationBar != nullptr) {
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
m_loginPageRef->removeStyleClass("bulma-m-auto");
m_loginPageRef->removeStyleClass("bulma-container");
}
} else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef);
if (m_navigationBar != nullptr) {
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
m_loginPageRef->removeStyleClass("bulma-m-auto");
m_loginPageRef->removeStyleClass("bulma-container");
}
}
if (m_loginedRedirectUrl.empty()) {
if (m_loginPage) {
if (m_navigationBar != nullptr) {
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
m_loginPageRef->removeStyleClass("bulma-m-auto");
m_loginPageRef->removeStyleClass("bulma-container");
}
} else if (m_loginPageRef != nullptr && m_loginPageRef->parent() == m_root) {
m_loginPage = m_loginPageRef->parent()->removeWidget(m_loginPageRef);
if (m_navigationBar != nullptr) {
m_loginPageRef = m_navigationBar->addLoginItem(std::move(m_loginPage));
m_loginPageRef->removeStyleClass("bulma-m-auto");
m_loginPageRef->removeStyleClass("bulma-container");
}
}
setInternalPath("/", true);
} else {
redirect(m_loginedRedirectUrl);
@ -157,6 +157,9 @@ void Application::authEvent() {
if (m_navigationBar != nullptr) {
m_loginPage = m_navigationBar->removeLoginItem();
}
if (token != nullptr) {
app->removeCookie(*token);
}
LOG(info) << "user logged out, internal path: " << internalPath();
if (internalPath() == "/wt/login") {
handlePathChange(internalPath());
@ -258,9 +261,23 @@ const Wt::Auth::PasswordService &Server::passwordService() {
return *m_passwordService;
}
void Server::insertCookie(const std::string &cookie) {
std::optional<User> Server::user(const std::string &cookie) {
if (m_cookies.contains(cookie)) {
return m_cookies.at(cookie);
} else {
return std::nullopt;
}
}
void Server::insertCookie(const std::string &cookie, const Wt::Auth::User &user) {
if (!m_cookies.contains(cookie)) {
m_cookies.insert(cookie);
m_cookies.insert_or_assign(cookie, user);
}
}
void Server::removeCookie(const std::string &cookie) {
if (m_cookies.contains(cookie)) {
m_cookies.erase(cookie);
}
}
@ -281,7 +298,7 @@ Wt::Http::Cookie Server::updateCookie(const std::string &oldCookie, const Wt::Au
if (m_cookies.contains(oldCookie)) { // 勾选了记住我
m_cookies.erase(oldCookie);
cookie.setMaxAge(std::chrono::seconds(result.newTokenValidity()));
m_cookies.insert(newToken);
m_cookies.insert_or_assign(newToken, result.user());
} else { // 只在会话期间有效
cookie.setExpires(Wt::WDateTime());
}

View File

@ -4,7 +4,8 @@
#include "Singleton.h"
#include <Wt/WApplication.h>
#include <memory>
#include <unordered_set>
#include <unordered_map>
#include "Database/User.h"
namespace Wt {
class WServer;
@ -55,8 +56,10 @@ public:
void initializeAuthenticationService();
Wt::Auth::AuthService &authService();
const Wt::Auth::PasswordService &passwordService();
void insertCookie(const std::string &cookie);
void insertCookie(const std::string &cookie, const Wt::Auth::User &user);
Wt::Http::Cookie updateCookie(const std::string &oldCookie, const Wt::Auth::AuthTokenResult &result, bool secure);
std::optional<User> user(const std::string &cookie);
void removeCookie(const std::string &cookie);
protected:
Server(uint16_t port, const std::string &applicationRoot, const std::string &documentRoot);
@ -68,7 +71,7 @@ private:
std::unique_ptr<Wt::Auth::AuthService> m_authService;
std::unique_ptr<Wt::Auth::PasswordService> m_passwordService;
std::unordered_set<std::string> m_cookies;
std::unordered_map<std::string, User> m_cookies;
};
} // namespace WebToolkit
#endif // __WEBAPPLICATION_H__

View File

@ -26,7 +26,7 @@ std::unique_ptr<Wt::WWidget> LoginPage::createRegistrationView(const Wt::Auth::I
}
void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::Auth::AuthService &service) {
using namespace Wt::Auth;
using namespace Wt;
std::string emailToken;
if (service.emailVerificationEnabled()) {
auto redirectPath = service.emailRedirectInternalPath();
@ -36,24 +36,24 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::
}
}
if (!emailToken.empty()) {
EmailTokenResult result = model()->processEmailToken(emailToken);
Auth::EmailTokenResult result = model()->processEmailToken(emailToken);
switch (result.state()) {
case EmailTokenState::Invalid:
case Auth::EmailTokenState::Invalid:
displayError(tr("Wt.Auth.error-invalid-token"));
break;
case EmailTokenState::Expired:
case Auth::EmailTokenState::Expired:
displayError(tr("Wt.Auth.error-token-expired"));
break;
case EmailTokenState::UpdatePassword:
case Auth::EmailTokenState::UpdatePassword:
letUpdatePassword(result.user(), false);
break;
case EmailTokenState::EmailConfirmed:
case Auth::EmailTokenState::EmailConfirmed:
displayInfo(tr("Wt.Auth.info-email-confirmed"));
User user = result.user();
Auth::User user = result.user();
LoginState state = LoginState::Strong;
Auth::LoginState state = Auth::LoginState::Strong;
if (model()->hasMfaStep(user)) {
state = LoginState::RequiresMfa;
state = Auth::LoginState::RequiresMfa;
}
model()->loginUser(login(), user, state);
}
@ -67,10 +67,10 @@ void LoginPage::processExternalEnvironment(const std::string &externalPath, Wt::
return;
}
User user = model()->processAuthToken();
LoginState state = LoginState::Weak;
Auth::User user = model()->processAuthToken();
Auth::LoginState state = Auth::LoginState::Weak;
if (model()->hasMfaStep(user)) {
state = LoginState::RequiresMfa;
state = Auth::LoginState::RequiresMfa;
}
model()->loginUser(login(), user, state);
}

View File

@ -1,6 +1,7 @@
#include "Restful.h"
#include "Application.h"
#include "Database/Session.h"
#include "Database/User.h"
#include "model/AuthModel.h"
#include <Wt/Auth/AuthService.h>
#include <Wt/Auth/Identity.h>
@ -15,6 +16,7 @@
DBO_INSTANTIATE_TEMPLATES(MyMessage)
void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
using namespace Wt;
auto tag = request.urlParam("tag");
// LOG(info) << "path: " << request.path() << ", tag: " << tag << ", server: " << request.hostName();
response.setMimeType("application/json");
@ -22,30 +24,35 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt:
auto app = Amass::Singleton<WebToolkit::Server>::instance();
auto &service = app->authService();
if (tag == "verify") {
auto session = Database::session();
auto enabled = service.authTokenUpdateEnabled();
boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); });
service.setAuthTokenUpdateEnabled(false);
Wt::Auth::AuthTokenState state;
Wt::Auth::User user;
if (service.authTokensEnabled()) {
const std::string *token = request.getCookieValue(service.authTokenCookieName());
if (token != nullptr) {
Wt::Auth::AuthTokenResult result = service.processAuthToken(*token, session->users());
state = result.state();
if (state == Wt::Auth::AuthTokenState::Valid) {
user = result.user();
const std::string *token = request.getCookieValue(service.authTokenCookieName());
Auth::AuthTokenState state = Auth::AuthTokenState::Invalid;
if (token != nullptr) {
if (auto u = app->user(*token); u) {
state = Auth::AuthTokenState::Valid;
message.user = u->identity;
} else {
Wt::Auth::User user;
auto session = Database::session();
auto enabled = service.authTokenUpdateEnabled();
boost::scope::scope_exit raii([&enabled, &service] { service.setAuthTokenUpdateEnabled(enabled); });
service.setAuthTokenUpdateEnabled(false);
if (service.authTokensEnabled()) {
Auth::AuthTokenResult result = service.processAuthToken(*token, session->users());
state = result.state();
if (state == Auth::AuthTokenState::Valid) {
user = result.user();
message.user = user.identity(Auth::Identity::LoginName).toUTF8();
}
}
}
// LOG(info) << "state: " << (int)state << " " << message.user;
} else {
LOG(warning) << "cannot access cookie.";
}
if (user.isValid()) {
message.user = user.identity(Wt::Auth::Identity::LoginName).toUTF8();
}
// LOG(info) << "state: " << (int)state << " " << message.user;
message.message = "Hello, World!";
message.status = state == Wt::Auth::AuthTokenState::Valid ? 0 : 404;
message.status = state == Auth::AuthTokenState::Valid ? 0 : 404;
using namespace boost::beast::http;
response.setStatus(static_cast<int>(state == Wt::Auth::AuthTokenState::Valid ? status::ok : status::unauthorized));
response.setStatus(static_cast<int>(state == Auth::AuthTokenState::Valid ? status::ok : status::unauthorized));
} else { // logout
auto domain = request.hostName();
if (domain.find("amass.fun") != std::string::npos) {
@ -54,7 +61,7 @@ void AuthenticationResource::handleRequest(const Wt::Http::Request &request, Wt:
response.addHeader("Set-Cookie", std::format("{}=; path={}; Domain={}; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT",
service.authTokenCookieName(), AuthModel::CookiePath, domain));
}
Wt::Dbo::JsonSerializer writer(response.out());
Dbo::JsonSerializer writer(response.out());
writer.serialize(message);
}

View File

@ -9,22 +9,22 @@ AuthModel::AuthModel(const Wt::Auth::AuthService &baseAuth, Wt::Auth::AbstractUs
}
Wt::Auth::User AuthModel::processAuthToken() {
using namespace Wt::Auth;
using namespace Wt;
if (baseAuth()->authTokensEnabled()) {
Wt::WApplication *app = Wt::WApplication::instance();
const Wt::WEnvironment &env = app->environment();
const std::string *token = env.getCookie(baseAuth()->authTokenCookieName());
if (token) {
AuthTokenResult result = baseAuth()->processAuthToken(*token, users());
Auth::AuthTokenResult result = baseAuth()->processAuthToken(*token, users());
auto server = Amass::Singleton<WebToolkit::Server>::instance();
auto cookie = server->updateCookie(*token, result, app->environment().urlScheme() == "https");
if ((result.state() == AuthTokenState::Invalid) || !cookie.value().empty()) {
if ((result.state() == Auth::AuthTokenState::Invalid) || !cookie.value().empty()) {
app->setCookie(cookie);
}
return result.state() == AuthTokenState::Valid ? result.user() : User();
return result.state() == Auth::AuthTokenState::Valid ? result.user() : Auth::User();
}
}
return User();
return Auth::User();
}
void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) {
@ -39,6 +39,6 @@ void AuthModel::setRememberMeCookie(const Wt::Auth::User &user) {
cookie.setSecure(app->environment().urlScheme() == "https");
auto server = Amass::Singleton<WebToolkit::Server>::instance();
server->insertCookie(cookie.value());
server->insertCookie(cookie.value(), user);
app->setCookie(cookie);
}