feat: login via passkey

This commit is contained in:
Jacky 2024-09-15 20:32:03 +08:00
parent 44c3180df7
commit bdfbbd0e8f
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
37 changed files with 1529 additions and 643 deletions

View file

@ -29,3 +29,7 @@ func Set(key interface{}, value interface{}, ttl time.Duration) {
func Get(key interface{}) (value interface{}, ok bool) {
return cache.Get(key)
}
func Del(key interface{}) {
cache.Del(key)
}

View file

@ -9,6 +9,7 @@ import (
"github.com/0xJacky/Nginx-UI/internal/cluster"
"github.com/0xJacky/Nginx-UI/internal/cron"
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/0xJacky/Nginx-UI/internal/passkey"
"github.com/0xJacky/Nginx-UI/internal/validation"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
@ -50,6 +51,7 @@ func InitAfterDatabase() {
cron.InitCronJobs,
cluster.RegisterPredefinedNodes,
analytic.RetrieveNodesStatus,
passkey.Init,
}
for _, v := range syncs {

View file

@ -51,7 +51,7 @@ func registerPredefinedUser() {
logger.Fatal(err)
}
u := query.Auth
u := query.User
_, err = u.First()
@ -63,7 +63,7 @@ func registerPredefinedUser() {
// Create a new user with the predefined name and password
pwd, _ := bcrypt.GenerateFromPassword([]byte(pUser.Password), bcrypt.DefaultCost)
err = u.Create(&model.Auth{
err = u.Create(&model.User{
Name: pUser.Name,
Password: string(pwd),
})

View file

@ -14,7 +14,7 @@ func RequireSecureSession() gin.HandlerFunc {
c.Next()
return
}
cUser := u.(*model.Auth)
cUser := u.(*model.User)
if !cUser.EnabledOTP() {
c.Next()
return

View file

@ -0,0 +1,46 @@
package passkey
import (
"github.com/0xJacky/Nginx-UI/internal/logger"
"github.com/0xJacky/Nginx-UI/settings"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
)
var instance *webauthn.WebAuthn
func Init() {
options := &settings.WebAuthnSettings
if !Enabled() {
logger.Debug("WebAuthn settings are not configured")
return
}
requireResidentKey := true
var err error
instance, err = webauthn.New(&webauthn.Config{
RPDisplayName: options.RPDisplayName,
RPID: options.RPID,
RPOrigins: options.RPOrigins,
AuthenticatorSelection: protocol.AuthenticatorSelection{
RequireResidentKey: &requireResidentKey,
UserVerification: "required",
},
})
if err != nil {
logger.Fatal(err)
}
}
func Enabled() bool {
options := &settings.WebAuthnSettings
if options.RPDisplayName == "" || options.RPID == "" || len(options.RPOrigins) == 0 {
return false
}
return true
}
func GetInstance() *webauthn.WebAuthn {
return instance
}

View file

@ -14,8 +14,8 @@ var (
ErrUserBanned = errors.New("user banned")
)
func Login(name string, password string) (user *model.Auth, err error) {
u := query.Auth
func Login(name string, password string) (user *model.User, err error) {
u := query.User
user, err = u.Where(u.Name.Eq(name)).First()
if err != nil {

View file

@ -19,7 +19,7 @@ var (
ErrRecoveryCode = errors.New("invalid recovery code")
)
func VerifyOTP(user *model.Auth, otp, recoveryCode string) (err error) {
func VerifyOTP(user *model.User, otp, recoveryCode string) (err error) {
if otp != "" {
decrypted, err := crypto.AesDecrypt(user.OTPSecret)
if err != nil {

View file

@ -26,9 +26,9 @@ func BuildCacheTokenKey(token string) string {
return sb.String()
}
func GetUser(name string) (user *model.Auth, err error) {
func GetUser(name string) (user *model.User, err error) {
db := model.UseDB()
user = &model.Auth{}
user = &model.User{}
err = db.Where("name", name).First(user).Error
if err != nil {
return
@ -41,7 +41,7 @@ func DeleteToken(token string) {
_, _ = q.Where(q.Token.Eq(token)).Delete()
}
func GetTokenUser(token string) (*model.Auth, bool) {
func GetTokenUser(token string) (*model.User, bool) {
q := query.AuthToken
authToken, err := q.Where(q.Token.Eq(token)).First()
if err != nil {
@ -53,12 +53,12 @@ func GetTokenUser(token string) (*model.Auth, bool) {
return nil, false
}
u := query.Auth
u := query.User
user, err := u.FirstByID(authToken.UserID)
return user, err == nil
}
func GenerateJWT(user *model.Auth) (string, error) {
func GenerateJWT(user *model.User) (string, error) {
claims := JWTClaims{
Name: user.Name,
UserID: user.ID,
@ -114,7 +114,7 @@ func ValidateJWT(token string) (claims *JWTClaims, err error) {
return
}
func CurrentUser(token string) (u *model.Auth, err error) {
func CurrentUser(token string) (u *model.User, err error) {
// validate token
var claims *JWTClaims
claims, err = ValidateJWT(token)
@ -123,7 +123,7 @@ func CurrentUser(token string) (u *model.Auth, err error) {
}
// get user by id
user := query.Auth
user := query.User
u, err = user.FirstByID(claims.UserID)
if err != nil {
return