authelia/internal/model/one_time_code.go

69 lines
1.9 KiB
Go

package model
import (
"database/sql"
"fmt"
"time"
"github.com/google/uuid"
"github.com/authelia/authelia/v4/internal/random"
)
const (
// OTCIntentUserSessionElevation is the intent value for a one-time code indicating it's used for user session
// elevation.
OTCIntentUserSessionElevation = "use"
)
// NewOneTimeCode returns a new OneTimeCode.
func NewOneTimeCode(ctx Context, username string, characters int, duration time.Duration) (otp *OneTimeCode, err error) {
var (
publicID uuid.UUID
code []byte
)
src := ctx.GetRandom()
if publicID, err = uuid.NewRandomFromReader(src); err != nil {
return nil, fmt.Errorf("failed to generate public id: %w", err)
}
if code, err = src.BytesCustomErr(characters, []byte(random.CharSetUnambiguousUpper)); err != nil {
return nil, fmt.Errorf("failed to generate random bytes: %w", err)
}
return &OneTimeCode{
PublicID: publicID,
IssuedAt: ctx.GetClock().Now(),
IssuedIP: NewIP(ctx.RemoteIP()),
ExpiresAt: ctx.GetClock().Now().Add(duration),
Username: username,
Intent: OTCIntentUserSessionElevation,
Code: code,
}, nil
}
// OneTimeCode represents special one-time codes stored in the database.
type OneTimeCode struct {
ID int `db:"id"`
PublicID uuid.UUID `db:"public_id"`
Signature string `db:"signature"`
IssuedAt time.Time `db:"issued"`
IssuedIP IP `db:"issued_ip"`
ExpiresAt time.Time `db:"expires"`
Username string `db:"username"`
Intent string `db:"intent"`
ConsumedAt sql.NullTime `db:"consumed"`
ConsumedIP NullIP `db:"consumed_ip"`
RevokedAt sql.NullTime `db:"revoked"`
RevokedIP NullIP `db:"revoked_ip"`
Code []byte `db:"code"`
}
// Consume sets the values required to consume the one-time code.
func (otp *OneTimeCode) Consume(ctx Context) {
otp.ConsumedAt = sql.NullTime{Valid: true, Time: ctx.GetClock().Now()}
otp.ConsumedIP = NewNullIP(ctx.RemoteIP())
}