Files
sm-user-plugin/controllers/login_gates.go
Jakub Zych 8501f3475a feat: login gates and the login event
Register publishes a *classes.LoginGates registry. POST /login consults
it through controllers.CheckLoginGates after the password, activation and
throttle checks and the bcrypt rehash, and before the restore of a
deactivated account, last_seen and the mint: a gate result replaces the
token response, a gate error answers the opaque 500. The restore and its
reactivate mail now run after the gates, so a password alone cannot
reactivate an account a second factor protects. Without a gate /login
answers as before; refresh never consults the gates.

classes.LoginEvent (golem15.user.login) fires after every password login
that minted a token; controllers.FireLoginEvent lets other plugins fire it.
2026-10-26 09:55:48 +01:00

27 lines
905 B
Go

package controllers
import (
"context"
"git.golem15.com/golem15/sm-user-plugin/classes"
"git.golem15.com/golem15/summercms/modules/backpack"
)
// CheckLoginGates consults the published *classes.LoginGates for a
// first-factor sign-in that already succeeded and returns its answer: nil,
// nil allows the sign-in, a result replaces the token response, and an error
// must fail the sign-in closed (answer the opaque 500, issue no token). It
// looks the registry up on every call and allows the sign-in when none is
// published. Password Login calls it; a social-login plugin calls it before
// it issues a token.
func CheckLoginGates(ctx context.Context, app *backpack.App, a classes.LoginAttempt) (*classes.LoginGateResult, error) {
if app == nil {
return nil, nil
}
gates, ok := app.Lookup[*classes.LoginGates]()
if !ok || gates == nil {
return nil, nil
}
return gates.Check(ctx, a)
}