Add OAuth 2.0 authentication support
authorMikael Berthe <mikael@lilotux.net>
Wed, 10 May 2017 13:07:00 +0200
changeset 178 b63095e0f301
parent 177 4fb7376fa19a
child 179 fbe21b4aabda
Add OAuth 2.0 authentication support
.travis.yml
login.go
--- a/.travis.yml	Wed May 10 11:06:22 2017 +0200
+++ b/.travis.yml	Wed May 10 13:07:00 2017 +0200
@@ -11,6 +11,7 @@
   only:
   - master
 install:
+- go get golang.org/x/oauth2
 - go get github.com/pkg/errors
 - go get github.com/stretchr/testify/assert
 - go get github.com/sendgrid/rest
--- a/login.go	Wed May 10 11:06:22 2017 +0200
+++ b/login.go	Wed May 10 13:07:00 2017 +0200
@@ -10,10 +10,14 @@
 	"encoding/json"
 	"strings"
 
+	"golang.org/x/oauth2"
+
 	"github.com/pkg/errors"
 	"github.com/sendgrid/rest"
 )
 
+const oAuthRelPath = "/oauth/"
+
 // UserToken represents a user token as returned by the Mastodon API
 type UserToken struct {
 	AccessToken string `json:"access_token"`
@@ -50,7 +54,7 @@
 	}
 
 	req := rest.Request{
-		BaseURL:     mc.InstanceURL + "/oauth/token",
+		BaseURL:     mc.InstanceURL + oAuthRelPath + "token",
 		Headers:     hdrs,
 		QueryParams: opts,
 		Method:      rest.Post,
@@ -86,3 +90,39 @@
 	}
 	return nil
 }
+
+// LoginOAuth2 handles OAuth2 authentication
+// If code is empty, the URL to the server consent page will be returned;
+// if not, the user token is set.
+func (mc *Client) LoginOAuth2(code string, scopes []string) (string, error) {
+	if mc == nil {
+		return "", ErrUninitializedClient
+	}
+
+	conf := &oauth2.Config{
+		ClientID:     mc.ID,
+		ClientSecret: mc.Secret,
+		Scopes:       scopes,
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  mc.InstanceURL + oAuthRelPath + "authorize",
+			TokenURL: mc.InstanceURL + oAuthRelPath + "token",
+		},
+		RedirectURL: NoRedirect,
+	}
+
+	if code == "" {
+		// URL to consent page to ask for permission
+		// for the scopes specified above.
+		return conf.AuthCodeURL("state", oauth2.AccessTypeOffline), nil
+	}
+
+	// Return token
+	t, err := conf.Exchange(nil, code)
+	if err != nil {
+		return "", errors.Wrap(err, "cannot convert code into a token")
+	}
+	if t == nil || t.AccessToken == "" {
+		return "", errors.New("empty token")
+	}
+	return "", mc.SetUserToken(t.AccessToken, "", "", scopes)
+}