fixed #1
This commit is contained in:
@@ -10,9 +10,18 @@ type contextKey string
|
|||||||
|
|
||||||
const UserContextKey contextKey = contextKey("user")
|
const UserContextKey contextKey = contextKey("user")
|
||||||
|
|
||||||
|
// middleware.go
|
||||||
func AuthMiddleware(secret []byte) func(http.Handler) http.Handler {
|
func AuthMiddleware(secret []byte) func(http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// WICHTIG: Wenn der User auf einer öffentlichen Seite ist,
|
||||||
|
// darf die Middleware KEINEN Auth-Zwang ausüben und nicht redirecten!
|
||||||
|
if r.URL.Path == "/login" || r.URL.Path == "/register" || r.URL.Path == "/" {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tokenStr := ""
|
tokenStr := ""
|
||||||
authHeader := r.Header.Get("Authorization")
|
authHeader := r.Header.Get("Authorization")
|
||||||
|
|
||||||
@@ -37,22 +46,25 @@ func AuthMiddleware(secret []byte) func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
claims, err := ValidateJWT(tokenStr, secret)
|
claims, err := ValidateJWT(tokenStr, secret)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.HasPrefix(r.URL.Path, "/api/") {
|
if strings.HasPrefix(r.URL.Path, "/api/") {
|
||||||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||||
} else {
|
} else {
|
||||||
|
// Falls das Cookie korrupt oder abgelaufen ist, löschen wir es direkt,
|
||||||
|
// damit das Frontend sauber merkt, dass es weg ist.
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "access_token",
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: -1,
|
||||||
|
HttpOnly: false, // Erlaubt JS das Auslesen
|
||||||
|
})
|
||||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.WithValue(
|
ctx := context.WithValue(r.Context(), UserContextKey, claims)
|
||||||
r.Context(),
|
|
||||||
UserContextKey,
|
|
||||||
claims,
|
|
||||||
)
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(ctx))
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearAllAuth() {
|
function clearAllAuth() {
|
||||||
console.log("Clearing all auth remnants from cookies and localStorage...");
|
console.log("Clearing all auth remnants from everywhere...");
|
||||||
localStorage.removeItem("access_token");
|
localStorage.removeItem("access_token");
|
||||||
localStorage.removeItem("refresh_token");
|
localStorage.removeItem("refresh_token");
|
||||||
document.cookie = "access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
sessionStorage.removeItem("is_refreshing");
|
||||||
document.cookie = "refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;";
|
document.cookie = "access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; Secure; SameSite=Lax;";
|
||||||
|
document.cookie = "refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC; Secure; SameSite=Lax;";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tryTokenRefresh(refreshToken) {
|
async function tryTokenRefresh(refreshToken) {
|
||||||
if (!refreshToken) return false;
|
if (!refreshToken) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log("Sending refresh token to /api/refresh...");
|
||||||
const response = await fetch("/api/refresh", {
|
const response = await fetch("/api/refresh", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
@@ -33,13 +35,10 @@
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
localStorage.setItem("access_token", data.access_token);
|
localStorage.setItem("access_token", data.access_token);
|
||||||
localStorage.setItem("refresh_token", data.refresh_token);
|
localStorage.setItem("refresh_token", data.refresh_token);
|
||||||
|
|
||||||
document.cookie = `access_token=${data.access_token}; path=/; max-age=900; SameSite=Lax; Secure`;
|
document.cookie = `access_token=${data.access_token}; path=/; max-age=900; SameSite=Lax; Secure`;
|
||||||
document.cookie = `refresh_token=${data.refresh_token}; path=/; max-age=604800; SameSite=Lax; Secure`;
|
document.cookie = `refresh_token=${data.refresh_token}; path=/; max-age=604800; SameSite=Lax; Secure`;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -59,52 +58,71 @@
|
|||||||
const refreshToken = cookieRefreshToken || localRefreshToken;
|
const refreshToken = cookieRefreshToken || localRefreshToken;
|
||||||
|
|
||||||
console.log("Auth check started...");
|
console.log("Auth check started...");
|
||||||
console.log("AccessToken present:", !!accessToken);
|
console.log("AccessToken available (Cookie/Local):", !!cookieAccessToken, "/", !!localAccessToken);
|
||||||
console.log("RefreshToken present:", !!refreshToken);
|
console.log("RefreshToken available (Cookie/Local):", !!cookieRefreshToken, "/", !!localRefreshToken);
|
||||||
|
|
||||||
if (!accessToken && !refreshToken) {
|
if (!accessToken && !refreshToken) {
|
||||||
console.log("No tokens found. User is guest.");
|
console.log("No tokens found in cookies or localStorage. User is a guest.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
try {
|
try {
|
||||||
console.log("Attempting ping with access token...");
|
console.log("Validating token against /api/userinfo...");
|
||||||
const response = await fetch("/api/ping", {
|
|
||||||
|
const response = await fetch("/api/userinfo", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { "Authorization": `Bearer ${accessToken}` }
|
headers: { "Authorization": `Bearer ${accessToken}` }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
console.log("Ping successful! Redirecting to dashboard...");
|
console.log("Token is perfectly valid!");
|
||||||
|
sessionStorage.removeItem("is_refreshing");
|
||||||
|
|
||||||
|
if (!cookieAccessToken) {
|
||||||
|
document.cookie = `access_token=${accessToken}; path=/; max-age=900; SameSite=Lax; Secure`;
|
||||||
|
}
|
||||||
|
if (!cookieRefreshToken && localRefreshToken) {
|
||||||
|
document.cookie = `refresh_token=${localRefreshToken}; path=/; max-age=604800; SameSite=Lax; Secure`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Redirecting to dashboard...");
|
||||||
window.location.href = "/dashboard";
|
window.location.href = "/dashboard";
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.log("Ping failed. Token might be expired. Status:", response.status);
|
console.log("Token rejected by /api/userinfo. Status:", response.status);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Network error during ping:", err);
|
console.error("Network error during token verification:", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sessionStorage.getItem("is_refreshing") === "true") {
|
||||||
|
console.warn("Loop protection triggered! Tokens appear to be corrupt. Clearing storage.");
|
||||||
|
clearAllAuth();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (refreshToken) {
|
if (refreshToken) {
|
||||||
console.log("Starting token refresh to rebuild cookies...");
|
console.log("Access token has expired. Starting refresh process...");
|
||||||
|
sessionStorage.setItem("is_refreshing", "true");
|
||||||
|
|
||||||
const refreshSuccessful = await tryTokenRefresh(refreshToken);
|
const refreshSuccessful = await tryTokenRefresh(refreshToken);
|
||||||
|
|
||||||
if (refreshSuccessful) {
|
if (refreshSuccessful) {
|
||||||
console.log("Refresh successful! Redirecting to dashboard...");
|
console.log("Refresh successful! Reloading page to apply cookies...");
|
||||||
window.location.href = "/dashboard";
|
window.location.reload();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.log("Refresh failed. Refresh token is invalid/expired.");
|
console.log("Refresh failed. Refresh token has also expired.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No refresh token present.");
|
console.log("No refresh token available for recovery.");
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAllAuth();
|
clearAllAuth();
|
||||||
console.log("Authentication completely failed. Staying on current guest page.");
|
console.log("Authentication completely failed. User remains on login page.");
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuth();
|
setTimeout(checkAuth, 50);
|
||||||
})();
|
})();
|
||||||
@@ -268,6 +268,8 @@ func UserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("GET [api/userinfo] " + r.RemoteAddr + ": " + err.Error())
|
log.Println("GET [api/userinfo] " + r.RemoteAddr + ": " + err.Error())
|
||||||
|
http.Error(w, "Unauthorized: Invalid or expired token", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
idParam = claims.UserID
|
idParam = claims.UserID
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ func (this *Server) Run() {
|
|||||||
mux.HandleFunc("/api/refresh", handlers.RefreshToken)
|
mux.HandleFunc("/api/refresh", handlers.RefreshToken)
|
||||||
mux.Handle("/api/logout", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(handlers.Logout)))
|
mux.Handle("/api/logout", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(handlers.Logout)))
|
||||||
mux.Handle("/api/profile", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(handlers.UserInfo)))
|
mux.Handle("/api/profile", auth.AuthMiddleware(this.JWTSecret)(http.HandlerFunc(handlers.UserInfo)))
|
||||||
|
mux.HandleFunc("/api/userinfo", handlers.UserInfo)
|
||||||
if this.AllowRegistration {
|
if this.AllowRegistration {
|
||||||
mux.HandleFunc("/api/register", handlers.APIRegister)
|
mux.HandleFunc("/api/register", handlers.APIRegister)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user