Skip to content

Commit

Permalink
chore: restructure shared login components (#79)
Browse files Browse the repository at this point in the history
* chore: restructure shared login components

* fix: use pointer to http.Server
  • Loading branch information
Patrick Carey authored Jan 27, 2021
1 parent c283847 commit 2a03272
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 291 deletions.
62 changes: 62 additions & 0 deletions internal/auth/authutil/browser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package authutil

import (
"context"
"fmt"
"net/http"
"time"
)

// 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) {
type callback struct {
code string
err string
errDescription string
}

cbCh := make(chan *callback)
errCh := make(chan error)

m := http.NewServeMux()
s := &http.Server{Addr: addr, Handler: m}

m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
cb := &callback{
code: r.URL.Query().Get("code"),
err: r.URL.Query().Get("error"),
errDescription: r.URL.Query().Get("error_description"),
}

if cb.code == "" {
_, _ = w.Write([]byte("<p>&#10060; Unable to extract code from request, please try authenticating again.</p>"))
} else {
_, _ = w.Write([]byte("<p>&#128075; You can close the window and go back to the CLI to see the user info and tokens.</p>"))
}

cbCh <- cb
})

go func() {
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
errCh <- err
}
}()

select {
case cb := <-cbCh:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
defer func(c context.Context) { _ = s.Shutdown(ctx) }(ctx)

var err error
if cb.err != "" {
err = fmt.Errorf("%s: %s", cb.err, cb.errDescription)
}
return cb.code, err
case err := <-errCh:
return "", err
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package auth
package authutil

import (
"encoding/json"
Expand Down
42 changes: 42 additions & 0 deletions internal/auth/authutil/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package authutil

import (
"net/url"
"strings"
)

// 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) {
var path string = "/authorize"

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

if prompt != "" {
q.Add("prompt", prompt)
}

if connectionName != "" {
q.Add("connection", connectionName)
}

if audience != "" {
q.Add("audience", audience)
}

if len(scopes) > 0 {
q.Add("scope", strings.Join(scopes, " "))
}

u := &url.URL{
Scheme: "https",
Host: domain,
Path: path,
RawQuery: q.Encode(),
}

return u.String(), nil
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package auth
package authutil

import (
"encoding/json"
Expand Down
4 changes: 1 addition & 3 deletions internal/cli/get_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ Fetch an access token for the given client and API.
// initiate the client credentials flow instead to fetch a token,
// avoiding the browser and HTTP server shenanigans altogether.

abort, needsLocalCallbackURL := runLoginFlowPreflightChecks(cli, client)
if abort {
if proceed := runLoginFlowPreflightChecks(cli, client); !proceed {
return nil
}

Expand All @@ -54,7 +53,6 @@ Fetch an access token for the given client and API.
tenant,
client,
"", // specifying a connection is only supported for try-login
needsLocalCallbackURL,
audience,
"", // We don't want to force a prompt for get-token
scopes,
Expand Down
Loading

0 comments on commit 2a03272

Please sign in to comment.