Skip to content

Commit

Permalink
added ecobee.New(), Options and AuthCache.
Browse files Browse the repository at this point in the history
Deprecated ecobee.NewClient() in favor of New().

All client creation options are now passed through ecobee.Options.

AuthCache allows persisting OAuth2 tokens in storage other than files,
for example etcd or a database and allows passing custom context.
  • Loading branch information
jkowalski committed Dec 1, 2018
1 parent 2359387 commit 1c513f0
Showing 1 changed file with 76 additions and 17 deletions.
93 changes: 76 additions & 17 deletions ecobee/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package ecobee
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
Expand All @@ -31,17 +32,45 @@ import (
var Scopes = []string{"smartRead", "smartWrite"}

type tokenSource struct {
token oauth2.Token
cacheFile, clientID string
token oauth2.Token
apiKey string
authCache AuthCache
}

func TokenSource(clientID, cacheFile string) oauth2.TokenSource {
return oauth2.ReuseTokenSource(nil, newTokenSource(clientID, cacheFile))
// AuthCache encapsulates persistent cache for OAuth2 token.
type AuthCache interface {
LoadTokenData() ([]byte, error)
SaveTokenData(data []byte) error
}

func newTokenSource(clientID, cacheFile string) *tokenSource {
file, err := ioutil.ReadFile(cacheFile)
ets := tokenSource{clientID: clientID, cacheFile: cacheFile}
type fileAuthCache struct {
fileName string
}

func (p fileAuthCache) LoadTokenData() ([]byte, error) {
return ioutil.ReadFile(p.fileName)
}

func (p fileAuthCache) SaveTokenData(data []byte) error {
return ioutil.WriteFile(p.fileName, data, 0777)
}

// TokenCacheFile returns AuthCache that persists the OAuth2 token in the specified file.
func TokenCacheFile(fileName string) AuthCache {
return fileAuthCache{fileName}
}

func TokenSource(apiKey, cacheFile string) oauth2.TokenSource {
return TokenSourceWithAuthCache(apiKey, fileAuthCache{cacheFile})
}

func TokenSourceWithAuthCache(apiKey string, authCache AuthCache) oauth2.TokenSource {
return oauth2.ReuseTokenSource(nil, newTokenSource(apiKey, authCache))
}

func newTokenSource(apiKey string, authCache AuthCache) *tokenSource {
file, err := authCache.LoadTokenData()
ets := tokenSource{apiKey: apiKey, authCache: authCache}
if err != nil {
// no file, corrupted, or other problem: just start with an
// empty token.
Expand All @@ -56,15 +85,14 @@ func (ts *tokenSource) save() error {
if err != nil {
return err
}
err = ioutil.WriteFile(ts.cacheFile, d, 0777)
return err
return ts.authCache.SaveTokenData(d)
}

func (ts *tokenSource) firstAuth() error {

uv := url.Values{
"response_type": {"ecobeePin"},
"client_id": {ts.clientID},
"client_id": {ts.apiKey},
"scope": {strings.Join(Scopes, ",")},
}
u := url.URL{
Expand Down Expand Up @@ -128,14 +156,14 @@ func (tr *tokenResponse) Token() oauth2.Token {
func (ts *tokenSource) accessToken(code string) error {
return ts.getToken(url.Values{
"grant_type": {"ecobeePin"},
"client_id": {ts.clientID},
"client_id": {ts.apiKey},
"code": {code},
})
}
func (ts *tokenSource) refreshToken() error {
return ts.getToken(url.Values{
"grant_type": {"refresh_token"},
"client_id": {ts.clientID},
"client_id": {ts.apiKey},
"refresh_token": {ts.token.RefreshToken},
})
}
Expand Down Expand Up @@ -200,12 +228,43 @@ type Client struct {
*http.Client
}

// NewClient creates a Ecobee API client for the specific clientID
// NewClient creates a Ecobee API client for the specific apiKey
// (Application Key). Use the Ecobee Developer Portal to create the
// Application Key.
// (https://www.ecobee.com/consumerportal/index.html#/dev)
func NewClient(clientID, authCache string) *Client {
return &Client{oauth2.NewClient(
context.Background(),
TokenSource(clientID, authCache))}
// This function is deprecated - use New() instead.
func NewClient(apiKey, authCache string) *Client {
c, err := New(context.TODO(), Options{
APIKey: apiKey,
AuthCache: fileAuthCache{authCache},
})

if err != nil {
// can't fail here.
panic("unexpected usage: " + err.Error())
}

return c
}

// Options specifies EcoBee client parameters.
type Options struct {
APIKey string // API Key created in Ecobee Developer Portal
AuthCache AuthCache // token cache, typically TokenCacheFile(fileName)
}

// New creates a Ecobee API client for the specific options
// Use the Ecobee Developer Portal to create the Application Key.
// (https://www.ecobee.com/consumerportal/index.html#/dev)
func New(ctx context.Context, opt Options) (*Client, error) {
if opt.APIKey == "" {
return nil, errors.New("API key is required")
}
if opt.AuthCache == nil {
return nil, fmt.Errorf("auth cache is required")
}

ts := TokenSourceWithAuthCache(opt.APIKey, opt.AuthCache)
cli := oauth2.NewClient(ctx, ts)
return &Client{cli}, nil
}

0 comments on commit 1c513f0

Please sign in to comment.