You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-login/login/internal/oauth2/config.go

149 lines
3.5 KiB
Go

// 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
}