Files

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()
}
}