// Copyright 2017 Drone.IO Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package oauth2 import ( "encoding/json" "net/http" "net/url" "strings" "git.awesome-for.me/liuzhiguo/go-login/login/logger" ) // token stores the authorization credentials used to // access protected resources. type token struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` RefreshToken string `json:"refresh_token"` Expires int64 `json:"expires_in"` } // Config stores the application configuration. type Config struct { // HTTP client used to communicate with the authorization // server. If nil, DefaultClient is used. Client *http.Client // ClientID is the identifier issued to the application // during the registration process. ClientID string // ClientSecret is the secret issued to the application // during the registration process. ClientSecret string // Scope is the scope of the access request. Scope []string // RedirectURL is used by the authorization server to // return the authorization credentials to the client. RedirectURL string // AccessTokenURL is used by the client to exchange an // authorization grant for an access token. AccessTokenURL string // AuthorizationURL is used by the client to obtain // authorization from the resource owner. AuthorizationURL string // BasicAuthOff instructs the client to disable use of // the authorization header and provide the client_id // and client_secret in the formdata. BasicAuthOff bool // Logger is used to log errors. If nil the provider // use the default noop logger. Logger logger.Logger // Dumper is used to dump the http.Request and // http.Response for debug purposes. Dumper logger.Dumper } // authorizeRedirect returns a client authorization // redirect endpoint. func (c *Config) authorizeRedirect(state string) string { v := url.Values{ "response_type": {"code"}, "client_id": {c.ClientID}, } if len(c.Scope) != 0 { v.Set("scope", strings.Join(c.Scope, " ")) } if len(state) != 0 { v.Set("state", state) } if len(c.RedirectURL) != 0 { v.Set("redirect_uri", c.RedirectURL) } u, _ := url.Parse(c.AuthorizationURL) u.RawQuery = v.Encode() return u.String() } // exchange converts an authorization code into a token. func (c *Config) exchange(code, state string) (*token, error) { v := url.Values{ "grant_type": {"authorization_code"}, "code": {code}, } if c.BasicAuthOff { v.Set("client_id", c.ClientID) v.Set("client_secret", c.ClientSecret) } if len(state) != 0 { v.Set("state", state) } if len(c.RedirectURL) != 0 { v.Set("redirect_uri", c.RedirectURL) } req, err := http.NewRequest("POST", c.AccessTokenURL, strings.NewReader(v.Encode())) if err != nil { return nil, err } req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/x-www-form-urlencoded") if !c.BasicAuthOff { req.SetBasicAuth(c.ClientID, c.ClientSecret) } if c.Dumper != nil { c.Dumper.DumpRequest(req) } res, err := c.client().Do(req) if err != nil { return nil, err } defer res.Body.Close() if c.Dumper != nil { c.Dumper.DumpResponse(res) } if res.StatusCode > 299 { err := new(Error) json.NewDecoder(res.Body).Decode(err) return nil, err } token := &token{} err = json.NewDecoder(res.Body).Decode(token) return token, err } func (c *Config) client() *http.Client { client := c.Client if client == nil { client = http.DefaultClient } return client }