fitbit

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 25, 2023 License: MPL-2.0 Imports: 16 Imported by: 0

README

Fitbit Web API - Go client

This package allows you to create a "server" application that interacts with the Fitbit API in Go.

Usage

The prerequisite is to follow the Getting Started official guide.

Once you have created a Fitbit Developer account you have to register a new application, of server type. Then, there are 2 required steps:

  1. Server application Authorization flow.

    galeone/fitbit gives you the basic functionalities for implementing the Authorization Code Grant Flow with PKCE. You need to be familiar with some Web Framework for implementing what's described in the Server application Authorization flow.)

  2. Client usage.

    Once the user granted the permissions to your application, you can use the authorized client for querying the Fitbit API. NOTE: only the GET queries are supported right now. It means that you can fetch everything (in a very convenient format, using annotated Go Structs), but you can't do POST/PUT/DELETE operations.

Server application Authorization flow

You also need to be familiar with some Web Framework (not shown).

  1. Create a type that implements the fitbit.Storage interface. You can see an implementation based on PostgreSQL, through the package galeone/igor here: galeone/fitbit-pgdb.
  2. Create a fitbit.Authorizer object
  3. Create the endpoint for the authorization flow. The content should look like
     fitbitAuthorizer := fitbit.NewAuthorizer(_db, _clientID, _clientSecret, _redirectURL)
    
     authorizing := types.AuthorizingUser{
         CSRFToken: uuid.New().String(),
         // Code verifier for PKCE
         // https://dev.fitbit.com/build/reference/web-api/developer-guide/authorization/#Authorization-Code-Grant-Flow-with-PKCE
         Code: fmt.Sprintf("%s-%s", uuid.New().String(), uuid.New().String()),
     }
    
     fitbitAuthorizer.SetAuthorizing(&authorizing)
    
     // Potentially set cookie for identifying the authorizing user
     c.SetCookie(&http.Cookie{
         Name: "authorizing",
         Value: fitbitAuthorizer.CSRFToken().String(),
         // No Expires = Session cookie
         HttpOnly: true,
     })
    
     if err = _db.InsertAuhorizingUser(&authorizing); err != nil {
         return err
     }
    
     var auth_url *url.URL
     if auth_url, err = fitbitAuthorizer.AuthorizationURL(); err != nil {
         return err
     }
    
     c.Redirect(http.StatusTemporaryRedirect, auth_url.String())
    
  4. Create the endpoint for the Redirect URI. The content should look like
     state := c.QueryParam("state")
     if state != fitbitAuthorizer.CSRFToken().String() {
         return c.Redirect(http.StatusTemporaryRedirect, "/error?status=csrf")
     }
    
     code := c.QueryParam("code")
     var token *types.AuthorizedUser
     var err error
     if token, err = fitbitAuthorizer.ExchangeAuthorizationCode(code); err != nil {
         return c.Redirect(http.StatusTemporaryRedirect, "/error?status=exchange")
     }
     // Update the fitbitclient. Now it contains a valid token and HTTP can be used to query the API
     fitbitAuthorizer.SetToken(token)
    
     // Save token and redirect user to the application
     if err = _db.UpsertAuthorizedUser(token); err != nil {
         return err
     }
     // Cookie used to identify the user that authorized the application
     cookie := http.Cookie{
         Name:     "token",
         Value:    token.AccessToken,
         Domain:   _domain,
         Expires:  time.Now().Add(time.Second * time.Duration(token.ExpiresIn)),
         HttpOnly: true,
     }
     c.SetCookie(&cookie)
     // Redirect the user to your application endpoint
     c.Redirect(http.StatusTemporaryRedirect, "/app")
    

That's all.

Client usage

After the user authorized the application, you can re-create the fitbit.Authorizer fetching the data from the database (using your Storage implementation) and create the authorized fitbit.Client.

fitbitAuthorizer := fitbit.NewClient(_db, _clientID, _clientSecret, _redirectURL)

// Auhtorization token (after exhange)
cookie, err = c.Cookie("token")

var dbToken *types.AuthorizedUser
if dbToken, err = _db.AuthorizedUser(cookie.Value); err != nil {
    return err
}

// Set the valid token
fitbitAuthorizer.SetToken(dbToken)

// Create the client
var fb *client.Client
if fb, err = client.NewClient(fitbitAuthorizer); err != nil {
    return err
}

