Skip to content

Commit

Permalink
updated passkey authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
leobrada committed Jul 18, 2023
1 parent 602910c commit 3611cbc
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 265 deletions.
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
module github.com/vs-uulm/ztsfc_http_pep

go 1.17
go 1.20

require (
github.com/go-webauthn/webauthn v0.8.2
github.com/go-webauthn/webauthn v0.8.4
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/leobrada/golang_convenience_tools v0.0.0-20230226145447-cbd5896c5ecf
github.com/vs-uulm/ztsfc_http_attributes v0.0.0-20230526141130-defc0494f551
github.com/vs-uulm/ztsfc_http_attributes v0.0.0-20230718145859-e9c5fbffbfa7
github.com/vs-uulm/ztsfc_http_logger v0.0.0-20220504121928-852f30c337e5
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-webauthn/revoke v0.1.9 // indirect
github.com/go-webauthn/x v0.1.4 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/google/go-tpm v0.3.3 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/sirupsen/logrus v1.9.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect
)
235 changes: 16 additions & 219 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions internal/app/authorization/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func prepareAuthRequest(authoReq *http.Request, cpm *metadata.CpMetadata) {
q := authoReq.URL.Query()
q.Set("user", cpm.User)
q.Set("pwAuthenticated", strconv.FormatBool(cpm.PwAuthenticated))
q.Set("passkeyAuthenticated", strconv.FormatBool(cpm.PasskeyAuthenticated))
q.Set("certAuthenticated", strconv.FormatBool(cpm.CertAuthenticated))
q.Set("resource", cpm.Resource)
q.Set("action", cpm.Action)
Expand Down
31 changes: 22 additions & 9 deletions internal/app/basic_auth/basic_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func UserSessionIsValid(sysLogger *logger.Logger, w http.ResponseWriter, req *ht
}

username := token.Claims.(jwt.MapClaims)["sub"].(string)

authentication_method := token.Claims.(jwt.MapClaims)["iss"].(string)
/*
failedAttempts, err := getFailedAuthAttempts(sysLogger, username)
if err != nil {
Expand All @@ -51,7 +51,16 @@ func UserSessionIsValid(sysLogger *logger.Logger, w http.ResponseWriter, req *ht
*/

cpm.User = username
cpm.PwAuthenticated = true
if authentication_method == "password" {
cpm.PwAuthenticated = true
cpm.PasskeyAuthenticated = false
} else if authentication_method == "passkey" {
cpm.PwAuthenticated = false
cpm.PasskeyAuthenticated = true
} else {
return false
}

cpm.CertAuthenticated = performX509auth(req)

return true
Expand Down Expand Up @@ -163,7 +172,7 @@ func performPasswdAuth(sysLogger *logger.Logger, w http.ResponseWriter, req *htt
}

// pushAuthSuccess(sysLogger, username)
if err = setCookieAndFinishAuthentication(sysLogger, w, req, username); err != nil {
if err = setCookieAndFinishAuthentication(sysLogger, w, req, username, "password"); err != nil {
HandleFormResponse("Internal Error", w)
sysLogger.Errorf("basic_auth: validPassword(): For user '%s' no session cookie could be created.", username)
if err := pushAuthFail(sysLogger, username); err != nil {
Expand All @@ -179,9 +188,9 @@ func performPasswdAuth(sysLogger *logger.Logger, w http.ResponseWriter, req *htt
}
}

func setCookieAndFinishAuthentication(sysLogger *logger.Logger, w http.ResponseWriter, req *http.Request, username string) error {
func setCookieAndFinishAuthentication(sysLogger *logger.Logger, w http.ResponseWriter, req *http.Request, username, authType string) error {
// Create JWT
jwtToken, err := createJWToken(username)
jwtToken, err := createJWToken(username, authType)
if err != nil {
return fmt.Errorf("%v", err)
}
Expand All @@ -202,9 +211,9 @@ func setCookieAndFinishAuthentication(sysLogger *logger.Logger, w http.ResponseW
return nil
}

func createJWToken(username string) (string, error) {
func createJWToken(username, authType string) (string, error) {
claims := &jwt.RegisteredClaims{
Issuer: "ztsfc_pep",
Issuer: authType,
Subject: username,
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Expand Down Expand Up @@ -306,15 +315,19 @@ func getFailedAuthAttempts(sysLogger *logger.Logger, username string) (int, erro
}

func validUser(sysLogger *logger.Logger, username string) bool {
_, ok := config.Config.BasicAuth.Passwd.PasswdList[username]
_, ok := config.Config.BasicAuth.Passwd.PasswdListByUsername[username]
if !ok {
return false
}
return true
}

func validPassword(sysLogger *logger.Logger, username, password string) bool {
if calcSaltedHash(password, config.Config.BasicAuth.Passwd.PasswdList[username].Salt) == config.Config.BasicAuth.Passwd.PasswdList[username].Digest {
// Check if user has password authentication enabled
if config.Config.BasicAuth.Passwd.PasswdListByUsername[username].Digest == "" {
return false
}
if calcSaltedHash(password, config.Config.BasicAuth.Passwd.PasswdListByUsername[username].Salt) == config.Config.BasicAuth.Passwd.PasswdListByUsername[username].Digest {
return true
} else {
return false
Expand Down
14 changes: 7 additions & 7 deletions internal/app/basic_auth/passkey_authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ func BeginPasskeyRegistration(w http.ResponseWriter, r *http.Request) {
username := extractUsername(w, r)

// Check if username exists
user, ok := config.Config.BasicAuth.Passwd.PasswdList[username]
user, ok := config.Config.BasicAuth.Passwd.PasswdListByUsername[username]
if !ok {
JSONResponse(w, "User does not exist", http.StatusBadRequest)
return
Expand All @@ -302,7 +302,7 @@ func BeginPasskeyRegistration(w http.ResponseWriter, r *http.Request) {
JSONResponse(w, "Error creating new user passkey options", http.StatusBadRequest)
return
}
sessionData.UserDisplayName = user.User
sessionData.UserID = user.ID
sessionstore[sessionData.Challenge] = sessionData

// store the sessionData values
Expand Down Expand Up @@ -331,7 +331,7 @@ func FinishPasskeyRegistration(w http.ResponseWriter, r *http.Request) {
// Get the session data stored from the function above
session := sessionstore[response.Response.CollectedClientData.Challenge]

user, ok := config.Config.BasicAuth.Passwd.PasswdList[session.UserDisplayName] // Get the user
user, ok := config.Config.BasicAuth.Passwd.PasswdListByID[string(session.UserID)] // Get the user
if !ok {
fmt.Printf("Error: user could not restored from active session\n")
}
Expand Down Expand Up @@ -378,7 +378,7 @@ func BeginPasskeyLogin(w http.ResponseWriter, r *http.Request) {
username := extractUsername(w, r)

// Check if username exists
user, ok := config.Config.BasicAuth.Passwd.PasswdList[username]
user, ok := config.Config.BasicAuth.Passwd.PasswdListByUsername[username]
if !ok {
JSONResponse(w, "User does not exist", http.StatusBadRequest)
return
Expand All @@ -398,7 +398,7 @@ func BeginPasskeyLogin(w http.ResponseWriter, r *http.Request) {
}

// store the session values
session.UserDisplayName = user.User
session.UserID = user.ID
sessionstore[session.Challenge] = session

JSONResponse(w, options, http.StatusOK) // return the options generated
Expand All @@ -416,7 +416,7 @@ func FinishPasskeyLogin(sysLogger *logger.Logger, w http.ResponseWriter, r *http
// Get the session data stored from the function above
session := sessionstore[response.Response.CollectedClientData.Challenge]

user, ok := config.Config.BasicAuth.Passwd.PasswdList[session.UserDisplayName] // Get the user
user, ok := config.Config.BasicAuth.Passwd.PasswdListByID[string(session.UserID)] // Get the user
if !ok {
JSONResponse(w, "User login could not be finished", http.StatusBadRequest)
return
Expand All @@ -430,7 +430,7 @@ func FinishPasskeyLogin(sysLogger *logger.Logger, w http.ResponseWriter, r *http

fmt.Println("All Good...")
// If login was successful, handle next steps
if err = setCookieAndFinishAuthentication(sysLogger, w, r, user.User); err != nil {
if err = setCookieAndFinishAuthentication(sysLogger, w, r, user.User, "passkey"); err != nil {
JSONResponse(w, "User session cookie could not be created", http.StatusBadRequest)
return
}
Expand Down
8 changes: 5 additions & 3 deletions internal/app/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ type PepT struct {
type BasicAuthT struct {
Passwd PasswdT `yaml:"passwd"`
Session SessionT `yaml:"session"`
RPID string `yaml:"rpid"`
}

type PasswdT struct {
PathToPasswd string `yaml:"path_to_passwd"`
PasswdList map[string]*ShadowT // Key is username
WaitPasswdList sync.WaitGroup
PathToPasswd string `yaml:"path_to_passwd"`
PasswdListByUsername map[string]*ShadowT // Key is username
PasswdListByID map[string]*ShadowT // Key is ID (string)
WaitPasswdList sync.WaitGroup
}

type ShadowT struct {
Expand Down
16 changes: 12 additions & 4 deletions internal/app/init/init_config_basic_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ func initBasicAuth(sysLogger *logger.Logger) error {

func initPasswd(sysLogger *logger.Logger) error {
var err error
config.Config.BasicAuth.Passwd.PasswdList = make(map[string]*config.ShadowT)
config.Config.BasicAuth.Passwd.PasswdListByUsername = make(map[string]*config.ShadowT)
config.Config.BasicAuth.Passwd.PasswdListByID = make(map[string]*config.ShadowT)

if config.Config.BasicAuth.Passwd.PathToPasswd == "" {
return errors.New("initPasswd(): path to passwd file is not defined")
Expand Down Expand Up @@ -80,7 +81,8 @@ func initPasswd(sysLogger *logger.Logger) error {
continue
}

config.Config.BasicAuth.Passwd.PasswdList[values[0]] = &config.ShadowT{User: values[0], ID: []byte(values[1]), Salt: values[2], Digest: values[3]}
config.Config.BasicAuth.Passwd.PasswdListByUsername[values[0]] = &config.ShadowT{User: values[0], ID: []byte(values[1]), Salt: values[2], Digest: values[3]}
config.Config.BasicAuth.Passwd.PasswdListByID[values[1]] = &config.ShadowT{User: values[0], ID: []byte(values[1]), Salt: values[2], Digest: values[3]}
}

if err = scanner.Err(); err != nil {
Expand Down Expand Up @@ -115,11 +117,17 @@ func initSession(sysLogger *logger.Logger) error {
}

func initWebAuthnContext(sysLogger *logger.Logger) error {
// Collect RPOrigins
rporigins := []string{}
for _, value := range config.Config.ServicePool {
rporigins = append(rporigins, "https://"+value.Sni)
}

// Create a new WebAuthn config
webAuthnConfig := &webauthn.Config{
RPDisplayName: "ZTSFC WebAuthn",
RPID: "wiki.bwnet.informatik.uni-ulm.de",
RPOrigins: []string{"https://wiki.bwnet.informatik.uni-ulm.de"},
RPID: config.Config.BasicAuth.RPID,
RPOrigins: rporigins,
}

// Create a new WebAuthn object
Expand Down
34 changes: 19 additions & 15 deletions internal/app/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,21 @@ type Sf struct {
// request. The struct can be passed across the PEP, such that several
// components can collect different information in here.
type CpMetadata struct {
AuthDecision bool
AuthReason string
User string
PwAuthenticated bool
CertAuthenticated bool
Resource string
Action string
Device string
Location string
ConnectionSecurity string
UserAgent string
RequestProtocol float64
SFC []Sf
SFP []struct {
AuthDecision bool
AuthReason string
User string
PwAuthenticated bool
PasskeyAuthenticated bool
CertAuthenticated bool
Resource string
Action string
Device string
Location string
ConnectionSecurity string
UserAgent string
RequestProtocol float64
SFC []Sf
SFP []struct {
Name string
URL string
}
Expand All @@ -51,6 +52,7 @@ func (cpm *CpMetadata) ClearMetadata() {
cpm.AuthReason = ""
cpm.User = ""
cpm.PwAuthenticated = false
cpm.PasskeyAuthenticated = false
cpm.CertAuthenticated = false
cpm.Resource = ""
cpm.Action = ""
Expand All @@ -72,6 +74,7 @@ func (cpm *CpMetadata) String() string {
authReason := fmt.Sprintf("AuthReason=%s, ", cpm.AuthReason)
user := fmt.Sprintf("User=%s, ", cpm.User)
pwAuthenticated := fmt.Sprintf("PwAuthenticated=%t, ", cpm.PwAuthenticated)
passkeyAuthenticated := fmt.Sprintf("PasskeyAuthenticaed=%t, ", cpm.PasskeyAuthenticated)
certAuthenticated := fmt.Sprintf("CertAuthenticated=%t, ", cpm.CertAuthenticated)
resource := fmt.Sprintf("Resource=%s, ", cpm.Resource)
action := fmt.Sprintf("Action=%s, ", cpm.Action)
Expand All @@ -80,7 +83,7 @@ func (cpm *CpMetadata) String() string {
connectionSecurity := fmt.Sprintf("ConnectionSecurity=%s, ", cpm.ConnectionSecurity)
userAgent := fmt.Sprintf("UserAgent=%s", cpm.UserAgent)
requestProtocol := fmt.Sprintf("RequestProtocol=%f", cpm.RequestProtocol)
mdString := header + authDecision + authReason + user + pwAuthenticated + certAuthenticated +
mdString := header + authDecision + authReason + user + pwAuthenticated + passkeyAuthenticated + certAuthenticated +
resource + action + device + location + connectionSecurity + userAgent + requestProtocol

return mdString
Expand All @@ -89,6 +92,7 @@ func (cpm *CpMetadata) String() string {
func CollectMetadata(clientReq *http.Request, cpm *CpMetadata) {
// User is set by BasicAuth()
// PwAuthenticated is set by BasicAuth()
// PasskeyAuthenticated is set by BasicAuth()
// CertAuthenticated is set by BasicAuth()
collectResource(clientReq, cpm)
collectAction(clientReq, cpm)
Expand Down

0 comments on commit 3611cbc

Please sign in to comment.