2024-01-10 14:48:03 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-01-14 17:28:04 +00:00
|
|
|
"auth-server/logic"
|
2024-01-10 14:48:03 +00:00
|
|
|
"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"
|
2024-01-14 21:35:00 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2024-01-14 17:28:04 +00:00
|
|
|
"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"
|
2024-01-10 14:48:03 +00:00
|
|
|
"log"
|
|
|
|
"log/slog"
|
|
|
|
"net/http"
|
2024-01-15 17:19:23 +00:00
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
2024-01-10 14:48:03 +00:00
|
|
|
)
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
func main() {
|
|
|
|
config, err := logic.LoadConfig("./config.json")
|
2024-01-13 19:36:23 +00:00
|
|
|
if err != nil {
|
2024-01-14 17:28:04 +00:00
|
|
|
log.Fatalf("Error while reading config: %s", err)
|
|
|
|
return
|
2024-01-14 14:48:25 +00:00
|
|
|
}
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
slog.Info("ContractCheckSum: " + config.ContractCheckSum)
|
|
|
|
slog.Info("AuthServerPort: " + strconv.Itoa(config.AuthServerPort))
|
|
|
|
slog.Info("WalletFile: " + config.WalletFile)
|
2024-01-15 14:49:10 +00:00
|
|
|
slog.Info("EndpointUrl: " + config.EndpointUrl)
|
|
|
|
slog.Info("AccountSecret: " + config.AccountSecret)
|
2024-01-14 14:48:25 +00:00
|
|
|
|
2024-01-10 14:48:03 +00:00
|
|
|
manager := manage.NewDefaultManager()
|
|
|
|
manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
|
|
|
|
|
2024-01-13 19:16:40 +00:00
|
|
|
// contract integration
|
2024-01-15 14:49:10 +00:00
|
|
|
fileWallet, err := wallet.NewWalletFromFile(config.WalletFile)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln("Wallet loading failed.", err)
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:35:00 +00:00
|
|
|
acc := fileWallet.Accounts[0]
|
2024-01-15 02:48:53 +00:00
|
|
|
if err := acc.Decrypt(config.AccountSecret, keys.NEP2ScryptParams()); err != nil {
|
2024-01-14 21:35:00 +00:00
|
|
|
log.Fatal("Wallet decryption failed")
|
|
|
|
}
|
2024-01-13 19:16:40 +00:00
|
|
|
defer fileWallet.Close()
|
|
|
|
|
2024-01-15 02:48:53 +00:00
|
|
|
// In idea we need an rpc-server(?)
|
|
|
|
rpcClient, _ := rpcclient.New(context.Background(), config.EndpointUrl, rpcclient.Options{})
|
2024-01-13 19:16:40 +00:00
|
|
|
rpcActor, _ := actor.NewSimple(rpcClient, fileWallet.Accounts[0])
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
// smart contract check sum
|
2024-01-14 21:35:00 +00:00
|
|
|
var contractHash, _ = util.Uint160DecodeStringLE(config.ContractCheckSum)
|
2024-01-14 17:28:04 +00:00
|
|
|
|
|
|
|
// using blockchain storage
|
|
|
|
blockchainStorage := logic.NewBlockchainStorage(rpcActor, contractHash)
|
2024-01-10 14:48:03 +00:00
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
// using default in memory token storage
|
|
|
|
manager.MustTokenStorage(store.NewMemoryTokenStore())
|
2024-01-10 14:48:03 +00:00
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
manager.MapClientStorage(blockchainStorage)
|
2024-01-10 14:48:03 +00:00
|
|
|
|
|
|
|
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())
|
|
|
|
})
|
|
|
|
|
2024-01-10 17:03:10 +00:00
|
|
|
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
2024-01-14 21:35:00 +00:00
|
|
|
id := r.URL.Query().Get("client_id")
|
|
|
|
secret := r.URL.Query().Get("client_secret")
|
2024-01-15 14:49:10 +00:00
|
|
|
secret_hash := logic.HashSecret(secret)
|
2024-01-14 17:28:04 +00:00
|
|
|
|
2024-01-15 14:49:10 +00:00
|
|
|
_, err := blockchainStorage.CheckPassword(id, secret_hash)
|
2024-01-14 14:48:25 +00:00
|
|
|
if err != nil {
|
2024-01-14 17:28:04 +00:00
|
|
|
msg := "Credentials verification failed"
|
|
|
|
slog.Warn(msg + " for client with id: " + id)
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
|
|
|
w.Write([]byte(msg))
|
2024-01-14 14:48:25 +00:00
|
|
|
}
|
2024-01-14 17:28:04 +00:00
|
|
|
|
2024-01-15 14:49:10 +00:00
|
|
|
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"))
|
|
|
|
|
2024-01-15 17:19:23 +00:00
|
|
|
srv.HandleTokenRequest(w, r) // grants access token
|
2024-01-10 14:48:03 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
http.HandleFunc("/register", func(writer http.ResponseWriter, request *http.Request) {
|
2024-01-15 17:19:23 +00:00
|
|
|
id := request.URL.Query().Get("client_id")
|
|
|
|
secret := request.URL.Query().Get("client_secret")
|
2024-01-10 14:48:03 +00:00
|
|
|
|
|
|
|
// check whether client exists
|
2024-01-14 17:28:04 +00:00
|
|
|
_, err := blockchainStorage.GetByID(context.Background(), id)
|
2024-01-10 14:48:03 +00:00
|
|
|
if err == nil {
|
2024-01-14 17:28:04 +00:00
|
|
|
msg := "Client already exists with id: " + id
|
|
|
|
slog.Warn(msg)
|
2024-01-10 14:48:03 +00:00
|
|
|
writer.WriteHeader(http.StatusBadRequest)
|
2024-01-14 17:28:04 +00:00
|
|
|
writer.Write([]byte(msg))
|
2024-01-10 14:48:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
// add client's credentials to blockchain
|
|
|
|
err = blockchainStorage.Set(&logic.StorageClientInfo{
|
|
|
|
Id: id,
|
|
|
|
Secret: logic.HashSecret(secret), // hash with sha256
|
|
|
|
})
|
|
|
|
|
2024-01-14 14:48:25 +00:00
|
|
|
if err != nil {
|
2024-01-14 17:28:04 +00:00
|
|
|
slog.Error("Failed to register user with id: "+id, err)
|
|
|
|
writer.WriteHeader(http.StatusInternalServerError)
|
2024-01-14 14:48:25 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-15 17:19:23 +00:00
|
|
|
redirectURL := "/login.html"
|
|
|
|
http.Redirect(writer, request, redirectURL, http.StatusSeeOther)
|
2024-01-10 14:48:03 +00:00
|
|
|
})
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
// 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")
|
2024-01-14 17:54:54 +00:00
|
|
|
errorMessage := "Fault during deleting client"
|
2024-01-14 17:28:04 +00:00
|
|
|
|
2024-01-15 14:49:10 +00:00
|
|
|
// 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)
|
2024-01-14 17:28:04 +00:00
|
|
|
if err != nil {
|
2024-01-14 17:54:54 +00:00
|
|
|
slog.Error(errorMessage+" (caused by blockchain) with id: "+id, err)
|
2024-01-14 17:28:04 +00:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2024-01-14 17:54:54 +00:00
|
|
|
w.Write([]byte(errorMessage))
|
2024-01-14 17:28:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}, 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"
|
|
|
|
|
2024-01-15 14:49:10 +00:00
|
|
|
// check whether client exists
|
|
|
|
_, err := blockchainStorage.GetByID(context.Background(), id)
|
2024-01-14 17:28:04 +00:00
|
|
|
if err != nil {
|
2024-01-15 14:49:10 +00:00
|
|
|
msg := "Client not found with id: " + id
|
|
|
|
slog.Warn(msg)
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Write([]byte(msg))
|
|
|
|
return
|
2024-01-14 17:28:04 +00:00
|
|
|
}
|
2024-01-10 14:48:03 +00:00
|
|
|
|
2024-01-15 14:49:10 +00:00
|
|
|
err = blockchainStorage.Delete(id)
|
2024-01-10 14:48:03 +00:00
|
|
|
if err != nil {
|
2024-01-15 14:49:10 +00:00
|
|
|
slog.Error(errorMessage+" for client with id: "+id, err)
|
2024-01-14 17:28:04 +00:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(errorMessage))
|
2024-01-10 14:48:03 +00:00
|
|
|
}
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
// 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))
|
|
|
|
|
2024-01-15 15:51:51 +00:00
|
|
|
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")
|
|
|
|
})
|
|
|
|
|
2024-01-15 18:03:57 +00:00
|
|
|
// 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))
|
|
|
|
|
2024-01-14 17:28:04 +00:00
|
|
|
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(config.AuthServerPort), nil))
|
2024-01-10 14:48:03 +00:00
|
|
|
}
|
2024-01-15 15:51:51 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|