vendor/github.com/McKael/madon/v2/login.go
changeset 265 05c40b36d3b2
parent 264 8f478162d991
child 266 80973a656b81
equal deleted inserted replaced
264:8f478162d991 265:05c40b36d3b2
     1 /*
       
     2 Copyright 2017-2018 Mikael Berthe
       
     3 
       
     4 Licensed under the MIT license.  Please see the LICENSE file is this directory.
       
     5 */
       
     6 
       
     7 package madon
       
     8 
       
     9 import (
       
    10 	"encoding/json"
       
    11 	"strings"
       
    12 
       
    13 	"golang.org/x/net/context"
       
    14 	"golang.org/x/oauth2"
       
    15 
       
    16 	"github.com/pkg/errors"
       
    17 	"github.com/sendgrid/rest"
       
    18 )
       
    19 
       
    20 const oAuthRelPath = "/oauth/"
       
    21 
       
    22 // UserToken represents a user token as returned by the Mastodon API
       
    23 type UserToken struct {
       
    24 	AccessToken string `json:"access_token"`
       
    25 	CreatedAt   int64  `json:"created_at"`
       
    26 	Scope       string `json:"scope"`
       
    27 	TokenType   string `json:"token_type"`
       
    28 }
       
    29 
       
    30 // LoginBasic does basic user authentication
       
    31 func (mc *Client) LoginBasic(username, password string, scopes []string) error {
       
    32 	if mc == nil {
       
    33 		return ErrUninitializedClient
       
    34 	}
       
    35 
       
    36 	if username == "" {
       
    37 		return errors.New("missing username")
       
    38 	}
       
    39 	if password == "" {
       
    40 		return errors.New("missing password")
       
    41 	}
       
    42 
       
    43 	hdrs := make(map[string]string)
       
    44 	opts := make(map[string]string)
       
    45 
       
    46 	hdrs["User-Agent"] = "madon/" + MadonVersion
       
    47 
       
    48 	opts["grant_type"] = "password"
       
    49 	opts["client_id"] = mc.ID
       
    50 	opts["client_secret"] = mc.Secret
       
    51 	opts["username"] = username
       
    52 	opts["password"] = password
       
    53 	if len(scopes) > 0 {
       
    54 		opts["scope"] = strings.Join(scopes, " ")
       
    55 	}
       
    56 
       
    57 	req := rest.Request{
       
    58 		BaseURL:     mc.InstanceURL + oAuthRelPath + "token",
       
    59 		Headers:     hdrs,
       
    60 		QueryParams: opts,
       
    61 		Method:      rest.Post,
       
    62 	}
       
    63 
       
    64 	r, err := restAPI(req)
       
    65 	if err != nil {
       
    66 		return err
       
    67 	}
       
    68 
       
    69 	var resp UserToken
       
    70 
       
    71 	err = json.Unmarshal([]byte(r.Body), &resp)
       
    72 	if err != nil {
       
    73 		return errors.Wrap(err, "cannot unmarshal server response")
       
    74 	}
       
    75 
       
    76 	mc.UserToken = &resp
       
    77 	return nil
       
    78 }
       
    79 
       
    80 // SetUserToken sets an existing user credentials
       
    81 // No verification of the arguments is made.
       
    82 func (mc *Client) SetUserToken(token, username, password string, scopes []string) error {
       
    83 	if mc == nil {
       
    84 		return ErrUninitializedClient
       
    85 	}
       
    86 
       
    87 	mc.UserToken = &UserToken{
       
    88 		AccessToken: token,
       
    89 		Scope:       strings.Join(scopes, " "),
       
    90 		TokenType:   "bearer",
       
    91 	}
       
    92 	return nil
       
    93 }
       
    94 
       
    95 // LoginOAuth2 handles OAuth2 authentication
       
    96 // If code is empty, the URL to the server consent page will be returned;
       
    97 // if not, the user token is set.
       
    98 func (mc *Client) LoginOAuth2(code string, scopes []string) (string, error) {
       
    99 	if mc == nil {
       
   100 		return "", ErrUninitializedClient
       
   101 	}
       
   102 
       
   103 	conf := &oauth2.Config{
       
   104 		ClientID:     mc.ID,
       
   105 		ClientSecret: mc.Secret,
       
   106 		Scopes:       scopes,
       
   107 		Endpoint: oauth2.Endpoint{
       
   108 			AuthURL:  mc.InstanceURL + oAuthRelPath + "authorize",
       
   109 			TokenURL: mc.InstanceURL + oAuthRelPath + "token",
       
   110 		},
       
   111 		RedirectURL: NoRedirect,
       
   112 	}
       
   113 
       
   114 	if code == "" {
       
   115 		// URL to consent page to ask for permission
       
   116 		// for the scopes specified above.
       
   117 		return conf.AuthCodeURL("state", oauth2.AccessTypeOffline), nil
       
   118 	}
       
   119 
       
   120 	// Return token
       
   121 	t, err := conf.Exchange(context.TODO(), code)
       
   122 	if err != nil {
       
   123 		return "", errors.Wrap(err, "cannot convert code into a token")
       
   124 	}
       
   125 	if t == nil || t.AccessToken == "" {
       
   126 		return "", errors.New("empty token")
       
   127 	}
       
   128 	return "", mc.SetUserToken(t.AccessToken, "", "", scopes)
       
   129 }