+package gfit
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "github.com/octo/gfitsync/app"
+ "golang.org/x/oauth2"
+ oauth2google "golang.org/x/oauth2/google"
+ fitness "google.golang.org/api/fitness/v1"
+)
+
+var oauthConfig = &oauth2.Config{
+ ClientID: "@GOOGLE_CLIENT_ID@",
+ ClientSecret: "@GOOGLE_CLIENT_SECRET@",
+ Endpoint: oauth2google.Endpoint,
+ RedirectURL: "https://fitbit-gfit-sync.appspot.com/google/grant",
+ Scopes: []string{
+ fitness.FitnessActivityWriteScope,
+ fitness.FitnessBodyWriteScope,
+ },
+}
+
+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 {
+ return fmt.Errorf("invalid state parameter: %q", state)
+ }
+
+ tok, err := oauthConfig.Exchange(ctx, r.FormValue("code"))
+ if err != nil {
+ return err
+ }
+
+ return u.SetToken(ctx, "Google", tok)
+}
+
+type Client struct {
+ *fitness.Service
+}
+
+func NewClient(ctx context.Context, u *app.User) (*Client, error) {
+ c, err := u.OAuthClient(ctx, "Google", oauthConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ service, err := fitness.New(c)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Client{
+ Service: service,
+ }, nil
+}