This commit is contained in:
parent
a6bcbaf5e9
commit
7ae75caaf5
3 changed files with 29 additions and 1 deletions
|
@ -9,6 +9,7 @@ package auth
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/goutils/other"
|
||||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ import (
|
||||||
|
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
webauthn *webauthn.WebAuthn
|
webauthn *webauthn.WebAuthn
|
||||||
|
recentlyUsedTotpTokens map[string]time.Time
|
||||||
}
|
}
|
||||||
type LoginNextState uint8
|
type LoginNextState uint8
|
||||||
|
|
||||||
|
@ -33,6 +35,17 @@ const (
|
||||||
LoginStartPasskey // Login starts with a passkey
|
LoginStartPasskey // Login starts with a passkey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func New(webauthnConfig *webauthn.Config) (*Authenticator, error) {
|
||||||
|
webauthn, err := webauthn.New(webauthnConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, other.Error("auth", "failed to create webauthn handler", err)
|
||||||
|
}
|
||||||
|
return &Authenticator{
|
||||||
|
webauthn: webauthn,
|
||||||
|
recentlyUsedTotpTokens: make(map[string]time.Time),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func calcAccessExpirationTimestamp() time.Time {
|
func calcAccessExpirationTimestamp() time.Time {
|
||||||
// For now, the default expiration is one month after creation
|
// For now, the default expiration is one month after creation
|
||||||
// though "never" might also be a good option
|
// though "never" might also be a good option
|
||||||
|
|
|
@ -12,6 +12,7 @@ var (
|
||||||
// A user may not login, for whatever reason
|
// A user may not login, for whatever reason
|
||||||
ErrCantLogin = errors.New("user can't login")
|
ErrCantLogin = errors.New("user can't login")
|
||||||
ErrDecryptionFailure = errors.New("failed to decrypt content")
|
ErrDecryptionFailure = errors.New("failed to decrypt content")
|
||||||
|
ErrTotpRecentlyUsed = errors.New("totp token was used too recently")
|
||||||
)
|
)
|
||||||
|
|
||||||
type CombinedError struct {
|
type CombinedError struct {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
|
// Some helpful comments from: https://waters.me/internet/google-authenticator-implementation-note-key-length-token-reuse/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -15,11 +17,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const totpUnverifiedSuffix = "-NOT_VERIFIED"
|
const totpUnverifiedSuffix = "-NOT_VERIFIED"
|
||||||
|
const totpTokenNoLongerRecentlyUsed = time.Second * 90
|
||||||
|
|
||||||
func (a *Authenticator) PerformTotpLogin(
|
func (a *Authenticator) PerformTotpLogin(
|
||||||
username string,
|
username string,
|
||||||
totpToken string,
|
totpToken string,
|
||||||
) (LoginNextState, string, error) {
|
) (LoginNextState, string, error) {
|
||||||
|
// First check if that token has been seen recently for that user
|
||||||
|
if timestamp, found := a.recentlyUsedTotpTokens[totpToken+"+"+username]; found {
|
||||||
|
if timestamp.Add(totpTokenNoLongerRecentlyUsed).After(time.Now()) {
|
||||||
|
return LoginNextFailure, "", ErrTotpRecentlyUsed
|
||||||
|
} else {
|
||||||
|
delete(a.recentlyUsedTotpTokens, totpToken+"+"+username)
|
||||||
|
}
|
||||||
|
}
|
||||||
if ok, err := a.canUsernameLogin(username); !ok {
|
if ok, err := a.canUsernameLogin(username); !ok {
|
||||||
return 0, "", other.Error("auth", "user may not login", err)
|
return 0, "", other.Error("auth", "user may not login", err)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +72,9 @@ func (a *Authenticator) PerformTotpLogin(
|
||||||
ErrInvalidCombination,
|
ErrInvalidCombination,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.recentlyUsedTotpTokens[totpToken+"+"+username] = time.Now()
|
||||||
|
|
||||||
token := models.AccessToken{
|
token := models.AccessToken{
|
||||||
User: *acc,
|
User: *acc,
|
||||||
UserId: acc.ID,
|
UserId: acc.ID,
|
||||||
|
|
Loading…
Reference in a new issue