~emersion/go-oauth2

2326e103f2072ce02e2795e30db0709d6a8e75da — Simon Ser 4 months ago 17dca10
Add support for client registration
3 files changed, 88 insertions(+), 20 deletions(-)

M client.go
A client_reg.go
M oauth2.go
M client.go => client.go +1 -1
@@ 110,7 110,7 @@ func (c *Client) doJSON(req *http.Request, data interface{}) error {
		return fmt.Errorf("oauth2: invalid token response MIME type %q", mediaType)
	}

	if resp.StatusCode != http.StatusOK {
	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		var errorData Error
		if err := json.NewDecoder(resp.Body).Decode(&errorData); err != nil {
			return err

A client_reg.go => client_reg.go +81 -0
@@ 0,0 1,81 @@
package oauth2

import (
	"bytes"
	"encoding/json"
	"net/http"
	"strings"
	"time"
)

// ClientMetadata contains registered client metadata defined in RFC 7591.
type ClientMetadata struct {
	RedirectURIs            []string       `json:"redirect_uris,omitempty"`
	TokenEndpointAuthMethod AuthMethod     `json:"token_endpoint_auth_method,omitempty"`
	GrantTypes              []GrantType    `json:"grant_types,omitempty"`
	ResponseTypes           []ResponseType `json:"response_types,omitempty"`
	ClientName              string         `json:"client_name,omitempty"`
	ClientURI               string         `json:"client_uri,omitempty"`
	LogoURI                 string         `json:"logo_uri,omitempty"`
	Scope                   []string       `json:"-"`
	Contacts                []string       `json:"contacts,omitempty"`
	TOSURI                  string         `json:"tos_uri,omitempty"`
	PolicyURI               string         `json:"policy_uri,omitempty"`
	JWKSURI                 string         `json:"jwks_uri,omitempty"`
	JWKS                    struct{}       `json:"jwks,omitempty"` // TODO
	SoftwareID              string         `json:"software_id,omitempty"`
	SoftwareVersion         string         `json:"software_version,omitempty"`
}

// RegisterOptions contains optional parameters for client registration.
type RegisterOptions struct {
	TokenResp *TokenResp
}

// RegisterResp contains data returned by the client registration endpoint.
type RegisterResp struct {
	ClientID              string    `json:"client_id"`
	ClientSecret          string    `json:"client_secret,omitempty"`
	ClientIDIssuedAt      time.Time `json:"-"`
	ClientSecretExpiresAt time.Time `json:"-"`
}

// Register registers a new OAuth client.
//
// See RFC 7591 section 3.
func (c *Client) Register(metadata *ClientMetadata, options *RegisterOptions) (*RegisterResp, error) {
	reqData := struct {
		*ClientMetadata
		ScopeStr string `json:"scope,omitempty"`
	}{
		ClientMetadata: metadata,
		ScopeStr:       strings.Join(metadata.Scope, " "),
	}
	var buf bytes.Buffer
	if err := json.NewEncoder(&buf).Encode(&reqData); err != nil {
		return nil, err
	}

	req, err := http.NewRequest(http.MethodPost, c.Server.RegistrationEndpoint, &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	if options != nil && options.TokenResp != nil {
		options.TokenResp.SetAuthHeader(req.Header)
	}

	var respData struct {
		RegisterResp
		ClientIDIssuedAtUnix      int64 `json:"client_id_issued_at,omitempty"`
		ClientSecretExpiresAtUnix int64 `json:"client_secret_expires_at"`
	}
	if err := c.doJSON(req, &respData); err != nil {
		return nil, err
	}
	if respData.ClientIDIssuedAtUnix != 0 {
		respData.ClientIDIssuedAt = time.Unix(respData.ClientIDIssuedAtUnix, 0)
	}
	respData.ClientSecretExpiresAt = time.Unix(respData.ClientSecretExpiresAtUnix, 0)
	return &respData.RegisterResp, nil
}

M oauth2.go => oauth2.go +6 -19
@@ 42,25 42,6 @@ type ServerMetadata struct {
	DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint,omitempty"`
}

// ClientMetadata contains registered client metadata defined in RFC 7591.
type ClientMetadata struct {
	RedirectURIs            []string       `json:"redirect_uris,omitempty"`
	TokenEndpointAuthMethod AuthMethod     `json:"token_endpoint_auth_method,omitempty"`
	GrantTypes              []GrantType    `json:"grant_types,omitempty"`
	ResponseTypes           []ResponseType `json:"response_types,omitempty"`
	ClientName              string         `json:"client_name,omitempty"`
	ClientURI               string         `json:"client_uri,omitempty"`
	LogoURI                 string         `json:"logo_uri,omitempty"`
	Scope                   []string       `json:"-"`
	Contacts                []string       `json:"contacts,omitempty"`
	TOSURI                  string         `json:"tos_uri,omitempty"`
	PolicyURI               string         `json:"policy_uri,omitempty"`
	JWKSURI                 string         `json:"jwks_uri,omitempty"`
	JWKS                    struct{}       `json:"jwks,omitempty"` // TODO
	SoftwareID              string         `json:"software_id,omitempty"`
	SoftwareVersion         string         `json:"software_version,omitempty"`
}

// ResponseType is the desired response for the authorization endpoint.
//
// See RFC 7591 section 2.


@@ 200,6 181,12 @@ const (
	ErrorCodeAuthorisationPending ErrorCode = "authorization_pending"
	ErrorCodeSlowDown             ErrorCode = "slow_down"
	ErrorCodeExpiredToken         ErrorCode = "expired_token"

	// RFC 7591 section 3.2.2
	ErrorCodeInvalidRedirectURI          ErrorCode = "invalid_redirect_uri"
	ErrorCodeInvalidClientMetadata       ErrorCode = "invalid_client_metadata"
	ErrorCodeInvalidSoftwareStatement    ErrorCode = "invalid_software_statement"
	ErrorCodeUnapprovedSoftwareStatement ErrorCode = "unapproved_software_statement"
)

// Error is an OAuth 2.0 error returned by the server.