fix(go,tmpl): solve the Chromium/Safari logout...
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
...issue by deleting the session cookie after successful password change and forcing the user to re-authenticate. additionally, split the InitialPasswordChange func into separate "GET" and "POST" variants.
This commit is contained in:
parent
e8515d9a89
commit
1b2d860beb
@ -75,7 +75,7 @@ func (a *App) SetupRoutes() error {
|
||||
user := e.Group("/user", handlers.MiddlewareSession, xsrf)
|
||||
|
||||
user.GET("/initial-password-change", handlers.InitialPasswordChange())
|
||||
user.POST("/initial-password-change", handlers.InitialPasswordChange())
|
||||
user.POST("/initial-password-change", handlers.InitialPasswordChangePost())
|
||||
user.GET("/hibp-search", handlers.GetSearchHIBP())
|
||||
user.POST("/hibp-search", handlers.SearchHIBP())
|
||||
user.GET("/hibp-breach-details/:name", handlers.ViewHIBP())
|
||||
|
@ -36,7 +36,7 @@ func Home(client *ent.Client) echo.HandlerFunc {
|
||||
return c.Redirect(http.StatusSeeOther, "/signin")
|
||||
}
|
||||
|
||||
log.Info("gorilla session", "username", sess.Values["username"].(string))
|
||||
log.Debug("session", "username", sess.Values["username"].(string), "endpoint", "/home")
|
||||
username = sess.Values["username"].(string)
|
||||
|
||||
// example denial.
|
||||
@ -94,16 +94,21 @@ func Home(client *ent.Client) echo.HandlerFunc {
|
||||
p.Name = username
|
||||
p.User = u
|
||||
|
||||
data := make(map[string]any)
|
||||
flash := sess.Values["flash"]
|
||||
|
||||
if flash != nil {
|
||||
data["flash"] = flash.(string)
|
||||
if flsh, ok := sess.Values["flash"].(string); ok {
|
||||
p.Data["flash"] = flsh
|
||||
|
||||
delete(sess.Values, "flash")
|
||||
}
|
||||
|
||||
if _, ok := sess.Values["reauthFlash"].(string); ok {
|
||||
p.Data["info"] = "First time after changing the password, yay!"
|
||||
|
||||
// if this is the first login after the initial password change, delete
|
||||
// the cookie value.
|
||||
delete(sess.Values, "reauthFlash")
|
||||
}
|
||||
|
||||
_ = sess.Save(c.Request(), c.Response())
|
||||
}
|
||||
|
||||
err := c.Render(http.StatusOK, "home.tmpl", p)
|
||||
if err != nil {
|
||||
|
@ -4,6 +4,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@ -30,6 +31,25 @@ func GetSearchHIBP() echo.HandlerFunc {
|
||||
)
|
||||
}
|
||||
|
||||
if !u.IsAdmin {
|
||||
ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger)
|
||||
|
||||
f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID)
|
||||
if err != nil {
|
||||
return renderErrorPage(
|
||||
c,
|
||||
http.StatusInternalServerError,
|
||||
http.StatusText(http.StatusInternalServerError),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
if !f {
|
||||
log.Warn("resource access attempt without performing the initial password change", "user", u.Username, "endpoint", "/user/hibp-search")
|
||||
return c.Redirect(http.StatusSeeOther, "/user/initial-password-change")
|
||||
}
|
||||
}
|
||||
|
||||
csrf := c.Get("csrf").(string)
|
||||
p := newPage()
|
||||
|
||||
@ -90,6 +110,29 @@ func SearchHIBP() echo.HandlerFunc {
|
||||
)
|
||||
}
|
||||
|
||||
if !u.IsAdmin {
|
||||
ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger)
|
||||
|
||||
f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID)
|
||||
if err != nil {
|
||||
return renderErrorPage(
|
||||
c,
|
||||
http.StatusInternalServerError,
|
||||
http.StatusText(http.StatusInternalServerError),
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
|
||||
if !f {
|
||||
return renderErrorPage(
|
||||
c,
|
||||
http.StatusUnauthorized,
|
||||
http.StatusText(http.StatusUnauthorized)+" - you need to perform your initial password change before accessing this resource",
|
||||
"user never changed password",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
csrf := c.Get("csrf").(string)
|
||||
|
||||
a := new(hibpSearch)
|
||||
|
@ -20,10 +20,27 @@ func Signin() echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
addHeaders(c)
|
||||
|
||||
if sess, _ := session.Get(setting.SessionCookieName(), c); sess != nil {
|
||||
reauth := false
|
||||
r := c.QueryParam("reauth")
|
||||
|
||||
if r == "true" {
|
||||
reauth = true
|
||||
}
|
||||
|
||||
// slog.Info("XXX session cookie name:", setting.SessionCookieName())
|
||||
|
||||
sess, _ := session.Get(setting.SessionCookieName(), c)
|
||||
|
||||
var uf *userSignin
|
||||
|
||||
if sess != nil {
|
||||
username := sess.Values["username"]
|
||||
if username != nil {
|
||||
return c.Redirect(http.StatusFound, "/home")
|
||||
if !reauth {
|
||||
return c.Redirect(http.StatusPermanentRedirect, "/home")
|
||||
}
|
||||
|
||||
uf = &userSignin{Username: username.(string)}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +51,18 @@ func Signin() echo.HandlerFunc {
|
||||
p.Current = "signin"
|
||||
p.CSRF = csrf
|
||||
|
||||
if reauth {
|
||||
fl := sess.Values["reauthFlash"]
|
||||
if _, ok := fl.(string); !ok {
|
||||
p.Data["flash"] = "re-login please"
|
||||
} else {
|
||||
p.Data["reauthFlash"] = fl.(string)
|
||||
if uf != nil {
|
||||
p.Data["form"] = uf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.Render(
|
||||
http.StatusOK,
|
||||
"signin.tmpl",
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func InitialPasswordChange() echo.HandlerFunc {
|
||||
func InitialPasswordChangePost() echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
addHeaders(c)
|
||||
|
||||
@ -41,8 +41,6 @@ func InitialPasswordChange() echo.HandlerFunc {
|
||||
|
||||
f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID)
|
||||
|
||||
switch {
|
||||
case c.Request().Method == "POST":
|
||||
switch {
|
||||
case err != nil:
|
||||
return renderErrorPage(
|
||||
@ -97,14 +95,53 @@ func InitialPasswordChange() echo.HandlerFunc {
|
||||
)
|
||||
}
|
||||
|
||||
log.Info("successfully performed initial password change", "user", u.Username)
|
||||
|
||||
if sess, ok := c.Get("sess").(*sessions.Session); ok {
|
||||
sess.Values["flash"] = "Successfully updated your password"
|
||||
_ = sess.Save(c.Request(), c.Response())
|
||||
sess.Values["reauthFlash"] = "Successfully updated your password, log in again, please"
|
||||
|
||||
if err = sess.Save(c.Request(), c.Response()); err != nil {
|
||||
return renderErrorPage(
|
||||
c,
|
||||
http.StatusInternalServerError,
|
||||
http.StatusText(http.StatusInternalServerError)+" - could not change the session cookie",
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Redirect(http.StatusSeeOther, "/home")
|
||||
return c.Redirect(http.StatusSeeOther, "/signin?reauth=true")
|
||||
}
|
||||
}
|
||||
|
||||
func InitialPasswordChange() echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
addHeaders(c)
|
||||
|
||||
u, ok := c.Get("sessUsr").(moduser.User)
|
||||
if !ok {
|
||||
return renderErrorPage(
|
||||
c,
|
||||
http.StatusUnauthorized,
|
||||
http.StatusText(http.StatusUnauthorized)+", perhaps you need to log in first?",
|
||||
"Username was nil",
|
||||
)
|
||||
} else if u.IsAdmin {
|
||||
status := http.StatusUnauthorized
|
||||
msg := http.StatusText(status)
|
||||
|
||||
return renderErrorPage(
|
||||
c, status, msg+": You should not be here", "This endpoint is for users only",
|
||||
)
|
||||
}
|
||||
|
||||
ctx, ok := c.Get("sloggerCtx").(context.Context)
|
||||
if !ok {
|
||||
ctx = context.WithValue(context.Background(), moduser.CtxKey{}, slogger)
|
||||
}
|
||||
|
||||
f, err := moduser.UsrFinishedSetup(ctx, dbclient, u.ID)
|
||||
|
||||
case c.Request().Method == "GET":
|
||||
switch {
|
||||
case err != nil:
|
||||
return renderErrorPage(
|
||||
@ -125,8 +162,9 @@ func InitialPasswordChange() echo.HandlerFunc {
|
||||
p.Current = "init-password-change"
|
||||
p.CSRF = csrf
|
||||
p.User = u
|
||||
p.Name = u.Username
|
||||
|
||||
err := c.Render(
|
||||
err = c.Render(
|
||||
http.StatusOK,
|
||||
"user/init-password-change.tmpl",
|
||||
p,
|
||||
@ -141,7 +179,6 @@ func InitialPasswordChange() echo.HandlerFunc {
|
||||
err.Error(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -4,13 +4,22 @@
|
||||
<main class="grow">
|
||||
<div class="px-2 md:px-0 place-items-center text-center">
|
||||
{{ if and .Data .Data.flash }}
|
||||
<h1 class="text-xl text-pink-600 dark:text-pink-500 py-2">
|
||||
<h1 class="mt-8 text-xl text-pink-600 dark:text-pink-500 py-2">
|
||||
{{ .Data.flash }}
|
||||
</h1>
|
||||
{{- end }}
|
||||
{{ if and .Data .Data.info }}
|
||||
<h2 class="mt-4 text-xl font-bold text-green-600 dark:text-green-500 py-2">
|
||||
{{ .Data.info }}
|
||||
</h2>
|
||||
{{- end }}
|
||||
{{ if .Name -}}
|
||||
<h1 class="mt-20 text-2xl text-pink-400 font-bold">
|
||||
Welcome, <code>{{.Name}}</code>!<br>
|
||||
<h1 class="mt-14 text-2xl text-pink-400 font-bold">
|
||||
Welcome to
|
||||
<code class="p-1 rounded-sm border-l-2 border-fuchsia-200">
|
||||
{{ .AppName }}</code>,
|
||||
<code class="p-1 rounded-sm bg-fuchsia-200">
|
||||
{{.Name}}</code>!
|
||||
</h1>
|
||||
{{if .User}}
|
||||
{{if .User.IsAdmin}}
|
||||
|
@ -20,6 +20,11 @@
|
||||
<p class="mt-2 text-md text-rose-800 dark:text-rose-500"><span class="font-medium">Error:</span> {{.Data.flash}}</p>
|
||||
</div>
|
||||
{{- else -}}{{end}}
|
||||
{{ if and .Data .Data.reauthFlash }}
|
||||
<div class="relative flex items-center mb-4">
|
||||
<p class="mt-2 text-xl text-blue-500 dark:text-blue-400"><span class="italic font-medium">Success:</span> {{.Data.reauthFlash}}</p>
|
||||
</div>
|
||||
{{- else -}}{{end}}
|
||||
<!-- username field -->
|
||||
<div class="relative flex items-center">
|
||||
<span class="absolute" role="img" aria-label="person outline icon for username">
|
||||
|
@ -10,16 +10,30 @@
|
||||
{{- end }}
|
||||
{{ if .User -}}
|
||||
<h1 class="mt-20 text-2xl text-pink-400 font-bold">
|
||||
Welcome, <code>{{.Name}}</code>, since you have never changed your password here before, now is the time to do that!<br>
|
||||
Welcome,
|
||||
<code class="p-1 rounded-sm bg-fuchsia-200">
|
||||
{{.Name}}</code>!
|
||||
</h1>
|
||||
<h3 class="mt-2 mb-4 text-2xl text-purple-500 dark:text-purple-300 font-bold">
|
||||
Since you have never changed your password here before, now is the time to do that!
|
||||
</h3>
|
||||
<span class="text-lg text-gray-500 dark:text-gray-300 font-italic">
|
||||
This is necessary in order to make sure that only you know the password, since the account was pre-created <em>for</em> you.
|
||||
This is necessary in order to make sure that <b>only you</b> know the
|
||||
password, as the account was pre-created <em>for</em> you.
|
||||
</span>
|
||||
<div class="block">
|
||||
<div class="mt-8 md:flex md:items-center md:place-items-center md:justify-between">
|
||||
<div class="mt-6 md:flex md:items-center md:place-items-center md:justify-between">
|
||||
<form method="POST" class="w-full md:w-5xl" action="/user/initial-password-change">
|
||||
<input type="hidden" name="csrf" value="{{- .CSRF -}}">
|
||||
<div class="relative flex items-center mt-4">
|
||||
<label class="group relative mt-2 font-bold text-fuchsia-500 dark:text-fuchsia-300 text-sm">
|
||||
Your new password 🛈
|
||||
<span
|
||||
class="absolute hidden group-hover:flex -left-5 -top-2 -translate-y-full w-48 px-2 py-1 bg-gray-700 rounded-lg text-center text-white text-sm after:content-[''] after:absolute after:left-1/2 after:top-[100%] after:-translate-x-1/2 after:border-8 after:border-x-transparent after:border-b-transparent after:border-t-gray-700">
|
||||
Please, save this password into a password manager because after clicking
|
||||
submit you will be logged out and will need to re-login again.
|
||||
</span>
|
||||
</label>
|
||||
<div class="relative flex items-center">
|
||||
<span class="absolute" role="img" aria-label="password lock icon">
|
||||
{{ template "svg-password.tmpl" }}
|
||||
</span>
|
||||
|
Loading…
Reference in New Issue
Block a user