From 1b0eaa82adf8853641ac460743020f83e889a4a3 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Tue, 30 Jan 2018 20:42:25 +0100 Subject: [PATCH] Replace CSRF token with tokens based on the user's ID. --- app/user.go | 10 ++++++++++ fitbit/fitbit.go | 12 +++++------- gfit/gfit.go | 13 ++++++------- kraftakt.go | 18 ++++++++++++++---- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/app/user.go b/app/user.go index 9fe7e5d..3f5b97e 100644 --- a/app/user.go +++ b/app/user.go @@ -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 diff --git a/fitbit/fitbit.go b/fitbit/fitbit.go index d7a885d..36242d1 100644 --- a/fitbit/fitbit.go +++ b/fitbit/fitbit.go @@ -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) diff --git a/gfit/gfit.go b/gfit/gfit.go index 610ee5c..858f94d 100644 --- a/gfit/gfit.go +++ b/gfit/gfit.go @@ -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") } diff --git a/kraftakt.go b/kraftakt.go index 4768da8..e84b03a 100644 --- a/kraftakt.go +++ b/kraftakt.go @@ -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 } -- 2.11.0