Skip to content
This repository has been archived by the owner on Feb 1, 2024. It is now read-only.

Commit

Permalink
Auth0 integration_V2 (#703) (closes #706)
Browse files Browse the repository at this point in the history
* add separate auth0 file for beckend

* revert commit

* configured auth0 on GUI

* added interceptor

* added support for accepting GUI config file through CLI on server command

* fixed not rendering component on async token call

* finalize Auth0 Implementation

* removed unused code

* fixing xdg-open not found bug on some linux-machine

* trying to fix continuous reloading issue when deploying on k8s

* auth0-implemention

* auth0-implemention

* updated readme.md

* auth0-integration

* Delete auth0-config.json

* Auth0 Implementation

* Auth0 integration V2

* Updated interceptor.js

updated interceptor to create a new header for a request which doesn't have one.

* Update serverMetadata.js

reverted back the changes to this file.

* Update version.js

reverted this file back to its original state

Co-authored-by: sanj singh <[email protected]>
Co-authored-by: Sanj <[email protected]>
Co-authored-by: OrunPay B.V <[email protected]>
  • Loading branch information
4 people authored Jun 30, 2021
1 parent 4db12f8 commit 6b30fa2
Show file tree
Hide file tree
Showing 20 changed files with 386 additions and 21 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Your use of Kelp is governed by the Apache 2.0 open-source license. Please note
* [Running Kelp](#running-kelp)
* [Using CCXT](#using-ccxt)
* [Using Postgres](#using-postgres)
* [Using Auth0](#using-auth0)
* [Examples](#examples)
* [Walkthrough Guides](#walkthrough-guides)
* [Configuration Files](#configuration-files)
Expand Down Expand Up @@ -184,6 +185,11 @@ You can find more details on the [CCXT_REST github page][ccxt-rest].

[Postgres][postgres] v12.1 or later must be installed for Kelp to automatically write trades to a sql database along with updating the trader config file.

### Using Auth0

A [auth0](https://auth0.com/) account is required. To use it, uncomment \[AUTH0] section in [Sample GUI config file](examples/configs/trader/sample_GUI_config.cfg) and enter your auth0 crendentials in required fields.
Note: AUTH0 is only applicable for Kelp GUI or Kaas Mode. Intructions of how to configure your auth0 account can be found [here](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0)

## Examples

It's easier to learn with examples! Take a look at the walkthrough guides and sample configuration files below.
Expand All @@ -208,6 +214,7 @@ The following reference config files are in the [examples folder](examples/confi
- [Sample Balanced strategy config file](examples/configs/trader/sample_balanced.cfg)
- [Sample Pendulum strategy config file](examples/configs/trader/sample_pendulum.cfg)
- [Sample Mirror strategy config file](examples/configs/trader/sample_mirror.cfg)
- [Sample GUI(auth0 and other stuff) config file](examples/configs/trader/sample_GUI_config.cfg)

### Winning Educational Content from StellarBattle

Expand Down Expand Up @@ -403,6 +410,7 @@ See the [Code of Conduct](CODE_OF_CONDUCT.md).
[ccxt-rest]: https://github.com/franz-see/ccxt-rest
[docker]: https://www.docker.com/
[postgres]: https://www.postgresql.org/
[auth0]: https://auth0.com/
[kelp-battle-1]: https://stellarbattle.com/kelp-overview-battle/
[kelp-battle-1-winners]: https://medium.com/stellar-community/announcing-the-winners-of-the-first-kelpbot-stellarbattle-a6f28fef7776
[kraken]: https://www.kraken.com/
Expand Down
32 changes: 32 additions & 0 deletions cmd/server_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import (
"github.com/stellar/kelp/support/prefs"
"github.com/stellar/kelp/support/sdk"
"github.com/stellar/kelp/support/utils"
"github.com/stellar/kelp/support/guiconfig"
"github.com/stellar/go/support/config"
)

const kelpAssetsPath = "/assets"
Expand Down Expand Up @@ -72,6 +74,15 @@ type serverInputOptions struct {
enableKaas *bool
tlsCertFile *string
tlsKeyFile *string
guiConfigPath *string
}

// checks for required flag on CLI
func requiredFlags(flag string) {
e := serverCmd.MarkFlagRequired(flag)
if e != nil {
panic(e)
}
}

// String is the stringer method impl.
Expand All @@ -80,6 +91,19 @@ func (o serverInputOptions) String() string {
*o.port, *o.dev, *o.devAPIPort, *o.horizonTestnetURI, *o.horizonPubnetURI, *o.noHeaders, *o.verbose, *o.noElectron, *o.disablePubnet, *o.enableKaas)
}

// function for reading custom config file and returning config struct with intilized value
func readGUIConfig(options serverInputOptions) guiconfig.GUIConfig {
var guiConfigInFunc guiconfig.GUIConfig
e := config.Read(*options.guiConfigPath, &guiConfigInFunc)
utils.CheckConfigError(guiConfigInFunc, e, *options.guiConfigPath)
if e != nil {
panic(fmt.Errorf("could not read GUI config file '%s': %s", *options.guiConfigPath, e))
}
return guiConfigInFunc
}
// customConfigVar Variable with its equivalent struct #used to inject config values to jwt config var and to configure route
var auth0ConfigVar guiconfig.GUIConfig

func init() {
options := serverInputOptions{}
options.port = serverCmd.Flags().Uint16P("port", "p", 8000, "port on which to serve HTTP")
Expand All @@ -95,6 +119,9 @@ func init() {
options.enableKaas = serverCmd.Flags().Bool("enable-kaas", false, "enable kelp-as-a-service (KaaS) mode, which does not bring up browser or electron")
options.tlsCertFile = serverCmd.Flags().String("tls-cert-file", "", "path to TLS certificate file")
options.tlsKeyFile = serverCmd.Flags().String("tls-key-file", "", "path to TLS key file")
options.guiConfigPath = serverCmd.Flags().StringP("guiconfig", "c", "", "(required) gui-config for auth0 and other basic config file path")

requiredFlags("guiconfig")

serverCmd.Run = func(ccmd *cobra.Command, args []string) {
isLocalMode := env == envDev
Expand Down Expand Up @@ -143,6 +170,10 @@ func init() {

log.Printf("initialized server with cli flag inputs: %s", options)

//calliing readGUIConfig func and then inject values into JWT_middleware customconfigvar
auth0ConfigVar = readGUIConfig(options)
backend.Auth0ConfigVarJWT = auth0ConfigVar

if runtime.GOOS == "windows" {
if !*options.noElectron {
log.Printf("input options had specified noElectron=false for windows, but electron is not supported on windows yet. force setting noElectron=true for windows.\n")
Expand Down Expand Up @@ -382,6 +413,7 @@ func init() {
*options.noHeaders,
quit,
metricsTracker,
auth0ConfigVar,
)
if e != nil {
panic(e)
Expand Down
11 changes: 11 additions & 0 deletions examples/configs/trader/sample_GUI_config.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Sample UI config file for the kelp bot

# uncomment the AUTH0 section below to enable
# [AUTH0]
# AUTH0_ENABLED=false
# #auth0 domain
# DOMAIN= #"domain_goes_here" #example "dev-*******.eu.auth0.com"
# #auth0 clientID
# CLIENT_ID= #"Client_id_goes_here" #examples "7I47ob2************XKF29hY5"
# #auth0 audience
# AUDIENCE= #"Audience/Identifier goes_here"
4 changes: 4 additions & 0 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import:
- clients/horizonclient
- support/config
- support/errors
- package: github.com/auth0/go-jwt-middleware
version: v1.0.0
- package: github.com/form3tech-oss/jwt-go
version: 5b2d2b5f6c34ccb3b6b65f77f4706558067690ef
##############################################################
# manually added dependencies to fix glide import resolution
##############################################################
Expand Down
4 changes: 4 additions & 0 deletions gui/backend/api_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/stellar/go/clients/horizonclient"
"github.com/stellar/kelp/plugins"
"github.com/stellar/kelp/support/kelpos"
"github.com/stellar/kelp/support/guiconfig"
)

// UserData is the json data passed in to represent a user
Expand Down Expand Up @@ -59,6 +60,7 @@ type APIServer struct {
kelpErrorsByUserLock *sync.Mutex

cachedOptionsMetadata metadata
guiConfig guiconfig.GUIConfig
}

// MakeAPIServer is a factory method
Expand All @@ -76,6 +78,7 @@ func MakeAPIServer(
noHeaders bool,
quitFn func(),
metricsTracker *plugins.MetricsTracker,
guiConfig guiconfig.GUIConfig,
) (*APIServer, error) {
kelpBinPath := kos.GetBinDir().Join(filepath.Base(os.Args[0]))

Expand All @@ -102,6 +105,7 @@ func MakeAPIServer(
metricsTracker: metricsTracker,
kelpErrorsByUser: map[string]kelpErrorDataForUser{},
kelpErrorsByUserLock: &sync.Mutex{},
guiConfig: guiConfig,
}, nil
}

Expand Down
88 changes: 88 additions & 0 deletions gui/backend/jwt_middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package backend

import (
"encoding/json"
"errors"
"net/http"
"fmt"

jwtmiddleware "github.com/auth0/go-jwt-middleware"
"github.com/form3tech-oss/jwt-go"
"github.com/stellar/kelp/support/guiconfig"
)

type Response struct {
Message string `json:"message"`
}

type Jwks struct {
Keys []JSONWebKeys `json:"keys"`
}

type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}

var Auth0ConfigVarJWT guiconfig.GUIConfig

var JWTMiddlewareVar = jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
// Verify 'iss' claim
iss := "https://" + Auth0ConfigVarJWT.Auth0Config.Domain + "/"
checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
if !checkIss {
return token, errors.New("Invalid issuer.")
}

// Verify 'aud' claim
aud := Auth0ConfigVarJWT.Auth0Config.Audience
checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
if !checkAud {
return token, errors.New("Invalid audience.")
}

cert, err := getPemCert(token)
if err != nil {
return nil, fmt.Errorf("error when getting PEM certificate: %s", err)
}

result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
return result, nil
},
SigningMethod: jwt.SigningMethodRS256,
})

func getPemCert(token *jwt.Token) (string, error) {
cert := ""
resp, err := http.Get("https://" + Auth0ConfigVarJWT.Auth0Config.Domain + "/.well-known/jwks.json")

if err != nil {
return cert, err
}
defer resp.Body.Close()

var jwks = Jwks{}
err = json.NewDecoder(resp.Body).Decode(&jwks)

if err != nil {
return cert, err
}

for k, _ := range jwks.Keys {
if token.Header["kid"] == jwks.Keys[k].Kid {
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
}
}

if cert == "" {
err := errors.New("Unable to find appropriate key.")
return cert, err
}

return cert, nil
}
37 changes: 21 additions & 16 deletions gui/backend/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,27 @@ func SetRoutes(r *chi.Mux, s *APIServer) {
r.Get("/serverMetadata", http.HandlerFunc(s.serverMetadata))
r.Get("/newSecretKey", http.HandlerFunc(s.newSecretKey))
r.Get("/optionsMetadata", http.HandlerFunc(s.optionsMetadata))
var router chi.Router = r
if s.guiConfig.Auth0Config != nil && s.guiConfig.Auth0Config.Auth0Enabled {
// setting the router to use the JWT middleware to handle auth0 style JWT tokens
router = r.With(JWTMiddlewareVar.Handler)
}

r.Post("/listBots", http.HandlerFunc(s.listBots))
r.Post("/genBotName", http.HandlerFunc(s.generateBotName))
r.Post("/getNewBotConfig", http.HandlerFunc(s.getNewBotConfig))
r.Post("/autogenerate", http.HandlerFunc(s.autogenerateBot))
r.Post("/fetchKelpErrors", http.HandlerFunc(s.fetchKelpErrors))
r.Post("/removeKelpErrors", http.HandlerFunc(s.removeKelpErrors))
r.Post("/start", http.HandlerFunc(s.startBot))
r.Post("/stop", http.HandlerFunc(s.stopBot))
r.Post("/deleteBot", http.HandlerFunc(s.deleteBot))
r.Post("/getState", http.HandlerFunc(s.getBotState))
r.Post("/getBotInfo", http.HandlerFunc(s.getBotInfo))
r.Post("/getBotConfig", http.HandlerFunc(s.getBotConfig))
r.Post("/fetchPrice", http.HandlerFunc(s.fetchPrice))
r.Post("/upsertBotConfig", http.HandlerFunc(s.upsertBotConfig))
r.Post("/sendMetricEvent", http.HandlerFunc(s.sendMetricEvent))
router.Post("/listBots", http.HandlerFunc(s.listBots))
router.Post("/genBotName", http.HandlerFunc(s.generateBotName))
router.Post("/getNewBotConfig", http.HandlerFunc(s.getNewBotConfig))
router.Post("/autogenerate", http.HandlerFunc(s.autogenerateBot))
router.Post("/fetchKelpErrors", http.HandlerFunc(s.fetchKelpErrors))
router.Post("/removeKelpErrors", http.HandlerFunc(s.removeKelpErrors))
router.Post("/start", http.HandlerFunc(s.startBot))
router.Post("/stop", http.HandlerFunc(s.stopBot))
router.Post("/deleteBot", http.HandlerFunc(s.deleteBot))
router.Post("/getState", http.HandlerFunc(s.getBotState))
router.Post("/getBotInfo", http.HandlerFunc(s.getBotInfo))
router.Post("/getBotConfig", http.HandlerFunc(s.getBotConfig))
router.Post("/fetchPrice", http.HandlerFunc(s.fetchPrice))
router.Post("/upsertBotConfig", http.HandlerFunc(s.upsertBotConfig))
router.Post("/sendMetricEvent", http.HandlerFunc(s.sendMetricEvent))
})
r.Get("/ping", http.HandlerFunc(s.ping))
}
}
3 changes: 3 additions & 0 deletions gui/backend/serverMetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import (
"encoding/json"
"fmt"
"net/http"
"github.com/stellar/kelp/support/guiconfig"
)

// ServerMetadataResponse is the response from the /serverMetadata endpoint
type ServerMetadataResponse struct {
DisablePubnet bool `json:"disable_pubnet"`
EnableKaas bool `json:"enable_kaas"`
GuiConfig guiconfig.GUIConfig `json:"guiconfig"`
}

func (s *APIServer) serverMetadata(w http.ResponseWriter, r *http.Request) {
metadata := ServerMetadataResponse{
DisablePubnet: s.disablePubnet,
EnableKaas: s.enableKaas,
GuiConfig: s.guiConfig,
}

b, e := json.Marshal(metadata)
Expand Down
2 changes: 2 additions & 0 deletions gui/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@auth0/auth0-react": "^1.5.0",
"classnames": "^2.2.6",
"fetch-intercept": "^2.4.0",
"node-sass": "^4.11.0",
"react": "^16.8.4",
"react-dom": "^16.8.4",
Expand Down
Loading

0 comments on commit 6b30fa2

Please sign in to comment.