package auth import ( "encoding/json" "net/http" "time" "git.mstar.dev/mstar/goutils/other" "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/webauthn" "gorm.io/gorm/clause" "git.mstar.dev/mstar/linstrom/storage-new/dbgen" "git.mstar.dev/mstar/linstrom/storage-new/models" ) func (a *Authenticator) StartPasskeyLogin(username string) (*protocol.CredentialAssertion, error) { if ok, err := a.canUsernameLogin(username); !ok { return nil, other.Error("auth", "user may not login", err) } acc, err := dbgen.User.Where(dbgen.User.Username.Eq(username)).First() if err != nil { return nil, err } wrappedAcc := fakeUser{acc} options, session, err := a.webauthn.BeginLogin(&wrappedAcc) if err != nil { return nil, err } pkeySession := models.LoginProcessToken{ User: *acc, UserId: acc.ID, ExpiresAt: time.Now().Add(time.Minute * 3), Token: string(other.Must(json.Marshal(session))), } err = dbgen.LoginProcessToken.Clauses(clause.OnConflict{UpdateAll: true}).Create(&pkeySession) if err != nil { return nil, err } return options, nil } func (a *Authenticator) CompletePasskeyLogin( username string, response *http.Request, ) (accessToken string, err error) { // Get user in question acc, err := dbgen.User.Where(dbgen.User.Username.Eq(username)).First() if err != nil { return "", other.Error("auth", "failed to get user for passkey login completion", err) } // Get latest login token data loginToken, err := dbgen.LoginProcessToken.Where(dbgen.LoginProcessToken.UserId.Eq(acc.ID)). First() if err != nil { return "", other.Error( "auth", "failed to get user's login token for passkey login completion", err, ) } // Check if that token has expired if loginToken.ExpiresAt.Before(time.Now()) { return "", ErrProcessTimeout } var pkeySession webauthn.SessionData err = json.Unmarshal([]byte(loginToken.Token), &pkeySession) if err != nil { return "", other.Error("auth", "failed to unmarshal passkey session for user", err) } // Hand data to webauthn for completion newSession, err := a.webauthn.FinishLogin(&fakeUser{acc}, pkeySession, response) jsonSession, err := json.Marshal(newSession) if err != nil { return "", err } // Update credentials _, err = dbgen.UserAuthMethod.Where(dbgen.UserAuthMethod.Token.Like("%"+string(jsonSession)+"%")). Update(dbgen.UserAuthMethod.Token, jsonSession) if err != nil { return "", err } // And delete the login token dbgen.LoginProcessToken.Where(dbgen.LoginProcessToken.UserId.Eq(acc.ID)).Delete(loginToken) dbAccessToken := models.AccessToken{ User: *acc, UserId: acc.ID, ExpiresAt: calcAccessExpirationTimestamp(), } err = dbgen.AccessToken.Omit(dbgen.AccessToken.Token).Create(&dbAccessToken) if err != nil { return "", err } return dbAccessToken.Token, nil } func (a *Authenticator) StartPasskeyRegistration(username string) error { // p.l.Infof("begin registration") // // // can we actually do not use the username at all? // username, err := getUsername(r) // // if err != nil { // p.l.Errorf("can't get username: %s", err.Error()) // JSONResponse(w, fmt.Sprintf("can't get username: %s", err.Error()), http.StatusBadRequest) // // return // } // // user := p.userStore.GetOrCreateUser(username) // // options, session, err := p.webAuthn.BeginRegistration(user) // // if err != nil { // msg := fmt.Sprintf("can't begin registration: %s", err.Error()) // p.l.Errorf(msg) // JSONResponse(w, msg, http.StatusBadRequest) // // return // } // // // Make a session key and store the sessionData values // t, err := p.sessionStore.GenSessionID() // // if err != nil { // p.l.Errorf("can't generate session id: %s", err.Error()) // JSONResponse( // w, // fmt.Sprintf("can't generate session id: %s", err.Error()), // http.StatusInternalServerError, // ) // // return // } // // p.sessionStore.SaveSession(t, session) // p.setSessionCookie(w, t) // // // return the options generated with the session key // // options.publicKey contain our registration options // JSONResponse(w, options, http.StatusOK) panic("Not implemented") // TODO: Implement me } func (a *Authenticator) CompletePasskeyRegistration(username string) error { // // Get the session key from cookie // sid, err := r.Cookie(p.cookieSettings.Name) // // if err != nil { // p.l.Errorf("can't get session id: %s", err.Error()) // JSONResponse(w, fmt.Sprintf("can't get session id: %s", err.Error()), http.StatusBadRequest) // // return // } // // // Get the session data stored from the function above // session, ok := p.sessionStore.GetSession(sid.Value) // // if !ok { // p.l.Errorf("can't get session data") // JSONResponse(w, "can't get session data", http.StatusBadRequest) // // return // } // // user := p.userStore.GetUserByWebAuthnId(session.UserID) // Get the user // // credential, err := p.webAuthn.FinishRegistration(user, *session, r) // // if err != nil { // msg := fmt.Sprintf("can't finish registration: %s", err.Error()) // p.l.Errorf(msg) // // p.deleteSessionCookie(w) // JSONResponse(w, msg, http.StatusBadRequest) // // return // } // // // If creation was successful, store the credential object // user.PutCredential(*credential) // p.userStore.SaveUser(user) // // p.sessionStore.DeleteSession(sid.Value) // p.deleteSessionCookie(w) // // p.l.Infof("finish registration") // JSONResponse(w, "Registration Success", http.StatusOK) panic("Not implemented") // TODO: Implement me }