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.
This commit is contained in:
@@ -102,8 +102,10 @@ func RegisterUser(ctx context.Context, app *backpack.App, db *gorm.DB, fields ma
|
||||
// (a social sign-in). It restores a soft-deleted account and sends the
|
||||
// reactivate mail, records ip as last_ip_address, and touches last_seen. It
|
||||
// checks no password, throttle, activation or ban and fires no event; the
|
||||
// caller does those checks (Go has no login event). A failed last_seen
|
||||
// write is logged and does not fail it, as in Login.
|
||||
// caller does those checks, consults the login gates (CheckLoginGates)
|
||||
// before it, and fires the login event (FireLoginEvent) after it minted the
|
||||
// token. A failed last_seen write is logged and does not fail it, as in
|
||||
// Login.
|
||||
func FinishLogin(ctx context.Context, app *backpack.App, db *gorm.DB, user *models.User, ip string) error {
|
||||
if db == nil {
|
||||
return errors.New("golem15.user: gorm handle is missing")
|
||||
@@ -179,6 +181,17 @@ func FireDeactivatingEvent(ctx context.Context, app *backpack.App, user *models.
|
||||
return app.Events.Fire(ctx, &classes.DeactivatingEvent{User: user, Tx: tx})
|
||||
}
|
||||
|
||||
// FireLoginEvent fires classes.LoginEvent (golem15.user.login) for user
|
||||
// with source, one of the classes.LoginSource constants, and returns the
|
||||
// listeners' error. Call it after the session token was minted; a caller
|
||||
// logs the error and still answers the sign-in.
|
||||
func FireLoginEvent(ctx context.Context, app *backpack.App, user *models.User, source string) error {
|
||||
if app == nil || app.Events == nil {
|
||||
return nil
|
||||
}
|
||||
return app.Events.Fire(ctx, &classes.LoginEvent{User: user, Source: source})
|
||||
}
|
||||
|
||||
// IssueToken mints the session JWT for user exactly as /register and
|
||||
// /login do: the plugin's JWT secret, golem15.user.jwt.ttl and the request
|
||||
// URL as the issuer.
|
||||
|
||||
Reference in New Issue
Block a user