From cb6ca40507df61526b555b204b0581ab6dac16a1 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 7 Apr 2022 17:17:14 +0900 Subject: [PATCH 1/6] fix: login redirect does not work --- internal/app/auth_server.go | 97 +++++++++++++++++++++++-------------- internal/app/config.go | 45 +++++++++++++++++ internal/app/config_test.go | 6 +++ 3 files changed, 111 insertions(+), 37 deletions(-) diff --git a/internal/app/auth_server.go b/internal/app/auth_server.go index 7d80907c..5f96d9a5 100644 --- a/internal/app/auth_server.go +++ b/internal/app/auth_server.go @@ -4,11 +4,11 @@ import ( "context" "crypto/sha256" "encoding/json" + "errors" "net/http" "net/url" "os" "strconv" - "strings" "github.com/caos/oidc/pkg/op" "github.com/golang/gddo/httputil/header" @@ -17,6 +17,7 @@ import ( "github.com/reearth/reearth-backend/internal/usecase/interactor" "github.com/reearth/reearth-backend/internal/usecase/interfaces" "github.com/reearth/reearth-backend/pkg/log" + "github.com/reearth/reearth-backend/pkg/user" ) const ( @@ -29,13 +30,9 @@ const ( func authEndPoints(ctx context.Context, e *echo.Echo, r *echo.Group, cfg *ServerConfig) { userUsecase := interactor.NewUser(cfg.Repos, cfg.Gateways, cfg.Config.SignupSecret, cfg.Config.Host_Web) - d := cfg.Config.AuthSrv.Domain - if d == "" { - d = cfg.Config.Host - } - domain, err := url.Parse(d) - if err != nil { - log.Panicf("auth: not valid auth domain: %s", d) + domain := cfg.Config.AuthServeDomainURL() + if domain == nil || domain.String() == "" { + log.Panicf("auth: not valid auth domain: %s", domain) } domain.Path = "/" @@ -95,7 +92,7 @@ func authEndPoints(ctx context.Context, e *echo.Echo, r *echo.Group, cfg *Server } // Actual login endpoint - r.POST(loginEndpoint, login(ctx, cfg, storage, userUsecase)) + r.POST(loginEndpoint, login(ctx, domain, storage, userUsecase)) r.GET(logoutEndpoint, logout()) @@ -191,44 +188,68 @@ type loginForm struct { AuthRequestID string `json:"id" form:"id"` } -func login(ctx context.Context, cfg *ServerConfig, storage op.Storage, userUsecase interfaces.User) func(ctx echo.Context) error { +func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase interfaces.User) func(ctx echo.Context) error { return func(ec echo.Context) error { request := new(loginForm) err := ec.Bind(request) if err != nil { log.Errorln("auth: filed to parse login request") - return ec.Redirect(http.StatusFound, redirectURL(ec.Request().Referer(), !cfg.Debug, "", "Bad request!")) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/login", "", "Bad request!"), + ) } - authRequest, err := storage.AuthRequestByID(ctx, request.AuthRequestID) - if err != nil { + if _, err := storage.AuthRequestByID(ctx, request.AuthRequestID); err != nil { log.Errorf("auth: filed to parse login request: %s\n", err) - return ec.Redirect(http.StatusFound, redirectURL(ec.Request().Referer(), !cfg.Debug, "", "Bad request!")) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/login", "", "Bad request!"), + ) } if len(request.Email) == 0 || len(request.Password) == 0 { log.Errorln("auth: one of credentials are not provided") - return ec.Redirect(http.StatusFound, redirectURL(authRequest.GetRedirectURI(), !cfg.Debug, request.AuthRequestID, "Bad request!")) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/login", request.AuthRequestID, "Bad request!"), + ) } // check user credentials from db - user, err := userUsecase.GetUserByCredentials(ctx, interfaces.GetUserByCredentials{ + u, err := userUsecase.GetUserByCredentials(ctx, interfaces.GetUserByCredentials{ Email: request.Email, Password: request.Password, }) + var auth *user.Auth + if err == nil { + auth = u.GetAuthByProvider(authProvider) + if auth == nil { + err = errors.New("The account is not signed up with Re:Earth") + } + } if err != nil { log.Errorf("auth: wrong credentials: %s\n", err) - return ec.Redirect(http.StatusFound, redirectURL(authRequest.GetRedirectURI(), !cfg.Debug, request.AuthRequestID, "Login failed; Invalid user ID or password.")) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/login", request.AuthRequestID, "Login failed; Invalid user ID or password."), + ) } // Complete the auth request && set the subject - err = storage.(*interactor.AuthStorage).CompleteAuthRequest(ctx, request.AuthRequestID, user.GetAuthByProvider(authProvider).Sub) + err = storage.(*interactor.AuthStorage).CompleteAuthRequest(ctx, request.AuthRequestID, auth.Sub) if err != nil { log.Errorf("auth: failed to complete the auth request: %s\n", err) - return ec.Redirect(http.StatusFound, redirectURL(authRequest.GetRedirectURI(), !cfg.Debug, request.AuthRequestID, "Bad request!")) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/login", request.AuthRequestID, "Bad request!"), + ) } - return ec.Redirect(http.StatusFound, "/authorize/callback?id="+request.AuthRequestID) + return ec.Redirect( + http.StatusFound, + redirectURL(url, "/authorize/callback", request.AuthRequestID, ""), + ) } } @@ -239,25 +260,27 @@ func logout() func(ec echo.Context) error { } } -func redirectURL(domain string, secure bool, requestID string, error string) string { - domain = strings.TrimPrefix(domain, "http://") - domain = strings.TrimPrefix(domain, "https://") - - schema := "http" - if secure { - schema = "https" +func redirectURL(u *url.URL, p string, requestID, err string) string { + v := cloneURL(u) + if p == "" { + p = "/login" } - - u := url.URL{ - Scheme: schema, - Host: domain, - Path: "login", - } - + v.Path = p queryValues := u.Query() queryValues.Set("id", requestID) - queryValues.Set("error", error) - u.RawQuery = queryValues.Encode() + if err != "" { + queryValues.Set("error", err) + } + v.RawQuery = queryValues.Encode() + return v.String() +} - return u.String() +func cloneURL(u *url.URL) *url.URL { + return &url.URL{ + Scheme: u.Scheme, + Opaque: u.Opaque, + User: u.User, + Host: u.Host, + Path: u.Path, + } } diff --git a/internal/app/config.go b/internal/app/config.go index 354f1cc4..56bb8a8a 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -143,6 +143,17 @@ func ReadConfig(debug bool) (*Config, error) { if debug { c.Dev = true } + c.Host = addHTTPScheme(c.Host) + if c.Host_Web == "" { + c.Host_Web = c.Host + } else { + c.Host_Web = addHTTPScheme(c.Host_Web) + } + if c.AuthSrv.Domain == "" { + c.AuthSrv.Domain = c.Host + } else { + c.AuthSrv.Domain = addHTTPScheme(c.AuthSrv.Domain) + } if c.Host_Web == "" { c.Host_Web = c.Host } @@ -242,3 +253,37 @@ func (ipd *AuthConfigs) Decode(value string) error { *ipd = providers return nil } + +func (c Config) HostURL() *url.URL { + u, err := url.Parse(c.Host) + if err != nil { + u = nil + } + return u +} + +func (c Config) HostWebURL() *url.URL { + u, err := url.Parse(c.Host_Web) + if err != nil { + u = nil + } + return u +} + +func (c Config) AuthServeDomainURL() *url.URL { + u, err := url.Parse(c.AuthSrv.Domain) + if err != nil { + u = nil + } + return u +} + +func addHTTPScheme(host string) string { + if host == "" { + return "" + } + if !strings.HasPrefix(host, "https://") && !strings.HasPrefix(host, "http://") { + host = "http://" + host + } + return host +} diff --git a/internal/app/config_test.go b/internal/app/config_test.go index d2405902..40b5a2fa 100644 --- a/internal/app/config_test.go +++ b/internal/app/config_test.go @@ -32,3 +32,9 @@ func TestReadConfig(t *testing.T) { assert.Equal(t, "hoge", cfg.Auth_ISS) assert.Equal(t, "foo", cfg.Auth_AUD) } + +func Test_AddHTTPScheme(t *testing.T) { + assert.Equal(t, "http://a", addHTTPScheme("a")) + assert.Equal(t, "http://a", addHTTPScheme("http://a")) + assert.Equal(t, "https://a", addHTTPScheme("https://a")) +} From adeda41232d8aab7bc16b70d28597786db6e9789 Mon Sep 17 00:00:00 2001 From: yk-eukarya <81808708+yk-eukarya@users.noreply.github.com> Date: Thu, 7 Apr 2022 11:49:13 +0300 Subject: [PATCH 2/6] chore: add log for GraphQL Playground endpoint (#133) * add log * change log message Co-authored-by: rot1024 --- internal/app/app.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/app.go b/internal/app/app.go index ccd40e16..8b007d4f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -67,6 +67,7 @@ func initEcho(ctx context.Context, cfg *ServerConfig) *echo.Echo { e.GET("/graphql", echo.WrapHandler( playground.Handler("reearth-backend", "/api/graphql"), )) + log.Infof("gql: GraphQL Playground is available") } // init usecases From 0c0e28c83eafddaea601a6d1ecd1eb60fa27f05a Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 7 Apr 2022 17:52:10 +0900 Subject: [PATCH 3/6] fix: enable auth srv dev mode when no domain is specified --- .env.example | 6 ++++-- internal/app/config.go | 14 +++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 98c11c80..7e4aba5a 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,8 @@ # General PORT=8080 REEARTH_DB=mongodb://localhost +REEARTH_HOST=https://localhost:8080 +REEARTH_HOST_WEB=https://localhost:3000 REEARTH_DEV=false # GCP @@ -35,13 +37,13 @@ REEARTH_AUTHSRV_KEY=abcdefghijklmnopqrstuvwxyz # Available mailers: [log, smtp, sendgrid] REEARTH_MAILER=log -#SendGrid config +# SendGrid config #REEARTH_MAILER=sendgrid #REEARTH_SENDGRID_EMAIL=noreplay@test.com #REEARTH_SENDGRID_NAME= #REEARTH_SENDGRID_API= -#SMTP config +# SMTP config #REEARTH_MAILER=smtp #REEARTH_SMTP_EMAIL=noreplay@test.com #REEARTH_SMTP_HOST=smtp.sendgrid.net diff --git a/internal/app/config.go b/internal/app/config.go index 56bb8a8a..3b2061cc 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -139,6 +139,13 @@ func ReadConfig(debug bool) (*Config, error) { var c Config err := envconfig.Process(configPrefix, &c) + // overwrite env vars + if !c.AuthSrv.Disabled && (c.Dev || c.AuthSrv.Dev || c.AuthSrv.Domain == "") { + if _, ok := os.LookupEnv(op.OidcDevMode); !ok { + _ = os.Setenv(op.OidcDevMode, "1") + } + } + // defailt values if debug { c.Dev = true @@ -158,13 +165,6 @@ func ReadConfig(debug bool) (*Config, error) { c.Host_Web = c.Host } - // overwrite env vars - if !c.AuthSrv.Disabled && (c.Dev || c.AuthSrv.Dev || c.AuthSrv.Domain == "") { - if _, ok := os.LookupEnv(op.OidcDevMode); !ok { - _ = os.Setenv(op.OidcDevMode, "1") - } - } - return &c, err } From e96f78ae3a33efdb36b067812c9d97fc7a67b8b8 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 7 Apr 2022 19:23:05 +0900 Subject: [PATCH 4/6] fix: add a trailing slash to jwt audiences --- internal/app/jwt.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/app/jwt.go b/internal/app/jwt.go index 94476673..84b72c73 100644 --- a/internal/app/jwt.go +++ b/internal/app/jwt.go @@ -59,11 +59,20 @@ func NewMultiValidator(providers []AuthConfig) (MultiValidator, error) { } algorithm := validator.SignatureAlgorithm(alg) + // add a trailing slash (auth0-spa-js adds a trailing slash to audiences) + aud := append([]string{}, p.AUD...) + for i, a := range aud { + if !strings.HasSuffix(a, "/") { + a += "/" + } + aud[i] = a + } + v, err := validator.New( provider.KeyFunc, algorithm, issuerURL.String(), - p.AUD, + aud, validator.WithCustomClaims(func() validator.CustomClaims { return &customClaims{} }), From 0ce79ff58c3a0a52ad7637437f24d5877d4dd0dc Mon Sep 17 00:00:00 2001 From: rot1024 Date: Fri, 8 Apr 2022 15:25:54 +0900 Subject: [PATCH 5/6] fix: allow separate auth server ui domain --- internal/app/auth_server.go | 21 +++++++++++---------- internal/app/config.go | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/internal/app/auth_server.go b/internal/app/auth_server.go index 5f96d9a5..9430fa3c 100644 --- a/internal/app/auth_server.go +++ b/internal/app/auth_server.go @@ -36,6 +36,8 @@ func authEndPoints(ctx context.Context, e *echo.Echo, r *echo.Group, cfg *Server } domain.Path = "/" + uidomain := cfg.Config.AuthServeUIDomainURL() + config := &op.Config{ Issuer: domain.String(), CryptoKey: sha256.Sum256([]byte(cfg.Config.AuthSrv.Key)), @@ -92,7 +94,7 @@ func authEndPoints(ctx context.Context, e *echo.Echo, r *echo.Group, cfg *Server } // Actual login endpoint - r.POST(loginEndpoint, login(ctx, domain, storage, userUsecase)) + r.POST(loginEndpoint, login(ctx, domain, uidomain, storage, userUsecase)) r.GET(logoutEndpoint, logout()) @@ -188,7 +190,7 @@ type loginForm struct { AuthRequestID string `json:"id" form:"id"` } -func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase interfaces.User) func(ctx echo.Context) error { +func login(ctx context.Context, url, uiurl *url.URL, storage op.Storage, userUsecase interfaces.User) func(ctx echo.Context) error { return func(ec echo.Context) error { request := new(loginForm) err := ec.Bind(request) @@ -196,7 +198,7 @@ func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase in log.Errorln("auth: filed to parse login request") return ec.Redirect( http.StatusFound, - redirectURL(url, "/login", "", "Bad request!"), + redirectURL(uiurl, "/login", "", "Bad request!"), ) } @@ -204,7 +206,7 @@ func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase in log.Errorf("auth: filed to parse login request: %s\n", err) return ec.Redirect( http.StatusFound, - redirectURL(url, "/login", "", "Bad request!"), + redirectURL(uiurl, "/login", "", "Bad request!"), ) } @@ -212,7 +214,7 @@ func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase in log.Errorln("auth: one of credentials are not provided") return ec.Redirect( http.StatusFound, - redirectURL(url, "/login", request.AuthRequestID, "Bad request!"), + redirectURL(uiurl, "/login", request.AuthRequestID, "Bad request!"), ) } @@ -232,7 +234,7 @@ func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase in log.Errorf("auth: wrong credentials: %s\n", err) return ec.Redirect( http.StatusFound, - redirectURL(url, "/login", request.AuthRequestID, "Login failed; Invalid user ID or password."), + redirectURL(uiurl, "/login", request.AuthRequestID, "Login failed; Invalid user ID or password."), ) } @@ -242,7 +244,7 @@ func login(ctx context.Context, url *url.URL, storage op.Storage, userUsecase in log.Errorf("auth: failed to complete the auth request: %s\n", err) return ec.Redirect( http.StatusFound, - redirectURL(url, "/login", request.AuthRequestID, "Bad request!"), + redirectURL(uiurl, "/login", request.AuthRequestID, "Bad request!"), ) } @@ -262,10 +264,9 @@ func logout() func(ec echo.Context) error { func redirectURL(u *url.URL, p string, requestID, err string) string { v := cloneURL(u) - if p == "" { - p = "/login" + if p != "" { + v.Path = p } - v.Path = p queryValues := u.Query() queryValues.Set("id", requestID) if err != "" { diff --git a/internal/app/config.go b/internal/app/config.go index 3b2061cc..62d6daac 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -60,6 +60,7 @@ type AuthSrvConfig struct { Dev bool Disabled bool Domain string + UIDomain string Key string DN *AuthSrvDNConfig } @@ -75,7 +76,7 @@ func (c AuthSrvConfig) AuthConfig(debug bool, host string) *AuthConfig { } var aud []string - if debug && host != "" && c.Domain != "" { + if debug && host != "" && c.Domain != "" && c.Domain != host { aud = []string{host, c.Domain} } else { aud = []string{domain} @@ -146,7 +147,7 @@ func ReadConfig(debug bool) (*Config, error) { } } - // defailt values + // default values if debug { c.Dev = true } @@ -164,6 +165,11 @@ func ReadConfig(debug bool) (*Config, error) { if c.Host_Web == "" { c.Host_Web = c.Host } + if c.AuthSrv.UIDomain == "" { + c.AuthSrv.UIDomain = c.Host_Web + } else { + c.AuthSrv.UIDomain = addHTTPScheme(c.AuthSrv.UIDomain) + } return &c, err } @@ -278,6 +284,14 @@ func (c Config) AuthServeDomainURL() *url.URL { return u } +func (c Config) AuthServeUIDomainURL() *url.URL { + u, err := url.Parse(c.AuthSrv.UIDomain) + if err != nil { + u = nil + } + return u +} + func addHTTPScheme(host string) string { if host == "" { return "" From db6ff776aba4219ff560239a59b3d53129961fde Mon Sep 17 00:00:00 2001 From: rot1024 Date: Fri, 8 Apr 2022 08:46:24 +0000 Subject: [PATCH 6/6] v0.6.0 --- CHANGELOG.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2367b725..c22e0f84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,56 @@ # Changelog All notable changes to this project will be documented in this file. -## 0.5.0 - 2022-02-22 +## 0.6.0 - 2022-04-08 + +### 🚀 Features + +- Authentication system ([#108](https://github.com/reearth/reearth-backend/pull/108)) [`b89c32`](https://github.com/reearth/reearth-backend/commit/b89c32) +- Default mailer that outputs mails into stdout [`aab26c`](https://github.com/reearth/reearth-backend/commit/aab26c) +- Assets filtering & pagination ([#81](https://github.com/reearth/reearth-backend/pull/81)) [`739943`](https://github.com/reearth/reearth-backend/commit/739943) +- Support sign up with information provided by OIDC providers ([#130](https://github.com/reearth/reearth-backend/pull/130)) [`fef60e`](https://github.com/reearth/reearth-backend/commit/fef60e) + +### 🔧 Bug Fixes + +- Load auth client domain from config ([#124](https://github.com/reearth/reearth-backend/pull/124)) [`9bde8a`](https://github.com/reearth/reearth-backend/commit/9bde8a) +- Signup fails when password is not set [`27c2f0`](https://github.com/reearth/reearth-backend/commit/27c2f0) +- Logger panics [`d1e3a8`](https://github.com/reearth/reearth-backend/commit/d1e3a8) +- Set auth server dev mode automatically [`83a66a`](https://github.com/reearth/reearth-backend/commit/83a66a) +- Auth server bugs and auth client bugs ([#125](https://github.com/reearth/reearth-backend/pull/125)) [`ce2309`](https://github.com/reearth/reearth-backend/commit/ce2309) +- Auth0 setting is not used by JWT verification middleware [`232e75`](https://github.com/reearth/reearth-backend/commit/232e75) +- Invalid mongo queries of pagination [`7caf68`](https://github.com/reearth/reearth-backend/commit/7caf68) +- Auth config not loaded expectedly [`570fe7`](https://github.com/reearth/reearth-backend/commit/570fe7) +- Users cannot creates a new team and scene [`5df25f`](https://github.com/reearth/reearth-backend/commit/5df25f) +- Auth server certificate is not saved as pem format [`982a71`](https://github.com/reearth/reearth-backend/commit/982a71) +- Repo filters are not merged expectedly [`f4cc3f`](https://github.com/reearth/reearth-backend/commit/f4cc3f) +- Auth is no longer required for GraphQL endpoint [`58a6d1`](https://github.com/reearth/reearth-backend/commit/58a6d1) +- Rename auth srv default client ID ([#128](https://github.com/reearth/reearth-backend/pull/128)) [`89adc3`](https://github.com/reearth/reearth-backend/commit/89adc3) +- Signup API is disabled when auth server is disabled, users and auth requests in mongo cannot be deleted ([#132](https://github.com/reearth/reearth-backend/pull/132)) [`47be6a`](https://github.com/reearth/reearth-backend/commit/47be6a) +- Auth to work with zero config ([#131](https://github.com/reearth/reearth-backend/pull/131)) [`3cbb45`](https://github.com/reearth/reearth-backend/commit/3cbb45) +- Property.SchemaListMap.List test fails [`3e6dff`](https://github.com/reearth/reearth-backend/commit/3e6dff) +- Errors when auth srv domain is not specified [`10691a`](https://github.com/reearth/reearth-backend/commit/10691a) +- Errors when auth srv domain is not specified [`648073`](https://github.com/reearth/reearth-backend/commit/648073) +- Login redirect does not work [`cb6ca4`](https://github.com/reearth/reearth-backend/commit/cb6ca4) +- Enable auth srv dev mode when no domain is specified [`0c0e28`](https://github.com/reearth/reearth-backend/commit/0c0e28) +- Add a trailing slash to jwt audiences [`e96f78`](https://github.com/reearth/reearth-backend/commit/e96f78) +- Allow separate auth server ui domain [`0ce79f`](https://github.com/reearth/reearth-backend/commit/0ce79f) + +### ⚡️ Performance + +- Reduce database queries to obtain scene IDs ([#119](https://github.com/reearth/reearth-backend/pull/119)) [`784332`](https://github.com/reearth/reearth-backend/commit/784332) + +### ✨ Refactor + +- Remove filter args from repos to prevent implementation errors in the use case layer ([#122](https://github.com/reearth/reearth-backend/pull/122)) [`82cf28`](https://github.com/reearth/reearth-backend/commit/82cf28) +- Http api to export layers [`3f2582`](https://github.com/reearth/reearth-backend/commit/3f2582) + +### Miscellaneous Tasks + +- Update dependencies ([#117](https://github.com/reearth/reearth-backend/pull/117)) [`d1a38e`](https://github.com/reearth/reearth-backend/commit/d1a38e) +- Update docker-compose config [`83f9b1`](https://github.com/reearth/reearth-backend/commit/83f9b1) +- Add log for GraphQL Playground endpoint ([#133](https://github.com/reearth/reearth-backend/pull/133)) [`adeda4`](https://github.com/reearth/reearth-backend/commit/adeda4) + +## 0.5.0 - 2022-02-24 ### 🚀 Features