Replace CSRF token with tokens based on the user's ID.
authorFlorian Forster <ff@octo.it>
Tue, 30 Jan 2018 19:42:25 +0000 (20:42 +0100)
committerFlorian Forster <ff@octo.it>
Tue, 30 Jan 2018 19:42:25 +0000 (20:42 +0100)
app/user.go
fitbit/fitbit.go
gfit/gfit.go
kraftakt.go

index 9fe7e5d..3f5b97e 100644 (file)
@@ -2,6 +2,9 @@ package app
 
 import (
        "context"
+       "crypto/hmac"
+       "crypto/sha1"
+       "encoding/hex"
        "fmt"
        "net/http"
        "sync"
@@ -116,6 +119,13 @@ func (u *User) String() string {
        return u.Email
 }
 
+func (u *User) Sign(payload string) string {
+       mac := hmac.New(sha1.New, []byte(u.ID))
+       mac.Write([]byte(payload))
+
+       return hex.EncodeToString(mac.Sum(nil))
+}
+
 type persistingTokenSource struct {
        ctx context.Context
        t   *oauth2.Token
index d7a885d..36242d1 100644 (file)
@@ -32,14 +32,8 @@ func oauthConfig() *oauth2.Config {
        }
 }
 
-const csrfToken = "@CSRFTOKEN@"
-
-func AuthURL() string {
-       return oauthConfig().AuthCodeURL(csrfToken, oauth2.AccessTypeOffline)
-}
-
 func ParseToken(ctx context.Context, r *http.Request, u *app.User) error {
-       if state := r.FormValue("state"); state != csrfToken {
+       if state := r.FormValue("state"); state != u.Sign("Fitbit") {
                return fmt.Errorf("invalid state parameter: %q", state)
        }
 
@@ -155,6 +149,10 @@ func NewClient(ctx context.Context, fitbitUserID string, u *app.User) (*Client,
        }, nil
 }
 
+func (c *Client) AuthURL(ctx context.Context) string {
+       return oauthConfig().AuthCodeURL(c.appUser.Sign("Fitbit"), oauth2.AccessTypeOffline)
+}
+
 func (c *Client) ActivitySummary(ctx context.Context, date string) (*ActivitySummary, error) {
        url := fmt.Sprintf("https://api.fitbit.com/1/user/%s/activities/date/%s.json",
                c.fitbitUserID, date)
index 610ee5c..858f94d 100644 (file)
@@ -19,8 +19,7 @@ import (
 )
 
 const (
-       csrfToken = "@CSRFTOKEN@"
-       userID    = "me"
+       userID = "me"
 
        dataTypeNameCalories        = "com.google.calories.expended"
        dataTypeNameDistance        = "com.google.distance.delta"
@@ -51,12 +50,8 @@ func Application(ctx context.Context) *fitness.Application {
        }
 }
 
-func AuthURL() string {
-       return oauthConfig().AuthCodeURL(csrfToken, oauth2.AccessTypeOffline)
-}
-
 func ParseToken(ctx context.Context, r *http.Request, u *app.User) error {
-       if state := r.FormValue("state"); state != csrfToken {
+       if state := r.FormValue("state"); state != u.Sign("Google") {
                return fmt.Errorf("invalid state parameter: %q", state)
        }
 
@@ -90,6 +85,10 @@ func NewClient(ctx context.Context, u *app.User) (*Client, error) {
        }, nil
 }
 
+func (c *Client) AuthURL(ctx context.Context) string {
+       return oauthConfig().AuthCodeURL(c.appUser.Sign("Google"), oauth2.AccessTypeOffline)
+}
+
 func (c *Client) DeleteToken(ctx context.Context) error {
        return c.appUser.DeleteToken(ctx, "Google")
 }
index 4768da8..e84b03a 100644 (file)
@@ -136,8 +136,13 @@ func loginHandler(_ context.Context, w http.ResponseWriter, r *http.Request, _ *
        return nil
 }
 
-func fitbitConnectHandler(_ context.Context, w http.ResponseWriter, r *http.Request, _ *app.User) error {
-       http.Redirect(w, r, fitbit.AuthURL(), http.StatusTemporaryRedirect)
+func fitbitConnectHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
+       c, err := fitbit.NewClient(ctx, "", u)
+       if err != nil {
+               return err
+       }
+
+       http.Redirect(w, r, c.AuthURL(ctx), http.StatusTemporaryRedirect)
        return nil
 }
 
@@ -196,8 +201,13 @@ func fitbitDisconnectHandler(ctx context.Context, w http.ResponseWriter, r *http
        return nil
 }
 
-func googleConnectHandler(_ context.Context, w http.ResponseWriter, r *http.Request, _ *app.User) error {
-       http.Redirect(w, r, gfit.AuthURL(), http.StatusTemporaryRedirect)
+func googleConnectHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, u *app.User) error {
+       c, err := gfit.NewClient(ctx, u)
+       if err != nil {
+               return err
+       }
+
+       http.Redirect(w, r, c.AuthURL(ctx), http.StatusTemporaryRedirect)
        return nil
 }