Skip to content

Commit

Permalink
Add state paremeter for try-login
Browse files Browse the repository at this point in the history
  • Loading branch information
jpadilla committed Feb 19, 2021
1 parent bc838d7 commit be3cc49
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 8 deletions.
8 changes: 5 additions & 3 deletions internal/auth/authutil/browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
// WaitForBrowserCallback lauches a new HTTP server listening on the provided
// address and waits for a request. Once received, the code is extracted from
// the query string (if any), and returned it to the caller.
func WaitForBrowserCallback(addr string) (string, error) {
func WaitForBrowserCallback(addr string) (code string, state string, err error) {
type callback struct {
code string
state string
err string
errDescription string
}
Expand All @@ -26,6 +27,7 @@ func WaitForBrowserCallback(addr string) (string, error) {
m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
cb := &callback{
code: r.URL.Query().Get("code"),
state: r.URL.Query().Get("state"),
err: r.URL.Query().Get("error"),
errDescription: r.URL.Query().Get("error_description"),
}
Expand Down Expand Up @@ -55,8 +57,8 @@ func WaitForBrowserCallback(addr string) (string, error) {
if cb.err != "" {
err = fmt.Errorf("%s: %s", cb.err, cb.errDescription)
}
return cb.code, err
return cb.code, cb.state, err
case err := <-errCh:
return "", err
return "", "", err
}
}
3 changes: 2 additions & 1 deletion internal/auth/authutil/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import (

// BuildLoginURL constructs a URL + query string that can be used to
// initiate a user-facing login-flow from the CLI.
func BuildLoginURL(domain, clientID, callbackURL, connectionName, audience, prompt string, scopes []string) (string, error) {
func BuildLoginURL(domain, clientID, callbackURL, state, connectionName, audience, prompt string, scopes []string) (string, error) {
var path string = "/authorize"

q := url.Values{}
q.Add("client_id", clientID)
q.Add("response_type", "code")
q.Add("redirect_uri", callbackURL)
q.Add("state", state)

if prompt != "" {
q.Add("prompt", prompt)
Expand Down
33 changes: 29 additions & 4 deletions internal/cli/utils_shared.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package cli

import (
"crypto/rand"
"fmt"

"encoding/base64"
"encoding/json"
"net/http"
"net/url"

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/auth/authutil"
"github.com/auth0/auth0-cli/internal/auth0"
"github.com/auth0/auth0-cli/internal/open"
"github.com/auth0/auth0-cli/internal/prompt"
"gopkg.in/auth0.v5/management"
"net/http"
"net/url"
)

const (
Expand All @@ -20,6 +23,7 @@ const (
cliLoginTestingCallbackAddr string = "localhost:8484"
cliLoginTestingCallbackURL string = "http://localhost:8484"
cliLoginTestingInitiateLoginURI string = "https://cli.auth0.com"
cliLoginTestingStateSize int = 64
)

var (
Expand Down Expand Up @@ -111,8 +115,13 @@ func runLoginFlow(cli *cli, t tenant, c *management.Client, connName, audience,
return err
}

state, err := generateState(cliLoginTestingStateSize)
if err != nil {
return err
}

// Build a login URL and initiate login in a browser window.
loginURL, err := authutil.BuildLoginURL(t.Domain, c.GetClientID(), cliLoginTestingCallbackURL, connName, audience, prompt, scopes)
loginURL, err := authutil.BuildLoginURL(t.Domain, c.GetClientID(), cliLoginTestingCallbackURL, state, connName, audience, prompt, scopes)
if err != nil {
return err
}
Expand All @@ -123,11 +132,15 @@ func runLoginFlow(cli *cli, t tenant, c *management.Client, connName, audience,

// launch a HTTP server to wait for the callback to capture the auth
// code.
authCode, err := authutil.WaitForBrowserCallback(cliLoginTestingCallbackAddr)
authCode, authState, err := authutil.WaitForBrowserCallback(cliLoginTestingCallbackAddr)
if err != nil {
return err
}

if state != authState {
return fmt.Errorf("unexpected auth state")
}

// once the callback is received, exchange the code for an access
// token.
tokenResponse, err = authutil.ExchangeCodeForToken(
Expand Down Expand Up @@ -235,3 +248,15 @@ func removeLocalCallbackURLFromClient(clientManager auth0.ClientAPI, client *man
return clientManager.Update(client.GetClientID(), updatedClient)

}

// generate state parameter value used to mitigate CSRF attacks
// more: https://auth0.com/docs/protocols/state-parameters
func generateState(size int) (string, error) {
b := make([]byte, size)
_, err := rand.Read(b)
if err != nil {
return "", err
}

return base64.URLEncoding.EncodeToString(b), nil
}

0 comments on commit be3cc49

Please sign in to comment.