forked from Nixius/authelia
Fix success page skipped due to webhook race condition
The webhook was provisioning the user before the success page loaded, causing IsNew=false and skipping the welcome/onboarding page entirely. Now: - Webhook only ensures user+stripe ID as a backstop (no password email) - Success page is the sole owner of password reset + welcome flow - Uses group membership (not IsNew) to distinguish new vs returning: if already in 'customers' group -> dashboard, otherwise -> welcome Made-with: Cursor
This commit is contained in:
parent
677bef195f
commit
c7d19ed20d
|
|
@ -72,50 +72,41 @@ func (a *App) handleSuccess(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.IsNew {
|
// Check if user is already an active customer (resubscribe case)
|
||||||
// New user: send password setup email, show onboarding page.
|
inGroup, _ := a.ldap.IsInGroup(result.Username, "customers")
|
||||||
// Group membership and stack deploy happen on /activate after they set a password.
|
if inGroup {
|
||||||
if err := a.triggerPasswordReset(result.Username); err != nil {
|
// Returning customer: ensure stack exists, go to dashboard
|
||||||
log.Printf("authelia reset trigger failed for %s: %v", username, err)
|
stackName := fmt.Sprintf("customer-%s", result.Username)
|
||||||
}
|
exists, _ := a.swarm.StackExists(stackName)
|
||||||
data := map[string]any{
|
if !exists {
|
||||||
"Username": result.Username,
|
if err := a.swarm.DeployStack(stackName, result.Username, a.cfg.TraefikDomain); err != nil {
|
||||||
"IsNew": true,
|
log.Printf("resubscribe: stack deploy failed for %s: %v", result.Username, err)
|
||||||
"Email": email,
|
}
|
||||||
"LoginURL": a.cfg.AutheliaURL,
|
|
||||||
"ResetURL": a.cfg.AutheliaURL + "/#/reset-password/step1",
|
|
||||||
"ActivateURL": a.cfg.AppURL + "/activate",
|
|
||||||
"DashboardURL": a.cfg.AppURL + "/dashboard",
|
|
||||||
"InstanceURL": "https://" + result.Username + "." + a.cfg.CustomerDomain,
|
|
||||||
}
|
|
||||||
if err := a.tmpl.ExecuteTemplate(w, "welcome.html", data); err != nil {
|
|
||||||
log.Printf("template error: %v", err)
|
|
||||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
|
log.Printf("resubscribe: %s payment verified, redirecting to dashboard", result.Username)
|
||||||
|
http.Redirect(w, r, a.cfg.AppURL+"/dashboard", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Existing user resubscribing: re-add to customers group if needed and
|
// New or lapsed customer: send password setup email, show onboarding.
|
||||||
// ensure their stack is running, then send straight to dashboard.
|
// Group membership and stack deploy happen on /activate after they set a password.
|
||||||
inGroup, _ := a.ldap.IsInGroup(result.Username, "customers")
|
if err := a.triggerPasswordReset(result.Username); err != nil {
|
||||||
if !inGroup {
|
log.Printf("authelia reset trigger failed for %s: %v", username, err)
|
||||||
if err := a.ldap.AddToGroup(result.Username, "customers"); err != nil {
|
|
||||||
log.Printf("resubscribe: add to group failed for %s: %v", result.Username, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("resubscribe: re-added %s to customers group", result.Username)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
data := map[string]any{
|
||||||
stackName := fmt.Sprintf("customer-%s", result.Username)
|
"Username": result.Username,
|
||||||
exists, _ := a.swarm.StackExists(stackName)
|
"IsNew": true,
|
||||||
if !exists {
|
"Email": email,
|
||||||
if err := a.swarm.DeployStack(stackName, result.Username, a.cfg.TraefikDomain); err != nil {
|
"LoginURL": a.cfg.AutheliaURL,
|
||||||
log.Printf("resubscribe: stack deploy failed for %s: %v", result.Username, err)
|
"ResetURL": a.cfg.AutheliaURL + "/#/reset-password/step1",
|
||||||
}
|
"ActivateURL": a.cfg.AppURL + "/activate",
|
||||||
|
"DashboardURL": a.cfg.AppURL + "/dashboard",
|
||||||
|
"InstanceURL": "https://" + result.Username + "." + a.cfg.CustomerDomain,
|
||||||
|
}
|
||||||
|
if err := a.tmpl.ExecuteTemplate(w, "welcome.html", data); err != nil {
|
||||||
|
log.Printf("template error: %v", err)
|
||||||
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("resubscribe: %s payment verified, redirecting to dashboard", result.Username)
|
|
||||||
http.Redirect(w, r, a.cfg.AppURL+"/dashboard", http.StatusSeeOther)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) handlePortal(w http.ResponseWriter, r *http.Request) {
|
func (a *App) handlePortal(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,9 @@ func (a *App) handleWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconciliation: ensures LLDAP user exists. Group + stack are handled by /activate.
|
// Reconciliation backstop: ensures LLDAP user + Stripe ID are set.
|
||||||
|
// Does NOT send password reset — that's the success page's responsibility
|
||||||
|
// so it can reliably show the welcome/onboarding page.
|
||||||
func (a *App) onCheckoutCompleted(event stripego.Event) {
|
func (a *App) onCheckoutCompleted(event stripego.Event) {
|
||||||
var sess stripego.CheckoutSession
|
var sess stripego.CheckoutSession
|
||||||
if err := json.Unmarshal(event.Data.Raw, &sess); err != nil {
|
if err := json.Unmarshal(event.Data.Raw, &sess); err != nil {
|
||||||
|
|
@ -55,18 +57,8 @@ func (a *App) onCheckoutCompleted(event stripego.Event) {
|
||||||
|
|
||||||
log.Printf("webhook: checkout completed email=%s customer=%s", email, customerID)
|
log.Printf("webhook: checkout completed email=%s customer=%s", email, customerID)
|
||||||
|
|
||||||
result, err := a.ldap.ProvisionUser(username, email, customerID)
|
if err := a.ldap.EnsureUser(username, email, customerID); err != nil {
|
||||||
if err != nil {
|
log.Printf("webhook: ldap ensure user failed: %v", err)
|
||||||
log.Printf("webhook: ldap provision user failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.IsNew {
|
|
||||||
if err := a.triggerPasswordReset(username); err != nil {
|
|
||||||
log.Printf("webhook: password reset email failed for %s: %v", username, err)
|
|
||||||
} else {
|
|
||||||
log.Printf("webhook: password reset email sent to %s (%s)", username, email)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue