Skip to content

Commit

Permalink
Saving token in secret manager works
Browse files Browse the repository at this point in the history
  • Loading branch information
filip-debricked committed Sep 5, 2024
1 parent 8e1432d commit 6252ecd
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 131 deletions.
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/debricked/cli
go 1.20

require (
github.com/becheran/wildmatch-go v1.0.0
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/chelnak/ysmrr v0.2.1
github.com/fatih/color v1.16.0
Expand All @@ -19,22 +20,24 @@ require (
golang.org/x/tools v0.19.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
github.com/becheran/wildmatch-go v1.0.0
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/cli/browser v1.0.0 // indirect
github.com/cli/safeexec v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-oauth2/oauth2 v3.9.2+incompatible // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand All @@ -61,6 +64,7 @@ require (
github.com/stretchr/objx v0.5.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/zalando/go-keyring v0.2.5 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.22.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
Expand Down Expand Up @@ -75,6 +77,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -107,6 +111,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-oauth2/oauth2 v3.9.2+incompatible h1:A8gSjq4110EgZDVk4ZtcpusynU2Fto9eM6sXvxL+EOs=
github.com/go-oauth2/oauth2 v3.9.2+incompatible/go.mod h1:GGcZ+i513KxN4yS7zBYfmwo3P+cyGvCS675uCNmWv/g=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -280,6 +286,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func NewLoginCmd(authenticator login.IAuthenticator) *cobra.Command {

func RunE(a login.IAuthenticator) func(_ *cobra.Command, args []string) error {
return func(cmd *cobra.Command, _ []string) error {
token, err := a.Authenticate()
token, err := login.AuthToken()
if err != nil {
return err
}
Expand Down
141 changes: 141 additions & 0 deletions internal/login/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package login

import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
// "github.com/debricked/cli/internal/client"
"log"
"math/rand"
"net/http"
"os/exec"
"runtime"
"strings"
"time"

"golang.org/x/oauth2"
)

type IAuthenticator interface {
Authenticate() (*oauth2.Token, error)
}

type Authenticator struct {
ClientID string
Scopes []string
}

const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

func generateRandomString(length int) string {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)

b := make([]byte, length)
for i := range b {
b[i] = charset[r.Intn(len(charset))]
}
return string(b)
}

func createCodeChallenge(codeVerifier string) string {
// Create a SHA-256 hash of the code verifier
hash := sha256.Sum256([]byte(codeVerifier))

// Encode the hash to base64
encoded := base64.StdEncoding.EncodeToString(hash[:])

// Make it URL safe
encoded = strings.TrimRight(encoded, "=")
encoded = strings.ReplaceAll(encoded, "+", "-")
encoded = strings.ReplaceAll(encoded, "/", "_")

return encoded
}

func (a Authenticator) Authenticate() (*oauth2.Token, error) {
// Set up OAuth2 configuration
config := &oauth2.Config{
ClientID: a.ClientID,
ClientSecret: "",
Endpoint: oauth2.Endpoint{
AuthURL: "https://debricked.com/app/oauth/authorize",
TokenURL: "https://debricked.com/app/oauth/token",
},
RedirectURL: "http://localhost:9096/callback",
Scopes: a.Scopes,
}

// Create a random state
state := generateRandomString(8)
codeVerifier := generateRandomString(64)

// Generate the authorization URL
authURL := config.AuthCodeURL(
state,
oauth2.SetAuthURLParam("code_challenge", createCodeChallenge(codeVerifier)),
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
)

// Start a temporary HTTP server to handle the callback
code := make(chan string)
defer close(code)
server := &http.Server{Addr: ":9096"}
// Start the server in a goroutine
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("HTTP server error: %v", err)
}
}()

// Ensure the server is shut down when we're done
defer server.Shutdown(context.Background())

// Open the browser for the user to log in
err := openBrowser(authURL)
if err != nil {
log.Fatal("Could not open browser:", err)
}

http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("state") != state {
http.Error(w, "Invalid state", http.StatusBadRequest)
return
}

code <- r.URL.Query().Get("code")
fmt.Fprintf(w, "Authentication successful! You can close this window now.")
})
// Wait for the authorization code
authCode := <-code

// Exchange the authorization code for a token
token, err := config.Exchange(
context.Background(),
authCode,
oauth2.SetAuthURLParam("client_id", a.ClientID),
oauth2.SetAuthURLParam("code_verifier", codeVerifier),
)
if err != nil {
return nil, err
}
return token, nil
}

func openBrowser(url string) error {
var cmd string
var args []string

switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
case "darwin":
cmd = "open"
default: // "linux", "freebsd", "openbsd", "netbsd"
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
132 changes: 3 additions & 129 deletions internal/login/login.go
Original file line number Diff line number Diff line change
@@ -1,136 +1,10 @@
package login

import (
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"math/rand"
"net/http"
"os/exec"
"runtime"
"strings"
"time"

"golang.org/x/oauth2"
)

type IAuthenticator interface {
Authenticate() (string, error)
}

type Authenticator struct {
ClientID string
Scopes []string
}

const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

func generateRandomString(length int) string {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)

b := make([]byte, length)
for i := range b {
b[i] = charset[r.Intn(len(charset))]
}
return string(b)
}

func createCodeChallenge(codeVerifier string) string {
// Create a SHA-256 hash of the code verifier
hash := sha256.Sum256([]byte(codeVerifier))

// Encode the hash to base64
encoded := base64.StdEncoding.EncodeToString(hash[:])

// Make it URL safe
encoded = strings.TrimRight(encoded, "=")
encoded = strings.ReplaceAll(encoded, "+", "-")
encoded = strings.ReplaceAll(encoded, "/", "_")

return encoded
}

func (a Authenticator) Authenticate() (string, error) {
// Set up OAuth2 configuration
config := &oauth2.Config{
ClientID: a.ClientID,
ClientSecret: "",
Endpoint: oauth2.Endpoint{
AuthURL: "https://debricked.com/app/oauth/authorize",
TokenURL: "https://debricked.com/app/oauth/token",
},
RedirectURL: "http://localhost:9096/callback",
Scopes: a.Scopes,
}

// Create a random state
state := generateRandomString(8)
codeVerifier := generateRandomString(64)

// Generate the authorization URL
authURL := config.AuthCodeURL(
state,
oauth2.SetAuthURLParam("code_challenge", createCodeChallenge(codeVerifier)),
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
)

// Start a temporary HTTP server to handle the callback
code := make(chan string)
defer close(code)
server := &http.Server{Addr: ":9096"}
// Start the server in a goroutine
go func() {
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("HTTP server error: %v", err)
}
}()

// Ensure the server is shut down when we're done
defer server.Shutdown(context.Background())

// Open the browser for the user to log in
err := openBrowser(authURL)
if err != nil {
log.Fatal("Could not open browser:", err)
}

http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("state") != state {
http.Error(w, "Invalid state", http.StatusBadRequest)
return
}

code <- r.URL.Query().Get("code")
fmt.Fprintf(w, "Authentication successful! You can close this window now.")
})

// Wait for the authorization code
authCode := <-code

// Exchange the authorization code for a token
token, err := config.Exchange(context.Background(), authCode)
func AuthToken() (string, error) {
tokenSource := GetDebrickedTokenSource()
token, err := tokenSource.Token()
if err != nil {
return "", err
}
return token.AccessToken, nil
}

func openBrowser(url string) error {
var cmd string
var args []string

switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
case "darwin":
cmd = "open"
default: // "linux", "freebsd", "openbsd", "netbsd"
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
Loading

0 comments on commit 6252ecd

Please sign in to comment.