package main import ( "auth-server/logic" "context" "github.com/go-oauth2/oauth2/v4/errors" "github.com/go-oauth2/oauth2/v4/manage" "github.com/go-oauth2/oauth2/v4/server" "github.com/go-oauth2/oauth2/v4/store" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "log" "log/slog" "net/http" "net/url" "os" "strconv" ) func main() { config, err := logic.LoadConfig("./config.json") if err != nil { log.Fatalf("Error while reading config: %s", err) return } slog.Info("ContractCheckSum: " + config.ContractCheckSum) slog.Info("AuthServerPort: " + strconv.Itoa(config.AuthServerPort)) slog.Info("WalletFile: " + config.WalletFile) slog.Info("EndpointUrl: " + config.EndpointUrl) slog.Info("AccountSecret: " + config.AccountSecret) manager := manage.NewDefaultManager() manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg) // contract integration fileWallet, err := wallet.NewWalletFromFile(config.WalletFile) if err != nil { log.Fatalln("Wallet loading failed.", err) } acc := fileWallet.Accounts[0] if err := acc.Decrypt(config.AccountSecret, keys.NEP2ScryptParams()); err != nil { log.Fatal("Wallet decryption failed") } defer fileWallet.Close() // In idea we need an rpc-server(?) rpcClient, _ := rpcclient.New(context.Background(), config.EndpointUrl, rpcclient.Options{}) rpcActor, _ := actor.NewSimple(rpcClient, fileWallet.Accounts[0]) // smart contract check sum var contractHash, _ = util.Uint160DecodeStringLE(config.ContractCheckSum) // using blockchain storage blockchainStorage := logic.NewBlockchainStorage(rpcActor, contractHash) // using default in memory token storage manager.MustTokenStorage(store.NewMemoryTokenStore()) manager.MapClientStorage(blockchainStorage) srv := server.NewDefaultServer(manager) srv.SetAllowGetAccessRequest(true) srv.SetClientInfoHandler(server.ClientFormHandler) manager.SetRefreshTokenCfg(manage.DefaultRefreshTokenCfg) srv.SetInternalErrorHandler(func(err error) (re *errors.Response) { log.Println("Internal Error:", err.Error()) return }) srv.SetResponseErrorHandler(func(re *errors.Response) { log.Println("Response Error:", re.Error.Error()) }) http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("client_id") secret := r.URL.Query().Get("client_secret") secret_hash := logic.HashSecret(secret) _, err := blockchainStorage.CheckPassword(id, secret_hash) if err != nil { msg := "Credentials verification failed" slog.Warn(msg + " for client with id: " + id) w.WriteHeader(http.StatusForbidden) w.Write([]byte(msg)) } r.Form = make(url.Values) r.Form.Add("client_id", id) r.Form.Add("client_secret", secret_hash) r.Form.Add("grant_type", r.URL.Query().Get("grant_type")) r.Form.Add("scope", r.URL.Query().Get("scope")) srv.HandleTokenRequest(w, r) // grants access token }) http.HandleFunc("/register", func(writer http.ResponseWriter, request *http.Request) { id := request.URL.Query().Get("client_id") secret := request.URL.Query().Get("client_secret") // check whether client exists _, err := blockchainStorage.GetByID(context.Background(), id) if err == nil { msg := "Client already exists with id: " + id slog.Warn(msg) writer.WriteHeader(http.StatusBadRequest) writer.Write([]byte(msg)) return } // add client's credentials to blockchain err = blockchainStorage.Set(&logic.StorageClientInfo{ Id: id, Secret: logic.HashSecret(secret), // hash with sha256 }) if err != nil { slog.Error("Failed to register user with id: "+id, err) writer.WriteHeader(http.StatusInternalServerError) return } redirectURL := "/login.html" http.Redirect(writer, request, redirectURL, http.StatusSeeOther) }) // can access only with valid token (when logged in), deletes client http.HandleFunc("/delete", logic.ValidateToken(func(w http.ResponseWriter, r *http.Request) { id := r.Header.Get("client_id") errorMessage := "Fault during deleting client" // check whether client exists _, err := blockchainStorage.GetByID(context.Background(), id) if err != nil { msg := "Client not found with id: " + id slog.Warn(msg) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(msg)) return } err = blockchainStorage.Delete(id) if err != nil { slog.Error(errorMessage+" (caused by blockchain) with id: "+id, err) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) } }, srv)) // can access only with valid token (when logged in), deletes client and creates new one with another secret http.HandleFunc("/reset-secret", logic.ValidateToken(func(w http.ResponseWriter, r *http.Request) { id := r.Header.Get("client_id") secret := r.Header.Get("new_client_secret") errorMessage := "Fault during secret reset" // check whether client exists _, err := blockchainStorage.GetByID(context.Background(), id) if err != nil { msg := "Client not found with id: " + id slog.Warn(msg) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(msg)) return } err = blockchainStorage.Delete(id) if err != nil { slog.Error(errorMessage+" for client with id: "+id, err) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) } // add client with new credentials to blockchain err = blockchainStorage.Set(&logic.StorageClientInfo{ Id: id, Secret: logic.HashSecret(secret), // hash with sha256 }) if err != nil { slog.Error(errorMessage+" (caused by blockchain storage) for client with id: "+id, err) w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(errorMessage)) return } }, srv)) http.HandleFunc("/login.html", func(w http.ResponseWriter, r *http.Request) { outputHTML(w, r, "static/login.html") }) http.HandleFunc("/register.html", func(w http.ResponseWriter, r *http.Request) { outputHTML(w, r, "static/register.html") }) // can access only with valid token (when logged in) http.HandleFunc("/verify", logic.ValidateToken(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }, srv)) log.Fatal(http.ListenAndServe(":"+strconv.Itoa(config.AuthServerPort), nil)) } func outputHTML(w http.ResponseWriter, req *http.Request, filename string) { file, err := os.Open(filename) if err != nil { http.Error(w, err.Error(), 500) return } defer file.Close() fi, _ := file.Stat() http.ServeContent(w, req, file.Name(), fi.ModTime(), file) }