From f5f215df8ac156fc9cc53ea4e46e18b167c4f846 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 8 Aug 2023 22:42:44 +0200 Subject: [PATCH 01/16] feat: ory auth via OAuth2 --- cmd/cloudx/client/handler.go | 341 ++++++++++++++++------------------- go.mod | 6 +- 2 files changed, 161 insertions(+), 186 deletions(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index fcd43d9a..adeebb30 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -7,16 +7,22 @@ import ( "bufio" "bytes" "context" + "crypto/sha256" + "encoding/base64" "encoding/json" stderrs "errors" "fmt" + "html/template" "io" "io/fs" + "net" "net/http" "net/url" "os" + "os/exec" "path/filepath" "strings" + "time" "github.com/gofrs/uuid/v3" "github.com/imdario/mergo" @@ -24,12 +30,15 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/tidwall/gjson" + "golang.org/x/exp/slices" + "golang.org/x/oauth2" "golang.org/x/term" cloud "github.com/ory/client-go" "github.com/ory/x/cmdx" "github.com/ory/x/flagx" "github.com/ory/x/jsonx" + "github.com/ory/x/randx" "github.com/ory/x/stringsx" ) @@ -50,10 +59,11 @@ func RegisterYesFlag(f *pflag.FlagSet) { } type AuthContext struct { - Version string `json:"version"` - SessionToken string `json:"session_token"` - SelectedProject uuid.UUID `json:"selected_project"` - IdentityTraits AuthIdentity `json:"session_identity_traits"` + Version string `json:"version"` + SessionToken string `json:"session_token"` + SelectedProject uuid.UUID `json:"selected_project"` + IdentityTraits AuthIdentity `json:"session_identity_traits"` + AccessToken *oauth2.Token `json:"access_token"` } func (i *AuthContext) ID() string { @@ -109,7 +119,6 @@ type CommandHelper struct { ConfigLocation string NoConfirm bool IsQuiet bool - APIDomain *url.URL Stdin *bufio.Reader PwReader passwordReader } @@ -272,230 +281,196 @@ func (h *CommandHelper) EnsureContext() (*AuthContext, error) { return c, nil } -func (h *CommandHelper) getField(i interface{}, path string) (*gjson.Result, error) { - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(i); err != nil { - return nil, err +func (h *CommandHelper) Authenticate() (*AuthContext, error) { + if h.IsQuiet { + return nil, errors.New("can not sign in or sign up when flag --quiet is set") } - result := gjson.GetBytes(b.Bytes(), path) - return &result, nil -} -func (h *CommandHelper) signup(c *cloud.APIClient) (*AuthContext, error) { - flow, _, err := c.FrontendApi.CreateNativeRegistrationFlow(h.Ctx).Execute() + ac, err := h.readConfig() if err != nil { - return nil, err - } - - var isRetry bool -retryRegistration: - if isRetry { - _, _ = fmt.Fprintf(h.VerboseErrWriter, "\nYour account creation attempt failed. Please try again!\n\n") - } - isRetry = true - - var form cloud.UpdateRegistrationFlowWithPasswordMethod - if err := renderForm(h.Stdin, h.PwReader, h.VerboseErrWriter, flow.Ui, "password", &form); err != nil { - return nil, err + if !errors.Is(err, ErrNoConfig) { + return nil, err + } } - signup, _, err := c.FrontendApi.UpdateRegistrationFlow(h.Ctx). - Flow(flow.Id).UpdateRegistrationFlowBody(cloud.UpdateRegistrationFlowBody{ - UpdateRegistrationFlowWithPasswordMethod: &form, - }).Execute() - if err != nil { - if e, ok := err.(*cloud.GenericOpenAPIError); ok { - switch m := e.Model().(type) { - case *cloud.RegistrationFlow: - flow = m - goto retryRegistration - case cloud.RegistrationFlow: - flow = &m - goto retryRegistration + if ac.AccessToken != nil { + // check existing token + t, err := oac.TokenSource(h.Ctx, ac.AccessToken).Token() + if err == nil { + // we're good + ac.AccessToken = t + if err := h.WriteConfig(ac); err != nil { + return nil, err } + fmt.Fprintf(h.VerboseWriter, "You are already logged in.\n") + return ac, nil } - - return nil, errors.WithStack(err) + fmt.Fprintf(h.VerboseErrWriter, "must get new access token: %v\n", err) } - sessionToken := *signup.SessionToken - sess, _, err := c.FrontendApi.ToSession(h.Ctx).XSessionToken(sessionToken).Execute() + ac, err = h.loginOAuth2() if err != nil { return nil, err } - return h.sessionToContext(sess, sessionToken) + if err := h.WriteConfig(ac); err != nil { + return nil, err + } + + return ac, nil } -func (h *CommandHelper) signin(c *cloud.APIClient, sessionToken string) (*AuthContext, error) { - req := c.FrontendApi.CreateNativeLoginFlow(h.Ctx) - if len(sessionToken) > 0 { - req = req.XSessionToken(sessionToken).Aal("aal2") +var ( + oac = oauth2.Config{ + // ClientID: "a3d01c0c-bf2a-44aa-a6ff-0e27ed79c399", + ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", + Endpoint: oauth2.Endpoint{ + // AuthURL: "https://project.console.ory:8080/oauth2/auth", + // TokenURL: "https://project.console.ory:8080/oauth2/token", + AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", + TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", + AuthStyle: oauth2.AuthStyleInParams, + }, + RedirectURL: "http://localhost:12345/callback", } - flow, _, err := req.Execute() - if err != nil { - return nil, err - } + tmpl = template.Must(template.New("").Parse(` + + Ory Network CLI login + +

{{ .Header }}

+

{{ .Message }}

+ +`)) +) - var isRetry bool -retryLogin: - if isRetry { - _, _ = fmt.Fprintf(h.VerboseErrWriter, "\nYour sign in attempt failed. Please try again!\n\n") - } - isRetry = true +type data struct { + Header, Message string +} - var form interface{} = &cloud.UpdateLoginFlowWithPasswordMethod{} - method := "password" - if len(sessionToken) > 0 { - var foundTOTP bool - var foundLookup bool - for _, n := range flow.Ui.Nodes { - if n.Group == "totp" { - foundTOTP = true - } else if n.Group == "lookup_secret" { - foundLookup = true - } - } - if !foundLookup && !foundTOTP { - return nil, errors.New("only TOTP and lookup secrets are supported for two-step verification in the CLI") - } +func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { + code, errs, outcome, stop := h.runOAuth2CallbackServer() + defer stop() - method = "lookup_secret" - if foundTOTP { - form = &cloud.UpdateLoginFlowWithTotpMethod{} - method = "totp" - } - } + codeVerifier := randx.MustString(64, randx.AlphaNum) + sha := sha256.Sum256([]byte(codeVerifier)) + codeChallege := base64.RawURLEncoding.EncodeToString(sha[:]) + url := oac.AuthCodeURL(randx.MustString(32, randx.AlphaNum), + oauth2.SetAuthURLParam("scope", "offline_access"), + oauth2.SetAuthURLParam("code_challenge_method", "S256"), + oauth2.SetAuthURLParam("code_challenge", codeChallege), + oauth2.SetAuthURLParam("response_type", "code"), + oauth2.SetAuthURLParam("prompt", "consent"), + ) - if err := renderForm(h.Stdin, h.PwReader, h.VerboseErrWriter, flow.Ui, method, form); err != nil { - return nil, err + err := exec.Command("open", url).Run() + if err != nil { + return nil, fmt.Errorf("failed to open browser: %w", err) } - var body cloud.UpdateLoginFlowBody - switch e := form.(type) { - case *cloud.UpdateLoginFlowWithTotpMethod: - body.UpdateLoginFlowWithTotpMethod = e - case *cloud.UpdateLoginFlowWithPasswordMethod: - body.UpdateLoginFlowWithPasswordMethod = e - default: - panic("unexpected type") - } + fmt.Fprintf(h.VerboseErrWriter, + `A browser should have opened for you to complete your login to Ory Network. +If no browser opened, visit the below page to continue: - login, _, err := c.FrontendApi.UpdateLoginFlow(h.Ctx).XSessionToken(sessionToken). - Flow(flow.Id).UpdateLoginFlowBody(body).Execute() - if err != nil { - if e, ok := err.(*cloud.GenericOpenAPIError); ok { - switch m := e.Model().(type) { - case *cloud.LoginFlow: - flow = m - goto retryLogin - case cloud.LoginFlow: - flow = &m - goto retryLogin - } - } + %s - return nil, errors.WithStack(err) - } - sessionToken = stringsx.Coalesce(*login.SessionToken, sessionToken) - sess, _, err := c.FrontendApi.ToSession(h.Ctx).XSessionToken(sessionToken).Execute() - if err == nil { - return h.sessionToContext(sess, sessionToken) - } +`, url) - if e, ok := err.(*cloud.GenericOpenAPIError); ok { - switch gjson.GetBytes(e.Body(), "error.id").String() { - case "session_aal2_required": - return h.signin(c, sessionToken) - } + var authCode string + select { + case authCode = <-code: + // ok + case err := <-errs: + fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) + return nil, fmt.Errorf("failed OAuth2 authorization: %w", err) } - return nil, err -} -func (h *CommandHelper) sessionToContext(session *cloud.Session, token string) (*AuthContext, error) { - email, err := h.getField(session.Identity.Traits, "email") + token, err := oac.Exchange( + h.Ctx, + authCode, + oauth2.SetAuthURLParam("code_verifier", codeVerifier), + ) if err != nil { - return nil, err + outcome <- data{"Login failed", err.Error()} + fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) + return nil, fmt.Errorf("failed OAuth2 token exchange: %w", err) } + outcome <- data{"Successfully logged into Ory Network.", "You may now close this browser tab and continue on with the Ory CLI."} - return &AuthContext{ - Version: Version, - SessionToken: token, - IdentityTraits: AuthIdentity{ - Email: email.String(), - ID: uuid.FromStringOrNil(session.Identity.Id), - }, - }, nil -} + fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") -func (h *CommandHelper) Authenticate() (*AuthContext, error) { - if h.IsQuiet { - return nil, errors.New("can not sign in or sign up when flag --quiet is set") + scope, _ := token.Extra("scope").(string) + if !slices.Contains(strings.Split(scope, " "), "offline_access") { + fmt.Fprintf(h.VerboseErrWriter, + "You have not granted the 'offline_access' permission during login and will have to authenticate again in %v.\n", + time.Until(token.Expiry).Round(time.Second), + ) + } + for range code { + // drain/wait } + // ok, response written to browser - ac, err := h.readConfig() + return &AuthContext{AccessToken: token}, nil +} + +func (h *CommandHelper) runOAuth2CallbackServer() (code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { + l, err := net.Listen("tcp", ":12345") if err != nil { - if !errors.Is(err, ErrNoConfig) { - return nil, err - } + fmt.Fprintln(h.VerboseErrWriter, "Failed to allocate port for OAuth2 callback handler") + os.Exit(1) } - - if len(ac.SessionToken) > 0 { - if !h.NoConfirm { - ok, err := cmdx.AskScannerForConfirmation(fmt.Sprintf("You are signed in as \"%s\" already. Do you wish to authenticate with another account?", ac.IdentityTraits.Email), h.Stdin, h.VerboseErrWriter) - if err != nil { - return nil, err - } else if !ok { - return ac, nil + _code, _errs, _outcome := make(chan string), make(chan error), make(chan data) + srv := http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer close(_code) + r.ParseForm() + code := r.Form.Get("code") + error, desc := r.Form.Get("error"), r.Form.Get("error_description") + if code == "" { + tmpl.Execute(w, &data{"Login failed", desc + ": " + error}) + _errs <- fmt.Errorf("%s: %s", error, desc) + return } - _, _ = fmt.Fprintf(h.VerboseErrWriter, "Ok, signing you out!\n") - } - - if err := h.SignOut(); err != nil { - return nil, err - } + _code <- code + tmpl.Execute(w, <-_outcome) + }), } - - c, err := NewKratosClient() - if err != nil { - return nil, err + go srv.Serve(l) + return _code, _errs, _outcome, func() { + ctx, cancel := context.WithTimeout(h.Ctx, 3*time.Second) + defer cancel() + srv.Shutdown(ctx) } +} - signIn, err := cmdx.AskScannerForConfirmation("Do you want to sign in to an existing Ory Network account?", h.Stdin, h.VerboseErrWriter) +func (h *CommandHelper) SignOut() error { + ac, err := h.readConfig() if err != nil { - return nil, err + return err } - - if signIn { - ac, err = h.signin(c, "") - if err != nil { - return nil, err - } - } else { - _, _ = fmt.Fprintln(h.VerboseErrWriter, "Great to have you! Let's create a new Ory Network account. Select the Enter key to start the account creation wizard.") - - ac, err = h.signup(c) - if err != nil { - return nil, err - } + if ac.AccessToken == nil { + return h.WriteConfig(new(AuthContext)) } - - if err := h.WriteConfig(ac); err != nil { - return nil, err + revoke, err := url.Parse(oac.Endpoint.AuthURL) + if err != nil { + return err } - - _, _ = fmt.Fprintf(h.VerboseErrWriter, "You are now signed in as: %s\n", ac.IdentityTraits.Email) - - if len(ac.SessionToken) == 0 { - return nil, errors.Errorf("unable to authenticate") + revoke.Path = "/oauth2/revoke" + res, err := http.PostForm(revoke.String(), url.Values{ + "client_id": []string{oac.ClientID}, + "token": []string{ac.AccessToken.RefreshToken}, // this also revokes the associated access token + }) + if err != nil { + return err + } + defer res.Body.Close() + if res.StatusCode < 200 || res.StatusCode > 299 { + body, _ := io.ReadAll(res.Body) + fmt.Fprintf(h.VerboseErrWriter, "/oauth2/revoke response: %v", string(body)) + return fmt.Errorf("failed to revoke token") } - - return ac, nil -} - -func (h *CommandHelper) SignOut() error { return h.WriteConfig(new(AuthContext)) } diff --git a/go.mod b/go.mod index 0274c7c0..a2d6c92c 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/ory/keto v0.10.0-alpha.0.0.20221026143738-31e323a91b68 github.com/ory/kratos v0.10.2-0.20221108163448-d3d148b3a589 github.com/ory/x v0.0.511-0.20221108105728-3fed9bc99daf + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.3.0 github.com/rs/cors v1.8.2 @@ -49,6 +50,8 @@ require ( github.com/tidwall/gjson v1.14.3 github.com/tidwall/sjson v1.2.5 github.com/urfave/negroni v1.0.0 + golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 + golang.org/x/oauth2 v0.3.0 golang.org/x/term v0.3.0 golang.org/x/text v0.5.0 gopkg.in/yaml.v2 v2.4.0 @@ -231,7 +234,6 @@ require ( github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/philhofer/fwd v1.1.1 // indirect - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect @@ -312,11 +314,9 @@ require ( go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.17.0 // indirect golang.org/x/crypto v0.1.0 // indirect - golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.6.0 // indirect golang.org/x/net v0.4.0 // indirect - golang.org/x/oauth2 v0.3.0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/time v0.1.0 // indirect From 0bbfbfc6cad418f4fe00889ad4768ab21d215839 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 25 Sep 2023 13:35:02 +0200 Subject: [PATCH 02/16] 123 --- .ory-k3d.json | 1 + .vscode/settings.json | 4 + cmd/cloudx/client/client.go | 18 +-- cmd/cloudx/client/handler.go | 194 +++++++++++-------------------- cmd/cloudx/client/http_client.go | 31 ----- cmd/cloudx/client/sdks.go | 11 +- cmd/cloudx/client/token.go | 34 ++++++ cmd/cloudx/create.go | 1 + cmd/cloudx/delete.go | 1 + cmd/cloudx/get.go | 2 +- cmd/cloudx/import.go | 1 + cmd/cloudx/introspect.go | 1 + cmd/cloudx/is.go | 1 + cmd/cloudx/list.go | 1 + cmd/cloudx/patch.go | 1 + cmd/cloudx/revoke.go | 2 + cmd/cloudx/update.go | 1 + cmd/cloudx/validate.go | 1 + go.mod | 22 ++-- go.sum | 42 ++++--- 20 files changed, 164 insertions(+), 206 deletions(-) create mode 100644 .ory-k3d.json create mode 100644 .vscode/settings.json delete mode 100644 cmd/cloudx/client/http_client.go create mode 100644 cmd/cloudx/client/token.go diff --git a/.ory-k3d.json b/.ory-k3d.json new file mode 100644 index 00000000..51c62c79 --- /dev/null +++ b/.ory-k3d.json @@ -0,0 +1 @@ +{"version":"v0alpha0","session_token":"","selected_project":"00000000-0000-0000-0000-000000000000","session_identity_traits":{"ID":"","email":""},"oauth_token":{"access_token":"ory_at_UorVx_iCXkbRnqvQz_oYghxoaXw4RzWQbD17ucdDKxo.uw5dzywjTO3iQFzZTJzAOwcgOVEeX2JO0UxMAGo0hI8","token_type":"bearer","refresh_token":"ory_rt_TgJ92omytURpbB6_4C24leOI9JWlSwR_RabqmkfHTzk.ihpEHua5fC7q9gZLMK_e_lTfRK5E95YMlYyId1pMMgk","expiry":"2023-09-21T18:59:20.810267+02:00"}} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..1385df3c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "gopls": { "formatting.local": "github.com/ory" } + } + \ No newline at end of file diff --git a/cmd/cloudx/client/client.go b/cmd/cloudx/client/client.go index 98ca663b..596ecefc 100644 --- a/cmd/cloudx/client/client.go +++ b/cmd/cloudx/client/client.go @@ -6,12 +6,11 @@ package client import ( "context" "fmt" - "net/http" "net/url" "os" - "time" cloud "github.com/ory/client-go" + "golang.org/x/oauth2" "github.com/hashicorp/go-retryablehttp" "github.com/spf13/cobra" @@ -95,10 +94,7 @@ func ContextWithClient(ctx context.Context) context.Context { } conf := hydra.NewConfiguration() - conf.HTTPClient = &http.Client{ - Transport: &bearerTokenTransporter{RoundTripper: c.StandardClient().Transport, bearerToken: ac.SessionToken}, - Timeout: time.Second * 10, - } + conf.HTTPClient = oac.Client(context.WithValue(context.Background(), oauth2.HTTPClient, c.StandardClient()), ac.AccessToken) consoleURL, err := url.ParseRequestURI(makeCloudConsoleURL(p.Slug + ".projects")) if err != nil { @@ -117,14 +113,8 @@ func ContextWithClient(ctx context.Context) context.Context { // We use the cloud console API because it works with ory cloud session tokens. return &kratoscli.ClientContext{ - Endpoint: makeCloudConsoleURL(p.Slug + ".projects"), - HTTPClient: &http.Client{ - Transport: &bearerTokenTransporter{ - RoundTripper: c.StandardClient().Transport, - bearerToken: ac.SessionToken, - }, - Timeout: time.Second * 10, - }, + Endpoint: makeCloudConsoleURL(p.Slug + ".projects"), + HTTPClient: oac.Client(context.WithValue(context.Background(), oauth2.HTTPClient, c.StandardClient()), ac.AccessToken), }, nil }) return ctx diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index adeebb30..de20f6d9 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -7,8 +7,6 @@ import ( "bufio" "bytes" "context" - "crypto/sha256" - "encoding/base64" "encoding/json" stderrs "errors" "fmt" @@ -19,7 +17,6 @@ import ( "net/http" "net/url" "os" - "os/exec" "path/filepath" "strings" "time" @@ -30,6 +27,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/tidwall/gjson" + "github.com/toqueteos/webbrowser" "golang.org/x/exp/slices" "golang.org/x/oauth2" "golang.org/x/term" @@ -63,21 +61,20 @@ type AuthContext struct { SessionToken string `json:"session_token"` SelectedProject uuid.UUID `json:"selected_project"` IdentityTraits AuthIdentity `json:"session_identity_traits"` - AccessToken *oauth2.Token `json:"access_token"` + AccessToken *oauth2.Token `json:"oauth_token"` } func (i *AuthContext) ID() string { - return i.IdentityTraits.ID.String() + return i.IdentityTraits.ID } func (*AuthContext) Header() []string { - return []string{"ID", "EMAIL", "SELECTED_PROJECT"} + return []string{"ID", "SELECTED_PROJECT"} } func (i *AuthContext) Columns() []string { return []string{ i.ID(), - i.IdentityTraits.Email, i.SelectedProject.String(), } } @@ -87,7 +84,7 @@ func (i *AuthContext) Interface() interface{} { } type AuthIdentity struct { - ID uuid.UUID + ID string Email string `json:"email"` } @@ -235,6 +232,10 @@ func (h *CommandHelper) HasValidContext() (*AuthContext, bool, error) { return nil, false, err } + if c.AccessToken != nil { + return c, true, nil + } + if len(c.SessionToken) > 0 { client, err := NewKratosClient() if err != nil { @@ -294,18 +295,8 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) { } if ac.AccessToken != nil { - // check existing token - t, err := oac.TokenSource(h.Ctx, ac.AccessToken).Token() - if err == nil { - // we're good - ac.AccessToken = t - if err := h.WriteConfig(ac); err != nil { - return nil, err - } - fmt.Fprintf(h.VerboseWriter, "You are already logged in.\n") - return ac, nil - } - fmt.Fprintf(h.VerboseErrWriter, "must get new access token: %v\n", err) + fmt.Fprintf(h.VerboseWriter, "You are already logged in.\n") + return ac, nil } ac, err = h.loginOAuth2() @@ -322,13 +313,12 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) { var ( oac = oauth2.Config{ - // ClientID: "a3d01c0c-bf2a-44aa-a6ff-0e27ed79c399", - ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", + ClientID: "ory-cli", Endpoint: oauth2.Endpoint{ - // AuthURL: "https://project.console.ory:8080/oauth2/auth", - // TokenURL: "https://project.console.ory:8080/oauth2/token", - AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", - TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", + AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", + TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", + // AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", + // TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", AuthStyle: oauth2.AuthStyleInParams, }, RedirectURL: "http://localhost:12345/callback", @@ -349,33 +339,29 @@ type data struct { } func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { - code, errs, outcome, stop := h.runOAuth2CallbackServer() + state := randx.MustString(32, randx.AlphaNum) + code, errs, outcome, stop := h.runOAuth2CallbackServer(state) defer stop() - codeVerifier := randx.MustString(64, randx.AlphaNum) - sha := sha256.Sum256([]byte(codeVerifier)) - codeChallege := base64.RawURLEncoding.EncodeToString(sha[:]) - url := oac.AuthCodeURL(randx.MustString(32, randx.AlphaNum), - oauth2.SetAuthURLParam("scope", "offline_access"), - oauth2.SetAuthURLParam("code_challenge_method", "S256"), - oauth2.SetAuthURLParam("code_challenge", codeChallege), + codeVerifier := oauth2.GenerateVerifier() + url := oac.AuthCodeURL(state, + oauth2.SetAuthURLParam("scope", "offline_access profile email"), + oauth2.S256ChallengeOption(codeVerifier), oauth2.SetAuthURLParam("response_type", "code"), - oauth2.SetAuthURLParam("prompt", "consent"), + oauth2.SetAuthURLParam("prompt", "login"), + oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")+" "+makeCloudConsoleURL("*.projects")), ) - err := exec.Command("open", url).Run() - if err != nil { - return nil, fmt.Errorf("failed to open browser: %w", err) - } + println(url) fmt.Fprintf(h.VerboseErrWriter, `A browser should have opened for you to complete your login to Ory Network. If no browser opened, visit the below page to continue: - %s - + %s `, url) + _ = webbrowser.Open(url) var authCode string select { @@ -389,7 +375,7 @@ If no browser opened, visit the below page to continue: token, err := oac.Exchange( h.Ctx, authCode, - oauth2.SetAuthURLParam("code_verifier", codeVerifier), + oauth2.VerifierOption(codeVerifier), ) if err != nil { outcome <- data{"Login failed", err.Error()} @@ -400,6 +386,11 @@ If no browser opened, visit the below page to continue: fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") + enc := json.NewEncoder(h.VerboseWriter) + enc.SetIndent("", "\t") + enc.SetEscapeHTML(false) + enc.Encode(token) + scope, _ := token.Extra("scope").(string) if !slices.Contains(strings.Split(scope, " "), "offline_access") { fmt.Fprintf(h.VerboseErrWriter, @@ -415,7 +406,7 @@ If no browser opened, visit the below page to continue: return &AuthContext{AccessToken: token}, nil } -func (h *CommandHelper) runOAuth2CallbackServer() (code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { +func (h *CommandHelper) runOAuth2CallbackServer(state string) (code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { l, err := net.Listen("tcp", ":12345") if err != nil { fmt.Fprintln(h.VerboseErrWriter, "Failed to allocate port for OAuth2 callback handler") @@ -426,6 +417,11 @@ func (h *CommandHelper) runOAuth2CallbackServer() (code <-chan string, errs <-ch Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer close(_code) r.ParseForm() + if s := r.Form.Get("state"); s != state { + tmpl.Execute(w, &data{"Login failed", ""}) + _errs <- fmt.Errorf("state mismatch: expected %s, got %s", state, s) + return + } code := r.Form.Get("code") error, desc := r.Form.Get("error"), r.Form.Get("error_description") if code == "" { @@ -463,29 +459,21 @@ func (h *CommandHelper) SignOut() error { "token": []string{ac.AccessToken.RefreshToken}, // this also revokes the associated access token }) if err != nil { - return err - } - defer res.Body.Close() - if res.StatusCode < 200 || res.StatusCode > 299 { - body, _ := io.ReadAll(res.Body) - fmt.Fprintf(h.VerboseErrWriter, "/oauth2/revoke response: %v", string(body)) - return fmt.Errorf("failed to revoke token") + fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", err) + } else { + defer res.Body.Close() + if res.StatusCode < 200 || res.StatusCode > 299 { + body, _ := io.ReadAll(res.Body) + fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", string(body)) + } } return h.WriteConfig(new(AuthContext)) } func (h *CommandHelper) ListProjects() ([]cloud.ProjectMetadata, error) { - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - - projects, res, err := c.ProjectApi.ListProjects(h.Ctx).Execute() + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + projects, res, err := c.ProjectApi.ListProjects(ctx).Execute() if err != nil { return nil, handleError("unable to list projects", res, err) } @@ -498,16 +486,6 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) return nil, errors.Errorf("No project selected! Please see the help message on how to set one.") } - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - id := uuid.FromStringOrNil(projectOrSlug) if id == uuid.Nil { pjs, err := h.ListProjects() @@ -530,7 +508,9 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) } } - project, res, err := c.ProjectApi.GetProject(h.Ctx, id.String()).Execute() + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + project, res, err := c.ProjectApi.GetProject(ctx, id.String()).Execute() if err != nil { return nil, handleError("unable to get project", res, err) } @@ -539,17 +519,9 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) } func (h *CommandHelper) CreateProject(name string, setDefault bool) (*cloud.Project, error) { - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - - project, res, err := c.ProjectApi.CreateProject(h.Ctx).CreateProjectBody(*cloud.NewCreateProjectBody(strings.TrimSpace(name))).Execute() + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + project, res, err := c.ProjectApi.CreateProject(ctx).CreateProjectBody(*cloud.NewCreateProjectBody(strings.TrimSpace(name))).Execute() if err != nil { return nil, handleError("unable to list projects", res, err) } @@ -599,16 +571,6 @@ func toPatch(op string, values []string) (patches []cloud.JsonPatch, err error) } func (h *CommandHelper) PatchProject(id string, raw []json.RawMessage, add, replace, del []string) (*cloud.SuccessfulProjectUpdate, error) { - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - var patches []cloud.JsonPatch for _, r := range raw { config, err := jsonx.EmbedSources(r, jsonx.WithIgnoreKeys("$id", "$schema"), jsonx.WithOnlySchemes("file")) @@ -641,7 +603,9 @@ func (h *CommandHelper) PatchProject(id string, raw []json.RawMessage, add, repl patches = append(patches, cloud.JsonPatch{Op: "remove", Path: del}) } - res, _, err := c.ProjectApi.PatchProject(h.Ctx, id).JsonPatch(patches).Execute() + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + res, _, err := c.ProjectApi.PatchProject(ctx, id).JsonPatch(patches).Execute() if err != nil { return nil, err } @@ -650,16 +614,6 @@ func (h *CommandHelper) PatchProject(id string, raw []json.RawMessage, add, repl } func (h *CommandHelper) UpdateProject(id string, name string, configs []json.RawMessage) (*cloud.SuccessfulProjectUpdate, error) { - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - for k := range configs { config, err := jsonx.EmbedSources( configs[k], @@ -702,10 +656,12 @@ func (h *CommandHelper) UpdateProject(id string, name string, configs []json.Raw return nil, errors.Errorf("at least one of the keys `services.identity.config` and `services.permission.config` and `services.oauth2.config` is required and can not be empty") } + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) if name != "" { payload.Name = name } else if payload.Name == "" { - res, _, err := c.ProjectApi.GetProject(h.Ctx, id).Execute() + res, _, err := c.ProjectApi.GetProject(ctx, id).Execute() if err != nil { return nil, errors.WithStack(err) } @@ -721,17 +677,9 @@ func (h *CommandHelper) UpdateProject(id string, name string, configs []json.Raw } func (h *CommandHelper) CreateAPIKey(projectIdOrSlug, name string) (*cloud.ProjectApiKey, error) { - ac, err := h.EnsureContext() - if err != nil { - return nil, err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return nil, err - } - - token, _, err := c.ProjectApi.CreateProjectApiKey(h.Ctx, projectIdOrSlug).CreateProjectApiKeyRequest(cloud.CreateProjectApiKeyRequest{Name: name}).Execute() + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + token, _, err := c.ProjectApi.CreateProjectApiKey(ctx, projectIdOrSlug).CreateProjectApiKeyRequest(cloud.CreateProjectApiKeyRequest{Name: name}).Execute() if err != nil { return nil, err } @@ -740,17 +688,9 @@ func (h *CommandHelper) CreateAPIKey(projectIdOrSlug, name string) (*cloud.Proje } func (h *CommandHelper) DeleteAPIKey(projectIdOrSlug, id string) error { - ac, err := h.EnsureContext() - if err != nil { - return err - } - - c, err := newCloudClient(ac.SessionToken) - if err != nil { - return err - } - - if _, err := c.ProjectApi.DeleteProjectApiKey(h.Ctx, projectIdOrSlug, id).Execute(); err != nil { + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) + c := newCloudClient(nil) + if _, err := c.ProjectApi.DeleteProjectApiKey(ctx, projectIdOrSlug, id).Execute(); err != nil { return err } diff --git a/cmd/cloudx/client/http_client.go b/cmd/cloudx/client/http_client.go deleted file mode 100644 index fa0adb14..00000000 --- a/cmd/cloudx/client/http_client.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -package client - -import ( - "net/http" - "time" -) - -type bearerTokenTransporter struct { - http.RoundTripper - bearerToken string -} - -func (t *bearerTokenTransporter) RoundTrip(req *http.Request) (*http.Response, error) { - if t.bearerToken != "" { - req.Header.Set("Authorization", "Bearer "+t.bearerToken) - } - return t.RoundTripper.RoundTrip(req) -} - -func newBearerTokenClient(token string) *http.Client { - return &http.Client{ - Transport: &bearerTokenTransporter{ - RoundTripper: http.DefaultTransport, - bearerToken: token, - }, - Timeout: time.Second * 30, - } -} diff --git a/cmd/cloudx/client/sdks.go b/cmd/cloudx/client/sdks.go index b9b411cf..e65a34b6 100644 --- a/cmd/cloudx/client/sdks.go +++ b/cmd/cloudx/client/sdks.go @@ -9,6 +9,9 @@ import ( "os" "time" + "golang.org/x/oauth2" + + "github.com/ory/cli/buildinfo" cloud "github.com/ory/client-go" "github.com/ory/x/stringsx" ) @@ -64,15 +67,17 @@ func NewKratosClient() (*cloud.APIClient, error) { return cloud.NewAPIClient(conf), nil } -func newCloudClient(token string) (*cloud.APIClient, error) { +func newCloudClient(t *oauth2.Token) *cloud.APIClient { u := makeCloudConsoleURL("api") conf := cloud.NewConfiguration() conf.Servers = cloud.ServerConfigurations{{URL: u}} - conf.HTTPClient = newBearerTokenClient(token) + // conf.HTTPClient = oac.Client(context.Background(), t) + conf.UserAgent = "ory-cli/" + buildinfo.Version if RateLimitHeader != "" { conf.AddDefaultHeader("Ory-RateLimit-Action", RateLimitHeader) } + conf.Debug = true - return cloud.NewAPIClient(conf), nil + return cloud.NewAPIClient(conf) } diff --git a/cmd/cloudx/client/token.go b/cmd/cloudx/client/token.go new file mode 100644 index 00000000..00a393bc --- /dev/null +++ b/cmd/cloudx/client/token.go @@ -0,0 +1,34 @@ +package client + +import ( + "fmt" + + "github.com/spf13/cobra" + "golang.org/x/oauth2" +) + +var Token *oauth2.Token + +func RegisterAuthHelpers(cmd *cobra.Command) { + var ( + h *CommandHelper + ac *AuthContext + ) + cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) { + fmt.Fprintf(cmd.OutOrStderr(), "RegisterAuthHelpers.PersistentPreRunE\n") + h, err = NewCommandHelper(cmd) + if err != nil { + return err + } + ac, err = h.EnsureContext() + if err != nil { + return err + } + Token = ac.AccessToken + return nil + } + cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { + fmt.Fprintf(cmd.OutOrStderr(), "RegisterAuthHelpers.PersistentPostRunE\n") + return h.WriteConfig(ac) + } +} diff --git a/cmd/cloudx/create.go b/cmd/cloudx/create.go index f5a9689d..531ff9d9 100644 --- a/cmd/cloudx/create.go +++ b/cmd/cloudx/create.go @@ -30,5 +30,6 @@ func NewCreateCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/delete.go b/cmd/cloudx/delete.go index ed21c57e..5f645b53 100644 --- a/cmd/cloudx/delete.go +++ b/cmd/cloudx/delete.go @@ -32,5 +32,6 @@ func NewDeleteCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/get.go b/cmd/cloudx/get.go index d21d4c6a..229201ed 100644 --- a/cmd/cloudx/get.go +++ b/cmd/cloudx/get.go @@ -33,6 +33,6 @@ func NewGetCmd() *cobra.Command { client.RegisterConfigFlag(cmd.PersistentFlags()) client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) - + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/import.go b/cmd/cloudx/import.go index ea51b555..f7be2525 100644 --- a/cmd/cloudx/import.go +++ b/cmd/cloudx/import.go @@ -29,5 +29,6 @@ func NewImportCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/introspect.go b/cmd/cloudx/introspect.go index e76cd51d..df3ffdb8 100644 --- a/cmd/cloudx/introspect.go +++ b/cmd/cloudx/introspect.go @@ -23,5 +23,6 @@ func NewIntrospectCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterHTTPClientFlags(cmd.PersistentFlags()) cmdx.RegisterFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/is.go b/cmd/cloudx/is.go index a1ef8efa..e0abcd4f 100644 --- a/cmd/cloudx/is.go +++ b/cmd/cloudx/is.go @@ -22,6 +22,7 @@ func NewIsCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/list.go b/cmd/cloudx/list.go index 437ca63f..eeebee0a 100644 --- a/cmd/cloudx/list.go +++ b/cmd/cloudx/list.go @@ -33,5 +33,6 @@ func NewListCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/patch.go b/cmd/cloudx/patch.go index 981f3b9d..87910bf5 100644 --- a/cmd/cloudx/patch.go +++ b/cmd/cloudx/patch.go @@ -23,5 +23,6 @@ func NewPatchCmd() *cobra.Command { project.NewPatchOAuth2ConfigCmd(), project.NewUpdateNamespaceConfigCmd(), ) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/revoke.go b/cmd/cloudx/revoke.go index af362995..929f1a52 100644 --- a/cmd/cloudx/revoke.go +++ b/cmd/cloudx/revoke.go @@ -6,6 +6,7 @@ package cloudx import ( "github.com/spf13/cobra" + "github.com/ory/cli/cmd/cloudx/client" "github.com/ory/cli/cmd/cloudx/oauth2" "github.com/ory/x/cmdx" ) @@ -19,5 +20,6 @@ func NewRevokeCmd() *cobra.Command { cmdx.RegisterHTTPClientFlags(cmd.PersistentFlags()) cmdx.RegisterFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/update.go b/cmd/cloudx/update.go index 1524f619..ef197790 100644 --- a/cmd/cloudx/update.go +++ b/cmd/cloudx/update.go @@ -29,6 +29,7 @@ func NewUpdateCmd() *cobra.Command { client.RegisterConfigFlag(cmd.PersistentFlags()) client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/validate.go b/cmd/cloudx/validate.go index 0f7422fd..2c58aa8c 100644 --- a/cmd/cloudx/validate.go +++ b/cmd/cloudx/validate.go @@ -23,5 +23,6 @@ func NewValidateCmd() *cobra.Command { client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) cmdx.RegisterJSONFormatFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/go.mod b/go.mod index a2d6c92c..fce7ea76 100644 --- a/go.mod +++ b/go.mod @@ -51,9 +51,9 @@ require ( github.com/tidwall/sjson v1.2.5 github.com/urfave/negroni v1.0.0 golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 - golang.org/x/oauth2 v0.3.0 - golang.org/x/term v0.3.0 - golang.org/x/text v0.5.0 + golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33 + golang.org/x/term v0.12.0 + golang.org/x/text v0.13.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -147,7 +147,7 @@ require ( github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92 // indirect github.com/google/go-github/v27 v27.0.1 // indirect @@ -313,19 +313,19 @@ require ( go.uber.org/automaxprocs v1.3.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.17.0 // indirect - golang.org/x/crypto v0.1.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/mod v0.6.0 // indirect - golang.org/x/net v0.4.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.15.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.3.0 // indirect + golang.org/x/sys v0.12.0 // indirect golang.org/x/time v0.1.0 // indirect - golang.org/x/tools v0.2.0 // indirect + golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/DataDog/dd-trace-go.v1 v1.43.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect diff --git a/go.sum b/go.sum index 4fc762be..fb0616b5 100644 --- a/go.sum +++ b/go.sum @@ -583,8 +583,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -1695,8 +1696,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1738,8 +1739,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1810,8 +1811,8 @@ golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1836,8 +1837,8 @@ golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= +golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33 h1:JtxNM2/PG/evek3/ZT0uu4BnPlOI3e6KwfW4w2Vh8WI= +golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1971,14 +1972,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1989,8 +1990,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2079,8 +2081,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2134,8 +2136,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -2231,8 +2234,9 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DataDog/dd-trace-go.v1 v1.43.0 h1:UE3SNh7T7ZnCrYsDZuUuwN3LFSc5aphaszUF+wMm4Sk= gopkg.in/DataDog/dd-trace-go.v1 v1.43.0/go.mod h1:YL9g+nlUY7ByCffD5pDytAqy99GNbytRV0EBpKuldM4= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= From 763fc6163a6f5d35b5240d7aa4be35725dd25b22 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Sat, 20 Apr 2024 14:37:43 +0200 Subject: [PATCH 03/16] feat: try more ports --- cmd/cloudx/client/handler.go | 52 ++++++++++++++++++++++-------------- cmd/cloudx/client/sdks.go | 8 ------ go.mod | 5 ++-- go.sum | 7 ++--- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index de20f6d9..34851f1c 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -13,6 +13,7 @@ import ( "html/template" "io" "io/fs" + "math/rand" "net" "net/http" "net/url" @@ -313,15 +314,15 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) { var ( oac = oauth2.Config{ - ClientID: "ory-cli", + // ClientID: "ory-cli", + ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", Endpoint: oauth2.Endpoint{ - AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", - TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", - // AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", - // TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", + // AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", + // TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", + AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", + TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", AuthStyle: oauth2.AuthStyleInParams, }, - RedirectURL: "http://localhost:12345/callback", } tmpl = template.Must(template.New("").Parse(` @@ -340,20 +341,20 @@ type data struct { func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { state := randx.MustString(32, randx.AlphaNum) - code, errs, outcome, stop := h.runOAuth2CallbackServer(state) + callbackURL, code, errs, outcome, stop := h.runOAuth2CallbackServer(state) defer stop() - codeVerifier := oauth2.GenerateVerifier() + oac.RedirectURL = callbackURL + pkceVerifier := oauth2.GenerateVerifier() url := oac.AuthCodeURL(state, - oauth2.SetAuthURLParam("scope", "offline_access profile email"), - oauth2.S256ChallengeOption(codeVerifier), + oauth2.S256ChallengeOption(pkceVerifier), + oauth2.SetAuthURLParam("scope", "offline_access"), oauth2.SetAuthURLParam("response_type", "code"), - oauth2.SetAuthURLParam("prompt", "login"), - oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")+" "+makeCloudConsoleURL("*.projects")), + oauth2.SetAuthURLParam("prompt", "consent"), + oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")), ) - println(url) - + _ = webbrowser.Open(url) fmt.Fprintf(h.VerboseErrWriter, `A browser should have opened for you to complete your login to Ory Network. If no browser opened, visit the below page to continue: @@ -361,7 +362,6 @@ If no browser opened, visit the below page to continue: %s `, url) - _ = webbrowser.Open(url) var authCode string select { @@ -375,7 +375,7 @@ If no browser opened, visit the below page to continue: token, err := oac.Exchange( h.Ctx, authCode, - oauth2.VerifierOption(codeVerifier), + oauth2.VerifierOption(pkceVerifier), ) if err != nil { outcome <- data{"Login failed", err.Error()} @@ -406,9 +406,21 @@ If no browser opened, visit the below page to continue: return &AuthContext{AccessToken: token}, nil } -func (h *CommandHelper) runOAuth2CallbackServer(state string) (code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { - l, err := net.Listen("tcp", ":12345") - if err != nil { +func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL string, code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { + var ( + l net.Listener + err error + ports = []int{12345, 34525, 49763, 51238, 59724, 60582, 62125} + ) + rand.Shuffle(len(ports), func(i, j int) { ports[i], ports[j] = ports[j], ports[i] }) + for _, port := range ports { + l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err == nil { + callbackURL = fmt.Sprintf("http://localhost:%d/callback", port) + break + } + } + if l == nil { fmt.Fprintln(h.VerboseErrWriter, "Failed to allocate port for OAuth2 callback handler") os.Exit(1) } @@ -434,7 +446,7 @@ func (h *CommandHelper) runOAuth2CallbackServer(state string) (code <-chan strin }), } go srv.Serve(l) - return _code, _errs, _outcome, func() { + return callbackURL, _code, _errs, _outcome, func() { ctx, cancel := context.WithTimeout(h.Ctx, 3*time.Second) defer cancel() srv.Shutdown(ctx) diff --git a/cmd/cloudx/client/sdks.go b/cmd/cloudx/client/sdks.go index e65a34b6..9f09cbc4 100644 --- a/cmd/cloudx/client/sdks.go +++ b/cmd/cloudx/client/sdks.go @@ -24,10 +24,6 @@ func CloudConsoleURL(prefix string) *url.URL { u = &url.URL{Scheme: "https", Host: "console.ory.sh"} } u.Host = prefix + "." + u.Host - if u.Port() == "" { - u.Host = u.Host + ":443" - } - return u } @@ -43,10 +39,6 @@ func CloudAPIsURL(prefix string) *url.URL { u = &url.URL{Scheme: "https", Host: "oryapis.com"} } u.Host = prefix + "." + u.Host - if u.Port() == "" { - u.Host = u.Host + ":443" - } - return u } diff --git a/go.mod b/go.mod index fce7ea76..20de8833 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,10 @@ require ( github.com/stretchr/testify v1.8.1 github.com/tidwall/gjson v1.14.3 github.com/tidwall/sjson v1.2.5 + github.com/toqueteos/webbrowser v1.2.0 github.com/urfave/negroni v1.0.0 golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 - golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33 + golang.org/x/oauth2 v0.19.0 golang.org/x/term v0.12.0 golang.org/x/text v0.13.0 gopkg.in/yaml.v2 v2.4.0 @@ -265,7 +266,6 @@ require ( github.com/tinylib/msgp v1.1.6 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect - github.com/toqueteos/webbrowser v1.2.0 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/urfave/cli v1.22.5 // indirect @@ -322,7 +322,6 @@ require ( golang.org/x/time v0.1.0 // indirect golang.org/x/tools v0.6.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index fb0616b5..12643edd 100644 --- a/go.sum +++ b/go.sum @@ -1837,8 +1837,8 @@ golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33 h1:JtxNM2/PG/evek3/ZT0uu4BnPlOI3e6KwfW4w2Vh8WI= -golang.org/x/oauth2 v0.12.1-0.20230912160149-2d9e4a2adf33/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1990,7 +1990,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2137,8 +2136,6 @@ google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From 0c20f428f201db45d0a9f678ef87cf509c9d2b79 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 22 Apr 2024 13:34:20 +0200 Subject: [PATCH 04/16] ... --- cmd/cloudx/client/handler.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index 34851f1c..0153f45a 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -314,13 +314,15 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) { var ( oac = oauth2.Config{ - // ClientID: "ory-cli", - ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", + ClientID: "ory-cli", + // ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", Endpoint: oauth2.Endpoint{ - // AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", - // TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", - AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", - TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", + AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", + TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", + // AuthURL: "https://project.console.ory:8080/oauth2/auth", + // TokenURL: "https://project.console.ory:8080/oauth2/token", + // AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", + // TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", AuthStyle: oauth2.AuthStyleInParams, }, } From 44b72eb3a648e999172059a918427302fbd89fd3 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 22 Apr 2024 23:03:13 +0200 Subject: [PATCH 05/16] ... --- cmd/cloudx/client/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index 0153f45a..257b77cb 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -352,7 +352,7 @@ func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { oauth2.S256ChallengeOption(pkceVerifier), oauth2.SetAuthURLParam("scope", "offline_access"), oauth2.SetAuthURLParam("response_type", "code"), - oauth2.SetAuthURLParam("prompt", "consent"), + oauth2.SetAuthURLParam("prompt", "login consent"), oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")), ) From adb7627f18579c9700a8a8d8c3df0634fc557154 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 23 Apr 2024 13:16:34 +0200 Subject: [PATCH 06/16] ... --- cmd/cloudx/client/handler.go | 76 ++++++++++++++++++------------------ cmd/cloudx/client/sdks.go | 2 + 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index 257b77cb..8d470f70 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -10,7 +10,6 @@ import ( "encoding/json" stderrs "errors" "fmt" - "html/template" "io" "io/fs" "math/rand" @@ -312,33 +311,18 @@ func (h *CommandHelper) Authenticate() (*AuthContext, error) { return ac, nil } -var ( - oac = oauth2.Config{ - ClientID: "ory-cli", - // ClientID: "7b29dd0e-3e98-4bf9-a14f-c6efbb35d508", - Endpoint: oauth2.Endpoint{ - AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", - TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", - // AuthURL: "https://project.console.ory:8080/oauth2/auth", - // TokenURL: "https://project.console.ory:8080/oauth2/token", - // AuthURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/auth", - // TokenURL: "https://epic-swanson-8q30djkp63.projects.oryapis.com/oauth2/token", - AuthStyle: oauth2.AuthStyleInParams, - }, - } - - tmpl = template.Must(template.New("").Parse(` - - Ory Network CLI login - -

{{ .Header }}

-

{{ .Message }}

- -`)) -) +var oac = oauth2.Config{ + ClientID: "ory-cli", + Endpoint: oauth2.Endpoint{ + AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", + TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", + AuthStyle: oauth2.AuthStyleInParams, + }, +} type data struct { - Header, Message string + OK bool + Error, Desc string } func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { @@ -380,11 +364,11 @@ If no browser opened, visit the below page to continue: oauth2.VerifierOption(pkceVerifier), ) if err != nil { - outcome <- data{"Login failed", err.Error()} + outcome <- data{OK: false, Error: "token exchange", Desc: "An error occured during the OAuth2 token exchange: " + err.Error()} fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) return nil, fmt.Errorf("failed OAuth2 token exchange: %w", err) } - outcome <- data{"Successfully logged into Ory Network.", "You may now close this browser tab and continue on with the Ory CLI."} + outcome <- data{OK: true} fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") @@ -400,10 +384,6 @@ If no browser opened, visit the below page to continue: time.Until(token.Expiry).Round(time.Second), ) } - for range code { - // drain/wait - } - // ok, response written to browser return &AuthContext{AccessToken: token}, nil } @@ -430,21 +410,29 @@ func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL strin srv := http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer close(_code) - r.ParseForm() + if err := r.ParseForm(); err != nil { + redirectErr(w, r, "parse form", "An error occured during CLI authentication. Please try again") + _errs <- err + return + } if s := r.Form.Get("state"); s != state { - tmpl.Execute(w, &data{"Login failed", ""}) + redirectErr(w, r, "state mismatch", "An error occured during CLI authentication. Please try again") _errs <- fmt.Errorf("state mismatch: expected %s, got %s", state, s) return } code := r.Form.Get("code") - error, desc := r.Form.Get("error"), r.Form.Get("error_description") if code == "" { - tmpl.Execute(w, &data{"Login failed", desc + ": " + error}) + error, desc := r.Form.Get("error"), r.Form.Get("error_description") + redirectErr(w, r, error, desc) _errs <- fmt.Errorf("%s: %s", error, desc) return } _code <- code - tmpl.Execute(w, <-_outcome) + if outcome := <-_outcome; !outcome.OK { + redirectErr(w, r, outcome.Error, outcome.Desc) + return + } + redirectOK(w, r) }), } go srv.Serve(l) @@ -455,6 +443,20 @@ func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL strin } } +func redirectOK(w http.ResponseWriter, r *http.Request) { + location := CloudConsoleURL("") + location.Path = "/projects/current/dashboard" + location.RawQuery = url.Values{"cli_auth": []string{"success"}}.Encode() + http.Redirect(w, r, location.String(), http.StatusFound) +} + +func redirectErr(w http.ResponseWriter, r *http.Request, err, desc string) { + location := CloudConsoleURL("") + location.Path = "/error" + location.RawQuery = url.Values{"error": []string{err}, "error_description": []string{desc}}.Encode() + http.Redirect(w, r, location.String(), http.StatusFound) +} + func (h *CommandHelper) SignOut() error { ac, err := h.readConfig() if err != nil { diff --git a/cmd/cloudx/client/sdks.go b/cmd/cloudx/client/sdks.go index 9f09cbc4..b5e21455 100644 --- a/cmd/cloudx/client/sdks.go +++ b/cmd/cloudx/client/sdks.go @@ -23,7 +23,9 @@ func CloudConsoleURL(prefix string) *url.URL { if err != nil { u = &url.URL{Scheme: "https", Host: "console.ory.sh"} } + if prefix != "" { u.Host = prefix + "." + u.Host + } return u } From 3f1407415ff5f5afbc20c1a3365bcf0b622249c0 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 23 Apr 2024 16:15:54 +0200 Subject: [PATCH 07/16] ... --- cmd/cloudx/client/client.go | 18 ++++++---- cmd/cloudx/client/handler.go | 65 +++++++++++++++++++----------------- cmd/cloudx/client/sdks.go | 8 ++--- cmd/cloudx/client/token.go | 12 +++---- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/cmd/cloudx/client/client.go b/cmd/cloudx/client/client.go index 596ecefc..cc526e1b 100644 --- a/cmd/cloudx/client/client.go +++ b/cmd/cloudx/client/client.go @@ -9,13 +9,14 @@ import ( "net/url" "os" - cloud "github.com/ory/client-go" "golang.org/x/oauth2" "github.com/hashicorp/go-retryablehttp" "github.com/spf13/cobra" flag "github.com/spf13/pflag" + "github.com/ory/cli/buildinfo" + cloud "github.com/ory/client-go" hydra "github.com/ory/hydra-client-go" hydracli "github.com/ory/hydra/cmd/cliclient" kratoscli "github.com/ory/kratos/cmd/cliclient" @@ -88,20 +89,23 @@ func ContextWithClient(ctx context.Context) context.Context { }) ctx = context.WithValue(ctx, hydracli.ClientContextKey, func(cmd *cobra.Command) (*hydra.APIClient, *url.URL, error) { - c, ac, p, err := Client(cmd) + _, ac, p, err := Client(cmd) if err != nil { return nil, nil, err } - conf := hydra.NewConfiguration() - conf.HTTPClient = oac.Client(context.WithValue(context.Background(), oauth2.HTTPClient, c.StandardClient()), ac.AccessToken) - + // We use the cloud console API because it works with ory cloud session tokens. consoleURL, err := url.ParseRequestURI(makeCloudConsoleURL(p.Slug + ".projects")) if err != nil { return nil, nil, err } - // We use the cloud console API because it works with ory cloud session tokens. - conf.Servers = hydra.ServerConfigurations{{URL: consoleURL.String()}} + conf := hydra.NewConfiguration() + conf.Servers = hydra.ServerConfigurations{{URL: consoleURL.String(), Variables: make(map[string]hydra.ServerVariable)}} + conf.Debug = true + conf.UserAgent = "ory-cli/" + buildinfo.Version + + cmd.SetContext(context.WithValue(cmd.Context(), hydra.ContextOAuth2, oac.TokenSource(cmd.Context(), ac.AccessToken))) + return hydra.NewAPIClient(conf), consoleURL, nil }) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index 8d470f70..8017ab95 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -232,7 +232,14 @@ func (h *CommandHelper) HasValidContext() (*AuthContext, bool, error) { return nil, false, err } - if c.AccessToken != nil { + if c.AccessToken == nil || !c.AccessToken.Valid() { + return nil, false, nil + } + + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, c.AccessToken)) + cl := newCloudClient() + _, _, err = cl.ProjectApi.GetActiveProjectInConsole(ctx).Execute() + if err == nil { return c, true, nil } @@ -370,13 +377,6 @@ If no browser opened, visit the below page to continue: } outcome <- data{OK: true} - fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") - - enc := json.NewEncoder(h.VerboseWriter) - enc.SetIndent("", "\t") - enc.SetEscapeHTML(false) - enc.Encode(token) - scope, _ := token.Extra("scope").(string) if !slices.Contains(strings.Split(scope, " "), "offline_access") { fmt.Fprintf(h.VerboseErrWriter, @@ -385,7 +385,19 @@ If no browser opened, visit the below page to continue: ) } - return &AuthContext{AccessToken: token}, nil + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, token)) + c := newCloudClient() + activeProject, _, err := c.ProjectApi.GetActiveProjectInConsole(ctx).Execute() + if err != nil { + return nil, fmt.Errorf("failed to get active project: %w", err) + } + + fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") + + return &AuthContext{ + AccessToken: token, + SelectedProject: uuid.FromStringOrNil(activeProject.GetProjectId()), + }, nil } func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL string, code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { @@ -487,9 +499,8 @@ func (h *CommandHelper) SignOut() error { } func (h *CommandHelper) ListProjects() ([]cloud.ProjectMetadata, error) { - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - projects, res, err := c.ProjectApi.ListProjects(ctx).Execute() + c := newCloudClient() + projects, res, err := c.ProjectApi.ListProjects(h.Ctx).Execute() if err != nil { return nil, handleError("unable to list projects", res, err) } @@ -524,9 +535,8 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) } } - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - project, res, err := c.ProjectApi.GetProject(ctx, id.String()).Execute() + c := newCloudClient() + project, res, err := c.ProjectApi.GetProject(h.Ctx, id.String()).Execute() if err != nil { return nil, handleError("unable to get project", res, err) } @@ -535,9 +545,8 @@ func (h *CommandHelper) GetProject(projectOrSlug string) (*cloud.Project, error) } func (h *CommandHelper) CreateProject(name string, setDefault bool) (*cloud.Project, error) { - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - project, res, err := c.ProjectApi.CreateProject(ctx).CreateProjectBody(*cloud.NewCreateProjectBody(strings.TrimSpace(name))).Execute() + c := newCloudClient() + project, res, err := c.ProjectApi.CreateProject(h.Ctx).CreateProjectBody(*cloud.NewCreateProjectBody(strings.TrimSpace(name))).Execute() if err != nil { return nil, handleError("unable to list projects", res, err) } @@ -619,9 +628,8 @@ func (h *CommandHelper) PatchProject(id string, raw []json.RawMessage, add, repl patches = append(patches, cloud.JsonPatch{Op: "remove", Path: del}) } - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - res, _, err := c.ProjectApi.PatchProject(ctx, id).JsonPatch(patches).Execute() + c := newCloudClient() + res, _, err := c.ProjectApi.PatchProject(h.Ctx, id).JsonPatch(patches).Execute() if err != nil { return nil, err } @@ -672,12 +680,11 @@ func (h *CommandHelper) UpdateProject(id string, name string, configs []json.Raw return nil, errors.Errorf("at least one of the keys `services.identity.config` and `services.permission.config` and `services.oauth2.config` is required and can not be empty") } - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) + c := newCloudClient() if name != "" { payload.Name = name } else if payload.Name == "" { - res, _, err := c.ProjectApi.GetProject(ctx, id).Execute() + res, _, err := c.ProjectApi.GetProject(h.Ctx, id).Execute() if err != nil { return nil, errors.WithStack(err) } @@ -693,9 +700,8 @@ func (h *CommandHelper) UpdateProject(id string, name string, configs []json.Raw } func (h *CommandHelper) CreateAPIKey(projectIdOrSlug, name string) (*cloud.ProjectApiKey, error) { - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - token, _, err := c.ProjectApi.CreateProjectApiKey(ctx, projectIdOrSlug).CreateProjectApiKeyRequest(cloud.CreateProjectApiKeyRequest{Name: name}).Execute() + c := newCloudClient() + token, _, err := c.ProjectApi.CreateProjectApiKey(h.Ctx, projectIdOrSlug).CreateProjectApiKeyRequest(cloud.CreateProjectApiKeyRequest{Name: name}).Execute() if err != nil { return nil, err } @@ -704,9 +710,8 @@ func (h *CommandHelper) CreateAPIKey(projectIdOrSlug, name string) (*cloud.Proje } func (h *CommandHelper) DeleteAPIKey(projectIdOrSlug, id string) error { - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, Token)) - c := newCloudClient(nil) - if _, err := c.ProjectApi.DeleteProjectApiKey(ctx, projectIdOrSlug, id).Execute(); err != nil { + c := newCloudClient() + if _, err := c.ProjectApi.DeleteProjectApiKey(h.Ctx, projectIdOrSlug, id).Execute(); err != nil { return err } diff --git a/cmd/cloudx/client/sdks.go b/cmd/cloudx/client/sdks.go index b5e21455..0e890572 100644 --- a/cmd/cloudx/client/sdks.go +++ b/cmd/cloudx/client/sdks.go @@ -9,8 +9,6 @@ import ( "os" "time" - "golang.org/x/oauth2" - "github.com/ory/cli/buildinfo" cloud "github.com/ory/client-go" "github.com/ory/x/stringsx" @@ -24,7 +22,7 @@ func CloudConsoleURL(prefix string) *url.URL { u = &url.URL{Scheme: "https", Host: "console.ory.sh"} } if prefix != "" { - u.Host = prefix + "." + u.Host + u.Host = prefix + "." + u.Host } return u } @@ -61,17 +59,15 @@ func NewKratosClient() (*cloud.APIClient, error) { return cloud.NewAPIClient(conf), nil } -func newCloudClient(t *oauth2.Token) *cloud.APIClient { +func newCloudClient() *cloud.APIClient { u := makeCloudConsoleURL("api") conf := cloud.NewConfiguration() conf.Servers = cloud.ServerConfigurations{{URL: u}} - // conf.HTTPClient = oac.Client(context.Background(), t) conf.UserAgent = "ory-cli/" + buildinfo.Version if RateLimitHeader != "" { conf.AddDefaultHeader("Ory-RateLimit-Action", RateLimitHeader) } conf.Debug = true - return cloud.NewAPIClient(conf) } diff --git a/cmd/cloudx/client/token.go b/cmd/cloudx/client/token.go index 00a393bc..ce69b831 100644 --- a/cmd/cloudx/client/token.go +++ b/cmd/cloudx/client/token.go @@ -1,13 +1,12 @@ package client import ( - "fmt" + "context" "github.com/spf13/cobra" - "golang.org/x/oauth2" -) -var Token *oauth2.Token + cloud "github.com/ory/client-go" +) func RegisterAuthHelpers(cmd *cobra.Command) { var ( @@ -15,7 +14,6 @@ func RegisterAuthHelpers(cmd *cobra.Command) { ac *AuthContext ) cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) { - fmt.Fprintf(cmd.OutOrStderr(), "RegisterAuthHelpers.PersistentPreRunE\n") h, err = NewCommandHelper(cmd) if err != nil { return err @@ -24,11 +22,11 @@ func RegisterAuthHelpers(cmd *cobra.Command) { if err != nil { return err } - Token = ac.AccessToken + cmd.SetContext(context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, ac.AccessToken))) + h.Ctx = cmd.Context() return nil } cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { - fmt.Fprintf(cmd.OutOrStderr(), "RegisterAuthHelpers.PersistentPostRunE\n") return h.WriteConfig(ac) } } From 19b24a2f74be8873a8c240986325301682bfaad0 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 23 Apr 2024 18:16:23 +0200 Subject: [PATCH 08/16] refactor --- cmd/cloudx/client/auth.go | 256 +++++++++++++++++++++++++++++++++++ cmd/cloudx/client/handler.go | 209 ---------------------------- cmd/cloudx/client/token.go | 32 ----- 3 files changed, 256 insertions(+), 241 deletions(-) create mode 100644 cmd/cloudx/client/auth.go delete mode 100644 cmd/cloudx/client/token.go diff --git a/cmd/cloudx/client/auth.go b/cmd/cloudx/client/auth.go new file mode 100644 index 00000000..b749d0e1 --- /dev/null +++ b/cmd/cloudx/client/auth.go @@ -0,0 +1,256 @@ +package client + +import ( + "context" + "fmt" + "io" + "math/rand" + "net" + "net/http" + "net/url" + "os" + "strings" + "time" + + "github.com/gofrs/uuid/v3" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/toqueteos/webbrowser" + "golang.org/x/exp/slices" + "golang.org/x/oauth2" + + cloud "github.com/ory/client-go" + "github.com/ory/x/randx" +) + +func RegisterAuthHelpers(cmd *cobra.Command) { + var ( + h *CommandHelper + ac *AuthContext + ) + cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) { + h, err = NewCommandHelper(cmd) + if err != nil { + return err + } + ac, err = h.EnsureContext() + if err != nil { + return err + } + cmd.SetContext(context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, ac.AccessToken))) + h.Ctx = cmd.Context() + return nil + } + cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { + return h.WriteConfig(ac) + } +} + +func (h *CommandHelper) Authenticate() (*AuthContext, error) { + if h.IsQuiet { + return nil, errors.New("can not sign in or sign up when flag --quiet is set") + } + + ac, err := h.readConfig() + if err != nil { + if !errors.Is(err, ErrNoConfig) { + return nil, err + } + } + + if ac.AccessToken != nil { + fmt.Fprintf(h.VerboseWriter, "You are already logged in.\n") + return ac, nil + } + + ac, err = h.loginOAuth2() + if err != nil { + return nil, err + } + + if err := h.WriteConfig(ac); err != nil { + return nil, err + } + + return ac, nil +} + +var oac = oauth2.Config{ + ClientID: "ory-cli", + Endpoint: oauth2.Endpoint{ + AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", + TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", + AuthStyle: oauth2.AuthStyleInParams, + }, +} + +type data struct { + OK bool + Error, Desc string +} + +func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { + state := randx.MustString(32, randx.AlphaNum) + callbackURL, code, errs, outcome, stop := h.runOAuth2CallbackServer(state) + defer stop() + + oac.RedirectURL = callbackURL + pkceVerifier := oauth2.GenerateVerifier() + url := oac.AuthCodeURL(state, + oauth2.S256ChallengeOption(pkceVerifier), + oauth2.SetAuthURLParam("scope", "offline_access"), + oauth2.SetAuthURLParam("response_type", "code"), + oauth2.SetAuthURLParam("prompt", "login consent"), + oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")), + ) + + _ = webbrowser.Open(url) + fmt.Fprintf(h.VerboseErrWriter, + `A browser should have opened for you to complete your login to Ory Network. +If no browser opened, visit the below page to continue: + + %s + +`, url) + + var authCode string + select { + case authCode = <-code: + // ok + case err := <-errs: + fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) + return nil, fmt.Errorf("failed OAuth2 authorization: %w", err) + } + + token, err := oac.Exchange( + h.Ctx, + authCode, + oauth2.VerifierOption(pkceVerifier), + ) + if err != nil { + outcome <- data{OK: false, Error: "token exchange", Desc: "An error occured during the OAuth2 token exchange: " + err.Error()} + fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) + return nil, fmt.Errorf("failed OAuth2 token exchange: %w", err) + } + outcome <- data{OK: true} + + scope, _ := token.Extra("scope").(string) + if !slices.Contains(strings.Split(scope, " "), "offline_access") { + fmt.Fprintf(h.VerboseErrWriter, + "You have not granted the 'offline_access' permission during login and will have to authenticate again in %v.\n", + time.Until(token.Expiry).Round(time.Second), + ) + } + + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, token)) + c := newCloudClient() + activeProject, _, err := c.ProjectAPI.GetActiveProjectInConsole(ctx).Execute() + if err != nil { + return nil, fmt.Errorf("failed to get active project: %w", err) + } + + fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") + + return &AuthContext{ + AccessToken: token, + SelectedProject: uuid.FromStringOrNil(activeProject.GetProjectId()), + }, nil +} + +func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL string, code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { + var ( + l net.Listener + err error + ports = []int{12345, 34525, 49763, 51238, 59724, 60582, 62125} + ) + rand.Shuffle(len(ports), func(i, j int) { ports[i], ports[j] = ports[j], ports[i] }) + for _, port := range ports { + l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err == nil { + callbackURL = fmt.Sprintf("http://localhost:%d/callback", port) + break + } + } + if l == nil { + fmt.Fprintln(h.VerboseErrWriter, "Failed to allocate port for OAuth2 callback handler") + os.Exit(1) + } + _code, _errs, _outcome := make(chan string), make(chan error), make(chan data) + srv := http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer close(_code) + if err := r.ParseForm(); err != nil { + redirectErr(w, r, "parse form", "An error occured during CLI authentication. Please try again") + _errs <- err + return + } + if s := r.Form.Get("state"); s != state { + redirectErr(w, r, "state mismatch", "An error occured during CLI authentication. Please try again") + _errs <- fmt.Errorf("state mismatch: expected %s, got %s", state, s) + return + } + code := r.Form.Get("code") + if code == "" { + error, desc := r.Form.Get("error"), r.Form.Get("error_description") + redirectErr(w, r, error, desc) + _errs <- fmt.Errorf("%s: %s", error, desc) + return + } + _code <- code + if outcome := <-_outcome; !outcome.OK { + redirectErr(w, r, outcome.Error, outcome.Desc) + return + } + redirectOK(w, r) + }), + } + go srv.Serve(l) + return callbackURL, _code, _errs, _outcome, func() { + ctx, cancel := context.WithTimeout(h.Ctx, 3*time.Second) + defer cancel() + srv.Shutdown(ctx) + } +} + +func redirectOK(w http.ResponseWriter, r *http.Request) { + location := CloudConsoleURL("") + location.Path = "/projects/current/dashboard" + location.RawQuery = url.Values{"cli_auth": []string{"success"}}.Encode() + http.Redirect(w, r, location.String(), http.StatusFound) +} + +func redirectErr(w http.ResponseWriter, r *http.Request, err, desc string) { + location := CloudConsoleURL("") + location.Path = "/error" + location.RawQuery = url.Values{"error": []string{err}, "error_description": []string{desc}}.Encode() + http.Redirect(w, r, location.String(), http.StatusFound) +} + +func (h *CommandHelper) SignOut() error { + ac, err := h.readConfig() + if err != nil { + return err + } + if ac.AccessToken == nil { + return h.WriteConfig(new(AuthContext)) + } + revoke, err := url.Parse(oac.Endpoint.AuthURL) + if err != nil { + return err + } + revoke.Path = "/oauth2/revoke" + res, err := http.PostForm(revoke.String(), url.Values{ + "client_id": []string{oac.ClientID}, + "token": []string{ac.AccessToken.RefreshToken}, // this also revokes the associated access token + }) + if err != nil { + fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", err) + } else { + defer res.Body.Close() + if res.StatusCode < 200 || res.StatusCode > 299 { + body, _ := io.ReadAll(res.Body) + fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", string(body)) + } + } + return h.WriteConfig(new(AuthContext)) +} diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index d16f6609..9e7df78a 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -295,215 +295,6 @@ func (h *CommandHelper) EnsureContext() (*AuthContext, error) { return c, nil } -func (h *CommandHelper) Authenticate() (*AuthContext, error) { - if h.IsQuiet { - return nil, errors.New("can not sign in or sign up when flag --quiet is set") - } - - ac, err := h.readConfig() - if err != nil { - if !errors.Is(err, ErrNoConfig) { - return nil, err - } - } - - if ac.AccessToken != nil { - fmt.Fprintf(h.VerboseWriter, "You are already logged in.\n") - return ac, nil - } - - ac, err = h.loginOAuth2() - if err != nil { - return nil, err - } - - if err := h.WriteConfig(ac); err != nil { - return nil, err - } - - return ac, nil -} - -var oac = oauth2.Config{ - ClientID: "ory-cli", - Endpoint: oauth2.Endpoint{ - AuthURL: makeCloudConsoleURL("project") + "/oauth2/auth", - TokenURL: makeCloudConsoleURL("project") + "/oauth2/token", - AuthStyle: oauth2.AuthStyleInParams, - }, -} - -type data struct { - OK bool - Error, Desc string -} - -func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { - state := randx.MustString(32, randx.AlphaNum) - callbackURL, code, errs, outcome, stop := h.runOAuth2CallbackServer(state) - defer stop() - - oac.RedirectURL = callbackURL - pkceVerifier := oauth2.GenerateVerifier() - url := oac.AuthCodeURL(state, - oauth2.S256ChallengeOption(pkceVerifier), - oauth2.SetAuthURLParam("scope", "offline_access"), - oauth2.SetAuthURLParam("response_type", "code"), - oauth2.SetAuthURLParam("prompt", "login consent"), - oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")), - ) - - _ = webbrowser.Open(url) - fmt.Fprintf(h.VerboseErrWriter, - `A browser should have opened for you to complete your login to Ory Network. -If no browser opened, visit the below page to continue: - - %s - -`, url) - - var authCode string - select { - case authCode = <-code: - // ok - case err := <-errs: - fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) - return nil, fmt.Errorf("failed OAuth2 authorization: %w", err) - } - - token, err := oac.Exchange( - h.Ctx, - authCode, - oauth2.VerifierOption(pkceVerifier), - ) - if err != nil { - outcome <- data{OK: false, Error: "token exchange", Desc: "An error occured during the OAuth2 token exchange: " + err.Error()} - fmt.Fprintf(h.VerboseErrWriter, "An error occured logging into Ory Network: %v\n", err) - return nil, fmt.Errorf("failed OAuth2 token exchange: %w", err) - } - outcome <- data{OK: true} - - scope, _ := token.Extra("scope").(string) - if !slices.Contains(strings.Split(scope, " "), "offline_access") { - fmt.Fprintf(h.VerboseErrWriter, - "You have not granted the 'offline_access' permission during login and will have to authenticate again in %v.\n", - time.Until(token.Expiry).Round(time.Second), - ) - } - - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, token)) - c := newCloudClient() - activeProject, _, err := c.ProjectAPI.GetActiveProjectInConsole(ctx).Execute() - if err != nil { - return nil, fmt.Errorf("failed to get active project: %w", err) - } - - fmt.Fprintf(h.VerboseErrWriter, "Successfully logged into Ory Network.\n") - - return &AuthContext{ - AccessToken: token, - SelectedProject: uuid.FromStringOrNil(activeProject.GetProjectId()), - }, nil -} - -func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL string, code <-chan string, errs <-chan error, outcome chan<- data, cleanup func()) { - var ( - l net.Listener - err error - ports = []int{12345, 34525, 49763, 51238, 59724, 60582, 62125} - ) - rand.Shuffle(len(ports), func(i, j int) { ports[i], ports[j] = ports[j], ports[i] }) - for _, port := range ports { - l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err == nil { - callbackURL = fmt.Sprintf("http://localhost:%d/callback", port) - break - } - } - if l == nil { - fmt.Fprintln(h.VerboseErrWriter, "Failed to allocate port for OAuth2 callback handler") - os.Exit(1) - } - _code, _errs, _outcome := make(chan string), make(chan error), make(chan data) - srv := http.Server{ - Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer close(_code) - if err := r.ParseForm(); err != nil { - redirectErr(w, r, "parse form", "An error occured during CLI authentication. Please try again") - _errs <- err - return - } - if s := r.Form.Get("state"); s != state { - redirectErr(w, r, "state mismatch", "An error occured during CLI authentication. Please try again") - _errs <- fmt.Errorf("state mismatch: expected %s, got %s", state, s) - return - } - code := r.Form.Get("code") - if code == "" { - error, desc := r.Form.Get("error"), r.Form.Get("error_description") - redirectErr(w, r, error, desc) - _errs <- fmt.Errorf("%s: %s", error, desc) - return - } - _code <- code - if outcome := <-_outcome; !outcome.OK { - redirectErr(w, r, outcome.Error, outcome.Desc) - return - } - redirectOK(w, r) - }), - } - go srv.Serve(l) - return callbackURL, _code, _errs, _outcome, func() { - ctx, cancel := context.WithTimeout(h.Ctx, 3*time.Second) - defer cancel() - srv.Shutdown(ctx) - } -} - -func redirectOK(w http.ResponseWriter, r *http.Request) { - location := CloudConsoleURL("") - location.Path = "/projects/current/dashboard" - location.RawQuery = url.Values{"cli_auth": []string{"success"}}.Encode() - http.Redirect(w, r, location.String(), http.StatusFound) -} - -func redirectErr(w http.ResponseWriter, r *http.Request, err, desc string) { - location := CloudConsoleURL("") - location.Path = "/error" - location.RawQuery = url.Values{"error": []string{err}, "error_description": []string{desc}}.Encode() - http.Redirect(w, r, location.String(), http.StatusFound) -} - -func (h *CommandHelper) SignOut() error { - ac, err := h.readConfig() - if err != nil { - return err - } - if ac.AccessToken == nil { - return h.WriteConfig(new(AuthContext)) - } - revoke, err := url.Parse(oac.Endpoint.AuthURL) - if err != nil { - return err - } - revoke.Path = "/oauth2/revoke" - res, err := http.PostForm(revoke.String(), url.Values{ - "client_id": []string{oac.ClientID}, - "token": []string{ac.AccessToken.RefreshToken}, // this also revokes the associated access token - }) - if err != nil { - fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", err) - } else { - defer res.Body.Close() - if res.StatusCode < 200 || res.StatusCode > 299 { - body, _ := io.ReadAll(res.Body) - fmt.Fprintf(h.VerboseErrWriter, "failed to revoke access token: %v\n", string(body)) - } - } - return h.WriteConfig(new(AuthContext)) -} - func (h *CommandHelper) ListProjects() ([]cloud.ProjectMetadata, error) { c := newCloudClient() projects, res, err := c.ProjectAPI.ListProjects(h.Ctx).Execute() diff --git a/cmd/cloudx/client/token.go b/cmd/cloudx/client/token.go deleted file mode 100644 index ce69b831..00000000 --- a/cmd/cloudx/client/token.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "context" - - "github.com/spf13/cobra" - - cloud "github.com/ory/client-go" -) - -func RegisterAuthHelpers(cmd *cobra.Command) { - var ( - h *CommandHelper - ac *AuthContext - ) - cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) (err error) { - h, err = NewCommandHelper(cmd) - if err != nil { - return err - } - ac, err = h.EnsureContext() - if err != nil { - return err - } - cmd.SetContext(context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, ac.AccessToken))) - h.Ctx = cmd.Context() - return nil - } - cmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { - return h.WriteConfig(ac) - } -} From 13afd455a25452e71c40baa3eb907be522d04066 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Tue, 23 Apr 2024 19:20:55 +0200 Subject: [PATCH 09/16] ... --- cmd/cloudx/client/handler.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index 9e7df78a..b9293fb6 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -12,14 +12,10 @@ import ( "fmt" "io" "io/fs" - "math/rand" - "net" "net/http" - "net/url" "os" "path/filepath" "strings" - "time" "github.com/gofrs/uuid/v3" "github.com/imdario/mergo" @@ -27,8 +23,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/tidwall/gjson" - "github.com/toqueteos/webbrowser" - "golang.org/x/exp/slices" "golang.org/x/oauth2" "golang.org/x/term" @@ -36,7 +30,6 @@ import ( "github.com/ory/x/cmdx" "github.com/ory/x/flagx" "github.com/ory/x/jsonx" - "github.com/ory/x/randx" "github.com/ory/x/stringsx" ) From f3154ef2a0c0de0f84f085984929c2dd12d24314 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Wed, 5 Jun 2024 15:49:48 +0200 Subject: [PATCH 10/16] ... --- cmd/cloudx/client/auth.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/cloudx/client/auth.go b/cmd/cloudx/client/auth.go index b749d0e1..a738546b 100644 --- a/cmd/cloudx/client/auth.go +++ b/cmd/cloudx/client/auth.go @@ -206,9 +206,7 @@ func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL strin } go srv.Serve(l) return callbackURL, _code, _errs, _outcome, func() { - ctx, cancel := context.WithTimeout(h.Ctx, 3*time.Second) - defer cancel() - srv.Shutdown(ctx) + _ = srv.Close() } } From 5492effa3e68bcc039765e93ecf3d3de0cc05e73 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Fri, 21 Jun 2024 21:25:20 +0200 Subject: [PATCH 11/16] feat: use slug.projects.oryapis.com --- cmd/cloudx/client/auth.go | 2 +- cmd/cloudx/client/client.go | 13 ++--- cmd/cloudx/client/handler.go | 6 ++- cmd/cloudx/oauth2/jwks.go | 1 + cmd/cloudx/perform.go | 1 + cmd/cloudx/relationtuples/relationtuples.go | 29 +++++------ go.mod | 27 +++++----- go.sum | 57 +++++++++------------ 8 files changed, 62 insertions(+), 74 deletions(-) diff --git a/cmd/cloudx/client/auth.go b/cmd/cloudx/client/auth.go index a738546b..704e7f6b 100644 --- a/cmd/cloudx/client/auth.go +++ b/cmd/cloudx/client/auth.go @@ -37,7 +37,7 @@ func RegisterAuthHelpers(cmd *cobra.Command) { if err != nil { return err } - cmd.SetContext(context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, ac.AccessToken))) + cmd.SetContext(context.WithValue(h.Ctx, cloud.ContextOAuth2, ac.TokenSource())) h.Ctx = cmd.Context() return nil } diff --git a/cmd/cloudx/client/client.go b/cmd/cloudx/client/client.go index 0f0b188b..5f3b7346 100644 --- a/cmd/cloudx/client/client.go +++ b/cmd/cloudx/client/client.go @@ -84,7 +84,6 @@ func ContextWithClient(ctx context.Context) context.Context { return nil } - // We use the cloud console API because it works with ory cloud session tokens. return apiURL }) @@ -94,19 +93,18 @@ func ContextWithClient(ctx context.Context) context.Context { return nil, nil, err } - // We use the cloud console API because it works with ory cloud session tokens. - consoleURL, err := url.ParseRequestURI(makeCloudConsoleURL(p.Slug + ".projects")) + apiURL, err := url.ParseRequestURI(makeCloudAPIsURL(p.Slug + ".projects")) if err != nil { return nil, nil, err } conf := hydra.NewConfiguration() - conf.Servers = hydra.ServerConfigurations{{URL: consoleURL.String(), Variables: make(map[string]hydra.ServerVariable)}} + conf.Servers = hydra.ServerConfigurations{{URL: apiURL.String(), Variables: make(map[string]hydra.ServerVariable)}} conf.Debug = true conf.UserAgent = "ory-cli/" + buildinfo.Version - cmd.SetContext(context.WithValue(cmd.Context(), hydra.ContextOAuth2, oac.TokenSource(cmd.Context(), ac.AccessToken))) + cmd.SetContext(context.WithValue(cmd.Context(), hydra.ContextOAuth2, ac.TokenSource())) - return hydra.NewAPIClient(conf), consoleURL, nil + return hydra.NewAPIClient(conf), apiURL, nil }) ctx = context.WithValue(ctx, kratoscli.ClientContextKey, func(cmd *cobra.Command) (*kratoscli.ClientContext, error) { @@ -115,9 +113,8 @@ func ContextWithClient(ctx context.Context) context.Context { return nil, err } - // We use the cloud console API because it works with ory cloud session tokens. return &kratoscli.ClientContext{ - Endpoint: makeCloudConsoleURL(p.Slug + ".projects"), + Endpoint: makeCloudAPIsURL(p.Slug + ".projects"), HTTPClient: oac.Client(context.WithValue(context.Background(), oauth2.HTTPClient, c.StandardClient()), ac.AccessToken), }, nil }) diff --git a/cmd/cloudx/client/handler.go b/cmd/cloudx/client/handler.go index b9293fb6..4fc2b359 100644 --- a/cmd/cloudx/client/handler.go +++ b/cmd/cloudx/client/handler.go @@ -57,6 +57,10 @@ type AuthContext struct { AccessToken *oauth2.Token `json:"oauth_token"` } +func (i *AuthContext) TokenSource() oauth2.TokenSource { + return oac.TokenSource(context.Background(), i.AccessToken) +} + func (i *AuthContext) ID() string { return i.IdentityTraits.ID } @@ -229,7 +233,7 @@ func (h *CommandHelper) HasValidContext() (*AuthContext, bool, error) { return nil, false, nil } - ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, oac.TokenSource(h.Ctx, c.AccessToken)) + ctx := context.WithValue(h.Ctx, cloud.ContextOAuth2, c.TokenSource()) cl := newCloudClient() _, _, err = cl.ProjectAPI.GetActiveProjectInConsole(ctx).Execute() if err == nil { diff --git a/cmd/cloudx/oauth2/jwks.go b/cmd/cloudx/oauth2/jwks.go index 74eb2e94..11bdcda5 100644 --- a/cmd/cloudx/oauth2/jwks.go +++ b/cmd/cloudx/oauth2/jwks.go @@ -17,6 +17,7 @@ func wrapHydraCmd(newCmd func() *cobra.Command) *cobra.Command { client.RegisterProjectFlag(c.Flags()) cmdx.RegisterFormatFlags(c.Flags()) cliclient.RegisterClientFlags(c.Flags()) + client.RegisterAuthHelpers(c) return c } diff --git a/cmd/cloudx/perform.go b/cmd/cloudx/perform.go index 26daba30..eb78ba71 100644 --- a/cmd/cloudx/perform.go +++ b/cmd/cloudx/perform.go @@ -26,6 +26,7 @@ func NewPerformCmd() *cobra.Command { client.RegisterConfigFlag(cmd.PersistentFlags()) client.RegisterYesFlag(cmd.PersistentFlags()) cmdx.RegisterNoiseFlags(cmd.PersistentFlags()) + client.RegisterAuthHelpers(cmd) return cmd } diff --git a/cmd/cloudx/relationtuples/relationtuples.go b/cmd/cloudx/relationtuples/relationtuples.go index 165f6a30..783477be 100644 --- a/cmd/cloudx/relationtuples/relationtuples.go +++ b/cmd/cloudx/relationtuples/relationtuples.go @@ -4,14 +4,16 @@ package relationtuples import ( + "context" "fmt" - "os" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/oauth" ketoClient "github.com/ory/keto/cmd/client" "github.com/ory/keto/cmd/relationtuple" - "github.com/ory/x/randx" "github.com/ory/cli/cmd/cloudx/client" ) @@ -57,25 +59,18 @@ func NewParseCmd() *cobra.Command { func forwardConnectionInfo(cmd *cobra.Command) { originalRunE := cmd.RunE cmd.RunE = func(cmd *cobra.Command, args []string) error { - _, _, project, err := client.Client(cmd) + _, ac, project, err := client.Client(cmd) if err != nil { return err } - h, err := client.NewCommandHelper(cmd) - if err != nil { - return err + dial := func(ctx context.Context, remote string) (*grpc.ClientConn, error) { + return grpc.DialContext(ctx, client.CloudAPIsURL(project.Slug+".projects").Host, + grpc.WithTransportCredentials(credentials.NewTLS(nil)), + grpc.WithBlock(), + grpc.WithPerRPCCredentials(oauth.TokenSource{ac.TokenSource()})) } - - key, err := h.CreateAPIKey(project.Slug, "keto-temp-"+randx.MustString(8, randx.AlphaNum)) - if err != nil { - return err - } - defer func() { _ = h.DeleteAPIKey(project.Slug, key.Id) }() - - _ = os.Setenv(ketoClient.EnvAuthToken, *key.Value) - _ = os.Setenv(ketoClient.EnvReadRemote, client.CloudAPIsURL(project.Slug+".projects").Host) - _ = os.Setenv(ketoClient.EnvWriteRemote, client.CloudAPIsURL(project.Slug+".projects").Host) + cmd.SetContext(context.WithValue(cmd.Context(), ketoClient.ContextKeyDialFunc, dial)) return originalRunE(cmd, args) } @@ -120,7 +115,7 @@ func hideKetoFlags(cmd *cobra.Command) { } } -// wrapForOryCLI wraps the Keto command to be used in the ORY CLI. +// wrapForOryCLI wraps the Keto command to be used in the Ory CLI. func wrapForOryCLI(cmd *cobra.Command) { cmd.Use = "relationships" cmd.Aliases = []string{"relation-tuples", "relationship", "relation-tuple"} diff --git a/go.mod b/go.mod index 9b2a8f7d..270dd09e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ replace ( github.com/gorilla/sessions => github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.14.16 github.com/ory/client-go/114 => github.com/ory/client-go v1.1.4 - github.com/ory/hydra-client-go => github.com/ory/hydra-client-go v1.11.9-0.20221102130300-f558e85344c8 github.com/ory/keto/proto => github.com/ory/keto/proto v0.11.1-alpha.0.0.20231229091411-ac44cabd79b7 ) @@ -29,7 +28,7 @@ require ( github.com/google/uuid v1.4.0 github.com/hashicorp/go-retryablehttp v0.7.4 github.com/imdario/mergo v0.3.16 - github.com/jackc/pgx/v4 v4.18.1 + github.com/jackc/pgx/v4 v4.18.2 github.com/ory/client-go v1.5.1 github.com/ory/client-go/114 v0.0.0-00010101000000-000000000000 github.com/ory/gochimp3 v0.0.0-20200417124117-ccd242db3655 @@ -54,9 +53,10 @@ require ( github.com/toqueteos/webbrowser v1.2.0 github.com/urfave/negroni v1.0.0 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f - golang.org/x/oauth2 v0.16.0 - golang.org/x/term v0.19.0 - golang.org/x/text v0.14.0 + golang.org/x/oauth2 v0.17.0 + golang.org/x/term v0.21.0 + golang.org/x/text v0.16.0 + google.golang.org/grpc v1.59.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -153,7 +153,7 @@ require ( github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.2.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 // indirect @@ -164,10 +164,10 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.14.1 // indirect + github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/puddle/v2 v2.1.2 // indirect @@ -283,19 +283,18 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.24.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/tools v0.20.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 0b6ba045..02bdea93 100644 --- a/go.sum +++ b/go.sum @@ -97,7 +97,6 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -514,8 +513,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -593,9 +592,8 @@ github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRb github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= -github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= -github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -614,8 +612,8 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= -github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -644,8 +642,8 @@ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgS github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0= github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= -github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= -github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -904,8 +902,6 @@ github.com/ory/hydra/v2 v2.2.0-pre.1 h1:+VzGZ6AtiajgTq0I5m2nYvEX0l+I/nIci5kTuVA/ github.com/ory/hydra/v2 v2.2.0-pre.1/go.mod h1:yQRtYAxtGOD41QakuLLI9GVO9BUQzL5ShAxikGZPQbA= github.com/ory/jsonschema/v3 v3.0.8 h1:Ssdb3eJ4lDZ/+XnGkvQS/te0p+EkolqwTsDOCxr/FmU= github.com/ory/jsonschema/v3 v3.0.8/go.mod h1:ZPzqjDkwd3QTnb2Z6PAS+OTvBE2x5i6m25wCGx54W/0= -github.com/ory/keto v0.11.1-alpha.0.0.20231228131228-b2c3464bdc4f h1:yAsriF/AhB+i6xwXE9e/qVVPwvivjctBEO1ug8lsKuw= -github.com/ory/keto v0.11.1-alpha.0.0.20231228131228-b2c3464bdc4f/go.mod h1:pZP9ao8GJ4r3dvNtoXZqXR1cZHSJa2x4VFKQaPH5myg= github.com/ory/keto/proto v0.11.1-alpha.0.0.20231229091411-ac44cabd79b7 h1:qHgWDFJ642DUYj7fMUs7pRTqr1oM7Hqw26qnxB9lvlk= github.com/ory/keto/proto v0.11.1-alpha.0.0.20231229091411-ac44cabd79b7/go.mod h1:6RagCXA7X1hhFSVjcy13ruIo8Dq/nj4J0mcN92qL+hY= github.com/ory/kratos v1.1.0-pre.0.0.20240205165553-fd7995077307 h1:GM/UJdUQY+t42wmo/6gcrqqr/BwwZI/NpyAirQtcppQ= @@ -1221,7 +1217,6 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1235,7 +1230,6 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1259,10 +1253,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1362,8 +1355,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1377,8 +1370,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1459,7 +1452,6 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1473,8 +1465,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1484,8 +1476,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1499,8 +1491,9 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1529,7 +1522,6 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1574,8 +1566,8 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1639,7 +1631,6 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -1710,8 +1701,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= From 3801e35053e5cc6fb28262bcd6984f1a36b9f9e7 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Sun, 23 Jun 2024 14:41:18 +0200 Subject: [PATCH 12/16] feat: e2e test (stub) --- .github/workflows/playwright.yml | 27 ++++++ .gitignore | 4 + package-lock.json | 141 +++++++++++++++++++++++++++++-- package.json | 2 + playwright.config.ts | 78 +++++++++++++++++ tests/login.spec.ts | 77 +++++++++++++++++ 6 files changed, 321 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.ts create mode 100644 tests/login.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..467190be --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 5761a099..a7d074a6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ unpacked_bin/ cypress/screenshots cypress/videos cli +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index b641e10e..bdafea26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,8 @@ "ory": "bin/ory" }, "devDependencies": { + "@playwright/test": "^1.44.1", + "@types/node": "^20.14.8", "cypress": "^8.7.0", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.5", @@ -127,6 +129,21 @@ "node": ">=10.13.0" } }, + "node_modules/@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "dependencies": { + "playwright": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -211,10 +228,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "14.17.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.32.tgz", - "integrity": "sha512-JcII3D5/OapPGx+eJ+Ik1SQGyt6WvuqdRfh9jUwL6/iHGjmyOriBDciBUu7lEIBTL2ijxwrR70WUnw5AEDmFvQ==", - "dev": true + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/qs": { "version": "6.9.7", @@ -838,6 +858,12 @@ "node": ">=12.0.0" } }, + "node_modules/cypress/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1262,6 +1288,20 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2859,6 +2899,36 @@ "node": ">=0.10.0" } }, + "node_modules/playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "dependencies": { + "playwright-core": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -3606,6 +3676,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -3865,6 +3941,15 @@ "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", "dev": true }, + "@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "requires": { + "playwright": "1.44.1" + } + }, "@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -3949,10 +4034,13 @@ "dev": true }, "@types/node": { - "version": "14.17.32", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.32.tgz", - "integrity": "sha512-JcII3D5/OapPGx+eJ+Ik1SQGyt6WvuqdRfh9jUwL6/iHGjmyOriBDciBUu7lEIBTL2ijxwrR70WUnw5AEDmFvQ==", - "dev": true + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/qs": { "version": "6.9.7", @@ -4433,6 +4521,14 @@ "untildify": "^4.0.0", "url": "^0.11.0", "yauzl": "^2.10.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + } } }, "dashdash": { @@ -4767,6 +4863,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5982,6 +6085,22 @@ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, + "playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.44.1" + } + }, + "playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true + }, "prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -6542,6 +6661,12 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index 9027d7be..f1952eed 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "binwrap": "^0.2.3" }, "devDependencies": { + "@playwright/test": "^1.44.1", + "@types/node": "^20.14.8", "cypress": "^8.7.0", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^2.0.5", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..19beb53e --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,78 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/login.spec.ts b/tests/login.spec.ts new file mode 100644 index 00000000..50afdec8 --- /dev/null +++ b/tests/login.spec.ts @@ -0,0 +1,77 @@ +import { test, expect } from "@playwright/test" +import { ChildProcessWithoutNullStreams, spawn } from "child_process" +import { randomBytes } from "crypto" +import { unlink } from "fs/promises" +import readline from "node:readline/promises" + +function generateRandomFileName(extension: string): string { + const randomString = randomBytes(16).toString("hex") + return `${randomString}${extension}` +} + +test.describe("should be able to login with the CLI", () => { + const config = generateRandomFileName(".cli-config.json") + let url: string = "" + let child: ChildProcessWithoutNullStreams + let rl: readline.Interface + + test.beforeAll(async () => { + child = spawn("./cli", ["auth"], { + env: { + HOME: "/dev/null", + ORY_CLOUD_ORYAPIS_URL: "https://oryapis:8080", + ORY_CLOUD_CONSOLE_URL: "https://console.ory:8080", + ORY_CLOUD_CONFIG_PATH: config, + }, + stdio: "pipe", + cwd: process.cwd(), + detached: false, + }) + child.on("error", (error) => { + test.fail(true, "Error running the CLI command") + }) + + rl = readline.createInterface(child.stderr) + await expect(async () => { + const line = await rl[Symbol.asyncIterator]().next() + expect(line.done).toBeFalsy() + const match = line.value.match( + new RegExp("https://project.console.ory.*"), + ) + expect(match).toBeTruthy() + url = match[0] + }).toPass() + }) + + test.afterAll(async () => { + child.kill() + await unlink(config) + }) + + test("with email and password", async ({ page }) => { + await page.goto(url) + const emailInput = await page.locator( + `[data-testid="node/input/identifier"] input`, + ) + await emailInput.fill("") + const passwordInput = await page.locator( + `[data-testid="node/input/password"] input`, + ) + await passwordInput.fill("") + const submit = page.locator( + '[type="submit"][name="method"][value="password"]', + ) + await submit.click() + + const allow = await page.getByRole("button", { name: "Allow" }) + await allow.click() + + let success = false + for await (const line of rl) { + if (line.includes("Successfully logged into Ory Network")) { + success = true + } + } + expect(success).toBeTruthy() + }) +}) From 698cb06dd118ea350aa6085ba4013a5152c84b62 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 24 Jun 2024 12:41:23 +0200 Subject: [PATCH 13/16] feat: fix e2e --- go.mod | 2 +- go.sum | 2 + package-lock.json | 96 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + tests/login.spec.ts | 39 +++++++++++++++--- 5 files changed, 134 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 270dd09e..ff4f55a0 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/ory/hydra-client-go/v2 v2.2.0-rc.3.0.20240202131107-1c7b57df3bb0 github.com/ory/hydra/v2 v2.2.0-pre.1 github.com/ory/jsonschema/v3 v3.0.8 - github.com/ory/keto v0.11.1-alpha.0.0.20231228131228-b2c3464bdc4f + github.com/ory/keto v0.13.0-alpha.0.0.20240624092507-567ceb9144b0 github.com/ory/kratos v1.1.0-pre.0.0.20240205165553-fd7995077307 github.com/ory/x v0.0.613 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 diff --git a/go.sum b/go.sum index 02bdea93..8127d1bc 100644 --- a/go.sum +++ b/go.sum @@ -902,6 +902,8 @@ github.com/ory/hydra/v2 v2.2.0-pre.1 h1:+VzGZ6AtiajgTq0I5m2nYvEX0l+I/nIci5kTuVA/ github.com/ory/hydra/v2 v2.2.0-pre.1/go.mod h1:yQRtYAxtGOD41QakuLLI9GVO9BUQzL5ShAxikGZPQbA= github.com/ory/jsonschema/v3 v3.0.8 h1:Ssdb3eJ4lDZ/+XnGkvQS/te0p+EkolqwTsDOCxr/FmU= github.com/ory/jsonschema/v3 v3.0.8/go.mod h1:ZPzqjDkwd3QTnb2Z6PAS+OTvBE2x5i6m25wCGx54W/0= +github.com/ory/keto v0.13.0-alpha.0.0.20240624092507-567ceb9144b0 h1:lsBYI6FsW+DQSzTld2fiC4bC8ELOfGrbARqguomWIqk= +github.com/ory/keto v0.13.0-alpha.0.0.20240624092507-567ceb9144b0/go.mod h1:NXGcNJ65ocZG5mvUCtoDHW53tA2MNoK2iQL7L0UDaGs= github.com/ory/keto/proto v0.11.1-alpha.0.0.20231229091411-ac44cabd79b7 h1:qHgWDFJ642DUYj7fMUs7pRTqr1oM7Hqw26qnxB9lvlk= github.com/ory/keto/proto v0.11.1-alpha.0.0.20231229091411-ac44cabd79b7/go.mod h1:6RagCXA7X1hhFSVjcy13ruIo8Dq/nj4J0mcN92qL+hY= github.com/ory/kratos v1.1.0-pre.0.0.20240205165553-fd7995077307 h1:GM/UJdUQY+t42wmo/6gcrqqr/BwwZI/NpyAirQtcppQ= diff --git a/package-lock.json b/package-lock.json index bdafea26..bc9f66a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.85", "hasInstallScript": true, "dependencies": { + "@ory/client": "^1.11.11", "binwrap": "^0.2.3" }, "bin": { @@ -120,6 +121,14 @@ "node": ">= 8" } }, + "node_modules/@ory/client": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/@ory/client/-/client-1.11.11.tgz", + "integrity": "sha512-HcAUikeLOj6XfNrohowHs0hv47k5sQ8I3gMy9914+wsTNRONRZdKJYf80AaiEUWyzGfpun9BebfjJU9YfLvP0w==", + "dependencies": { + "axios": "^1.6.1" + } + }, "node_modules/@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -464,6 +473,34 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1235,6 +1272,25 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -3935,6 +3991,14 @@ "fastq": "^1.6.0" } }, + "@ory/client": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/@ory/client/-/client-1.11.11.tgz", + "integrity": "sha512-HcAUikeLOj6XfNrohowHs0hv47k5sQ8I3gMy9914+wsTNRONRZdKJYf80AaiEUWyzGfpun9BebfjJU9YfLvP0w==", + "requires": { + "axios": "^1.6.1" + } + }, "@panva/asn1.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", @@ -4213,6 +4277,33 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4822,6 +4913,11 @@ "to-regex-range": "^5.0.1" } }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", diff --git a/package.json b/package.json index f1952eed..4362e2d9 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "prettier": "ory-prettier-styles", "dependencies": { + "@ory/client": "^1.11.11", "binwrap": "^0.2.3" }, "devDependencies": { diff --git a/tests/login.spec.ts b/tests/login.spec.ts index 50afdec8..e239aeb1 100644 --- a/tests/login.spec.ts +++ b/tests/login.spec.ts @@ -3,6 +3,7 @@ import { ChildProcessWithoutNullStreams, spawn } from "child_process" import { randomBytes } from "crypto" import { unlink } from "fs/promises" import readline from "node:readline/promises" +import * as sdk from "@ory/client" function generateRandomFileName(extension: string): string { const randomString = randomBytes(16).toString("hex") @@ -10,12 +11,40 @@ function generateRandomFileName(extension: string): string { } test.describe("should be able to login with the CLI", () => { + const email = `${randomBytes(16).toString("hex")}@example.com` + const password = randomBytes(16).toString("hex") const config = generateRandomFileName(".cli-config.json") let url: string = "" - let child: ChildProcessWithoutNullStreams + let child: ChildProcessWithoutNullStreams | undefined let rl: readline.Interface test.beforeAll(async () => { + const ory = new sdk.FrontendApi( + new sdk.Configuration({ + basePath: "https://project.console.ory:8080", + }), + ) + + const { + data: { id: flowID }, + } = await ory.createNativeRegistrationFlow() + const res = await ory.updateRegistrationFlow({ + flow: flowID, + updateRegistrationFlowBody: { + method: "password", + password, + traits: { + email, + name: "John Doe", + consent: { + newsletter: false, + tos: new Date().toISOString(), + }, + }, + }, + }) + expect(res.status).toBe(200) + child = spawn("./cli", ["auth"], { env: { HOME: "/dev/null", @@ -44,8 +73,8 @@ test.describe("should be able to login with the CLI", () => { }) test.afterAll(async () => { - child.kill() - await unlink(config) + child?.kill() + await unlink(config).catch(() => {}) }) test("with email and password", async ({ page }) => { @@ -53,11 +82,11 @@ test.describe("should be able to login with the CLI", () => { const emailInput = await page.locator( `[data-testid="node/input/identifier"] input`, ) - await emailInput.fill("") + await emailInput.fill(email) const passwordInput = await page.locator( `[data-testid="node/input/password"] input`, ) - await passwordInput.fill("") + await passwordInput.fill(password) const submit = page.locator( '[type="submit"][name="method"][value="password"]', ) From 198c65ddbc0dacf5d02e78162a2f5c2b73ef9ea6 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 24 Jun 2024 12:52:35 +0200 Subject: [PATCH 14/16] fix: delete temp --- .ory-k3d.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .ory-k3d.json diff --git a/.ory-k3d.json b/.ory-k3d.json deleted file mode 100644 index 51c62c79..00000000 --- a/.ory-k3d.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"v0alpha0","session_token":"","selected_project":"00000000-0000-0000-0000-000000000000","session_identity_traits":{"ID":"","email":""},"oauth_token":{"access_token":"ory_at_UorVx_iCXkbRnqvQz_oYghxoaXw4RzWQbD17ucdDKxo.uw5dzywjTO3iQFzZTJzAOwcgOVEeX2JO0UxMAGo0hI8","token_type":"bearer","refresh_token":"ory_rt_TgJ92omytURpbB6_4C24leOI9JWlSwR_RabqmkfHTzk.ihpEHua5fC7q9gZLMK_e_lTfRK5E95YMlYyId1pMMgk","expiry":"2023-09-21T18:59:20.810267+02:00"}} From e4cb149eff2bf53fa386be76aa43cb9d3e602d34 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Mon, 24 Jun 2024 13:47:48 +0200 Subject: [PATCH 15/16] fix: format --- playwright.config.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index 19beb53e..29a78363 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test" /** * Read environment variables from file. @@ -11,7 +11,7 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testDir: './tests', + testDir: "./tests", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -21,21 +21,21 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ // baseURL: 'http://127.0.0.1:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: "on-first-retry", }, /* Configure projects for major browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, // { @@ -75,4 +75,4 @@ export default defineConfig({ // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, // }, -}); +}) From 7186c1e858879693b4f06aff4feadb36e8693ba7 Mon Sep 17 00:00:00 2001 From: Arne Luenser Date: Thu, 27 Jun 2024 12:16:12 +0200 Subject: [PATCH 16/16] fix: listen on localhost, disable debug output --- cmd/cloudx/client/auth.go | 4 ++-- cmd/cloudx/client/client.go | 2 +- cmd/cloudx/client/sdks.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/cloudx/client/auth.go b/cmd/cloudx/client/auth.go index 704e7f6b..7e998305 100644 --- a/cmd/cloudx/client/auth.go +++ b/cmd/cloudx/client/auth.go @@ -98,7 +98,7 @@ func (h *CommandHelper) loginOAuth2() (*AuthContext, error) { pkceVerifier := oauth2.GenerateVerifier() url := oac.AuthCodeURL(state, oauth2.S256ChallengeOption(pkceVerifier), - oauth2.SetAuthURLParam("scope", "offline_access"), + oauth2.SetAuthURLParam("scope", "offline_access full_access"), oauth2.SetAuthURLParam("response_type", "code"), oauth2.SetAuthURLParam("prompt", "login consent"), oauth2.SetAuthURLParam("audience", makeCloudConsoleURL("api")), @@ -165,7 +165,7 @@ func (h *CommandHelper) runOAuth2CallbackServer(state string) (callbackURL strin ) rand.Shuffle(len(ports), func(i, j int) { ports[i], ports[j] = ports[j], ports[i] }) for _, port := range ports { - l, err = net.Listen("tcp", fmt.Sprintf(":%d", port)) + l, err = net.Listen("tcp", fmt.Sprintf("localhost:%d", port)) if err == nil { callbackURL = fmt.Sprintf("http://localhost:%d/callback", port) break diff --git a/cmd/cloudx/client/client.go b/cmd/cloudx/client/client.go index 5f3b7346..4d848023 100644 --- a/cmd/cloudx/client/client.go +++ b/cmd/cloudx/client/client.go @@ -99,7 +99,7 @@ func ContextWithClient(ctx context.Context) context.Context { } conf := hydra.NewConfiguration() conf.Servers = hydra.ServerConfigurations{{URL: apiURL.String(), Variables: make(map[string]hydra.ServerVariable)}} - conf.Debug = true + // conf.Debug = true conf.UserAgent = "ory-cli/" + buildinfo.Version cmd.SetContext(context.WithValue(cmd.Context(), hydra.ContextOAuth2, ac.TokenSource())) diff --git a/cmd/cloudx/client/sdks.go b/cmd/cloudx/client/sdks.go index 62145117..1876bdc9 100644 --- a/cmd/cloudx/client/sdks.go +++ b/cmd/cloudx/client/sdks.go @@ -69,6 +69,6 @@ func newCloudClient() *cloud.APIClient { if RateLimitHeader != "" { conf.AddDefaultHeader("Ory-RateLimit-Action", RateLimitHeader) } - conf.Debug = true + // conf.Debug = true return cloud.NewAPIClient(conf) }