mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-03 16:02:02 +00:00
55 lines
2.1 KiB
Go
55 lines
2.1 KiB
Go
package middleware
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/warmbly/warmbly/internal/app/token"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
)
|
|
|
|
// RequireFreshAuth refuses a request whose session has not re-proved the
|
|
// account holder recently.
|
|
//
|
|
// CASA 2.4.1 asks for a full session plus re-authentication or a secondary
|
|
// check before a sensitive account change. Changing a password and disabling
|
|
// 2FA already demand a current credential; the actions gated here are the rest
|
|
// of that set. Each either hands out a credential that outlives the session
|
|
// that created it (an API key, a passkey registered to a device) or cannot be
|
|
// reversed by the person it was done to (ownership transfer, scheduled
|
|
// deletion), which is more than a live token alone should carry.
|
|
//
|
|
// The caller re-authenticates at POST /v1/auth/reauth and retries.
|
|
//
|
|
// This applies to session callers only. An API key and an OAuth token have no
|
|
// session and no way to present a second factor, and they are not the threat
|
|
// this addresses: the risk is a browser token lifted from a machine somebody
|
|
// walked away from. A key is already a deliberate, scoped, revocable grant that
|
|
// was itself minted from a confirmed session, its use is audited, and the
|
|
// permission gate on the route decides what it may do. Refusing them here would
|
|
// break every automation, the CLI included, to exceed what the control asks
|
|
// for.
|
|
func RequireFreshAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
if authType, _ := c.Get(AuthTypeKey); authType == AuthTypeAPIKey || authType == AuthTypeOAuth {
|
|
c.Next()
|
|
return
|
|
}
|
|
|
|
session := GetSession(c)
|
|
if session == nil {
|
|
errx.JSON(c, errx.NewWithIdentifier(errx.Unauthorized, "reauth_required",
|
|
"This action needs a signed-in session. Sign in and try again."))
|
|
c.Abort()
|
|
return
|
|
}
|
|
if session.ReauthAt == nil || time.Since(*session.ReauthAt) > token.ReauthWindow {
|
|
errx.JSON(c, errx.NewWithIdentifier(errx.Forbidden, "reauth_required",
|
|
"Confirm it is you before making this change."))
|
|
c.Abort()
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|