mirror of
https://github.com/AlistGo/alist.git
synced 2025-06-05 18:04:39 +08:00
feat: add support for client-side discoverable WebAuthn login (#5722)
* Add support for client-side discoverable in begin login Use `(*webauthn.WebAuthn).BeginDiscoverableLogin()` to handle client-side discoverable login. * Upgrade github.com/go-webauthn/webauthn to v0.10.0 Upgrade [go-webauthn/webauthn](github.com/go-webauthn/webauthn) library to latest. The convenient finish login function (as FinishDiscoverableLogin) for discoverable functions has been added in the v0.9.0. [^1] --- [^1]: https://github.com/go-webauthn/webauthn/releases/tag/v0.9.0 * Add support for client-side discoverable in validating login Use `(*webauthn.WebAuthn).FinishDiscoverableLogin()` to handle client-side discoverable login. > **NOTE**: - The first param `rawID` in this callback function is unnecessary to check, it's handled by the third-party webauthn library later. - `userHandle` param is equal to the ID returned by (User).WebAuthnID() function.
This commit is contained in:
@ -2,6 +2,7 @@ package handles
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
@ -13,6 +14,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
@ -22,28 +24,30 @@ func BeginAuthnLogin(c *gin.Context) {
|
||||
common.ErrorStrResp(c, "WebAuthn is not enabled", 403)
|
||||
return
|
||||
}
|
||||
username := c.Query("username")
|
||||
if username == "" {
|
||||
common.ErrorStrResp(c, "empty or no username provided", 400)
|
||||
return
|
||||
}
|
||||
user, err := db.GetUserByName(username)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
authnInstance, err := authn.NewAuthnInstance(c.Request)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
|
||||
options, sessionData, err := authnInstance.BeginLogin(user)
|
||||
|
||||
var (
|
||||
options *protocol.CredentialAssertion
|
||||
sessionData *webauthn.SessionData
|
||||
)
|
||||
if username := c.Query("username"); username != "" {
|
||||
var user *model.User
|
||||
user, err = db.GetUserByName(username)
|
||||
if err == nil {
|
||||
options, sessionData, err = authnInstance.BeginLogin(user)
|
||||
}
|
||||
} else { // client-side discoverable login
|
||||
options, sessionData, err = authnInstance.BeginDiscoverableLogin()
|
||||
}
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
|
||||
val, err := json.Marshal(sessionData)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
@ -61,20 +65,13 @@ func FinishAuthnLogin(c *gin.Context) {
|
||||
common.ErrorStrResp(c, "WebAuthn is not enabled", 403)
|
||||
return
|
||||
}
|
||||
username := c.Query("username")
|
||||
user, err := db.GetUserByName(username)
|
||||
authnInstance, err := authn.NewAuthnInstance(c.Request)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
|
||||
sessionDataString := c.GetHeader("session")
|
||||
|
||||
authnInstance, err := authn.NewAuthnInstance(c.Request)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
sessionDataBytes, err := base64.StdEncoding.DecodeString(sessionDataString)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
@ -87,8 +84,28 @@ func FinishAuthnLogin(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = authnInstance.FinishLogin(user, sessionData, c.Request)
|
||||
var user *model.User
|
||||
if username := c.Query("username"); username != "" {
|
||||
user, err = db.GetUserByName(username)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
_, err = authnInstance.FinishLogin(user, sessionData, c.Request)
|
||||
} else { // client-side discoverable login
|
||||
_, err = authnInstance.FinishDiscoverableLogin(func(_, userHandle []byte) (webauthn.User, error) {
|
||||
// first param `rawID` in this callback function is equal to ID in webauthn.Credential,
|
||||
// but it's unnnecessary to check it.
|
||||
// userHandle param is equal to (User).WebAuthnID().
|
||||
userID := uint(binary.LittleEndian.Uint64(userHandle))
|
||||
user, err = db.GetUserById(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}, sessionData, c.Request)
|
||||
}
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
|
Reference in New Issue
Block a user