// Use it!

var logs *types.ActivityLogList
if logs, err = fb.UserActivityLogList(&types.Pagination{
    Offset:     0,
    BeforeDate: types.FitbitDateTime{Time: time.Now()},
    Limit:      10,
    Sort:       "desc",
}); err != nil {
    return
}

for _, activity := range logs.Activities {
    if activity.TcxLink != "" {
        var tcxDB *tcx.TCXDB
        if tcxDB, err = fb.UserActivityTCX(activity.LogID); err != nil {
            return
        }
        // So something with the tcxDB
    }
   // Do something with the activity
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Authorizer

type Authorizer struct {
	// contains filtered or unexported fields
}

Authorizer is the structure that manages the OAuth2 authorization process. It depends on a `Storage` where the tokens are stored during the various exchanges.

func NewAuthorizer

func NewAuthorizer(db Storage, clientID, clientSecret, redirectURL string) *Authorizer

NewAuthorizer creates a new Authorizer. The created client is already configured for requesting the correct scopes and make authenticathed/authorized requests to the fitbit API. The db parameter must be a valid implementation of the Storage interface.

func (*Authorizer) AuthorizationURL

func (c *Authorizer) AuthorizationURL() (*url.URL, error)

AuthorizationURL returns the URL where to redirect the user where it will be asked for giving the permissions for the various scopes

func (*Authorizer) CSRFToken

func (c *Authorizer) CSRFToken() *uuid.UUID

CSRFToken returns the CSRF code associated with this session

func (*Authorizer) ExchangeAuthorizationCode

func (c *Authorizer) ExchangeAuthorizationCode(code string) (token *types.AuthorizedUser, err error)

ExchangeAuthorizationCode exchanges the authorization code for the access and refresh tokens. In a Server Application Type, this request should be authenticated https://dev.fitbit.com/build/reference/web-api/developer-guide/authorization/#Authorization-Code-Grant-Flow-with-PKCE See step 4

This method also saves the exchanged token inside the *Authorizer structure. This token is later used for creating the HTTP client (see HTTP method).

func (*Authorizer) HTTP

func (c *Authorizer) HTTP() (*http.Client, error)

Return an HTTP client that uses the specified token for authenticating It handles all the refresh-token stuff, and it updates inside the db The values for the user that's this *Authorizer

func (*Authorizer) SetAuthorizing

func (c *Authorizer) SetAuthorizing(auth *types.AuthorizingUser)

SetAuthorizing sets the parameters required during the autorization process

func (*Authorizer) SetToken

func (c *Authorizer) SetToken(token *types.AuthorizedUser)

SetToken sets the token inside the Authorizer. From the types.AuthorizedUser to the oauth2.Token representation (privately used).

func (*Authorizer) UserID

func (c *Authorizer) UserID() (*string, error)

UserID returns the ID of the users that authorized this client Returns an error if the fitbitClient is not authorized

type Storage

type Storage interface {
	// InsertAuhorizingUser creates a new AuthorizingUser.
	// An AuthorizingUser is an user in the process of giving the
	// authorization to the fitbit-API-based application.
	InsertAuhorizingUser(*types.AuthorizingUser) error

	// UpsertAuthorizedUser creates or updates an AuthorizedUser.
	// It creates the user if it's not present in the storage. It updates its
	// attributes if the very same user is already present in the storage.
	// An AuthorizedUser is an user that completed the Authorization phase
	// that means it transitioned from the state of AuthorizingUser to the
	// state of AuthorizedUser.
	UpsertAuthorizedUser(*types.AuthorizedUser) error

	// AuthorizedUser returns a pointer to types.AuthorizedUser
	// given the accessToken.
	AuthorizedUser(accessToken string) (*types.AuthorizedUser, error)

	// AuthorizingUser returns a pointer to the types.AuthorizingUser
	// given it's unique identifier (any ID - in DB implementation often
	// a primary key).
	AuthorizingUser(id string) (*types.AuthorizingUser, error)
}

Storage is the interface to implement for implementing the persistence layer required by the fitbit client.

Directories

Path Synopsis
Package api contains the implementation of the REST [Fitbit Web API][1].
Package api contains the implementation of the REST [Fitbit Web API][1].
Package types contains all the types that represent a response given by the Fitbit API.
Package types contains all the types that represent a response given by the Fitbit API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL