Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions go/rtl/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/url"
"os"
"reflect"
"sync"
"time"

"golang.org/x/oauth2"
Expand Down Expand Up @@ -39,6 +40,47 @@ func (t *transportWithHeaders) RoundTrip(req *http.Request) (*http.Response, err
type AuthSession struct {
Config ApiSettings
Client http.Client
token *oauth2.Token
source oauth2.TokenSource
mu sync.RWMutex
}

const tokenLeeway = 10 * time.Second

func (s *AuthSession) IsActive() bool {
s.mu.RLock()
defer s.mu.RUnlock()
if s.token == nil {
return false
}
return s.token.Expiry.After(time.Now().Add(tokenLeeway))
}

func (s *AuthSession) Login() (*oauth2.Token, error) {
// First, check with a read lock to avoid contention if the token is valid.
s.mu.RLock()
if s.token != nil && s.token.Expiry.After(time.Now().Add(tokenLeeway)) {
token := s.token
s.mu.RUnlock()
return token, nil
}
s.mu.RUnlock()

// If the token is invalid or nil, acquire a write lock to refresh it.
s.mu.Lock()
defer s.mu.Unlock()

// Re-check after obtaining the write lock, in case another goroutine refreshed it.
if s.token != nil && s.token.Expiry.After(time.Now().Add(tokenLeeway)) {
return s.token, nil
}

token, err := s.source.Token()
if err != nil {
return nil, err
}
s.token = token
return token, nil
}

func NewAuthSession(config ApiSettings) *AuthSession {
Expand Down Expand Up @@ -74,21 +116,27 @@ func NewAuthSessionWithTransport(config ApiSettings, transport http.RoundTripper
&http.Client{Transport: appIdHeaderTransport},
)

source := oauthConfig.TokenSource(ctx)

// Make use of oauth2 transport to handle token management
oauthTransport := &oauth2.Transport{
Source: oauthConfig.TokenSource(ctx),
Source: source,
// Will set "x-looker-appid" Header on all other requests
Base: appIdHeaderTransport,
}

return &AuthSession{
Config: config,
Client: http.Client{Transport: oauthTransport},
source: source,
}
}

func (s *AuthSession) Do(result interface{}, method, ver, path string, reqPars map[string]interface{}, body interface{}, options *ApiSettings) error {

_, err := s.Login()
if err != nil {
return err
}
// prepare URL
u := fmt.Sprintf("%s/api%s%s", s.Config.BaseUrl, ver, path)

Expand Down
Loading