*: connectors use a different identity object than storage
This commit is contained in:
		| @@ -1,11 +1,7 @@ | |||||||
| // Package connector defines interfaces for federated identity strategies. | // Package connector defines interfaces for federated identity strategies. | ||||||
| package connector | package connector | ||||||
|  |  | ||||||
| import ( | import "net/http" | ||||||
| 	"net/http" |  | ||||||
|  |  | ||||||
| 	"github.com/coreos/poke/storage" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Connector is a mechanism for federating login to a remote identity service. | // Connector is a mechanism for federating login to a remote identity service. | ||||||
| // | // | ||||||
| @@ -15,18 +11,32 @@ type Connector interface { | |||||||
| 	Close() error | 	Close() error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Identity represents the ID Token claims supported by the server. | ||||||
|  | type Identity struct { | ||||||
|  | 	UserID        string | ||||||
|  | 	Username      string | ||||||
|  | 	Email         string | ||||||
|  | 	EmailVerified bool | ||||||
|  |  | ||||||
|  | 	// ConnectorData holds data used by the connector for subsequent requests after initial | ||||||
|  | 	// authentication, such as access tokens for upstream provides. | ||||||
|  | 	// | ||||||
|  | 	// This data is never shared with end users, OAuth clients, or through the API. | ||||||
|  | 	ConnectorData []byte | ||||||
|  | } | ||||||
|  |  | ||||||
| // PasswordConnector is an optional interface for password based connectors. | // PasswordConnector is an optional interface for password based connectors. | ||||||
| type PasswordConnector interface { | type PasswordConnector interface { | ||||||
| 	Login(username, password string) (identity storage.Identity, validPassword bool, err error) | 	Login(username, password string) (identity Identity, validPassword bool, err error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // CallbackConnector is an optional interface for callback based connectors. | // CallbackConnector is an optional interface for callback based connectors. | ||||||
| type CallbackConnector interface { | type CallbackConnector interface { | ||||||
| 	LoginURL(callbackURL, state string) (string, error) | 	LoginURL(callbackURL, state string) (string, error) | ||||||
| 	HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) | 	HandleCallback(r *http.Request) (identity Identity, state string, err error) | ||||||
| } | } | ||||||
|  |  | ||||||
| // GroupsConnector is an optional interface for connectors which can map a user to groups. | // GroupsConnector is an optional interface for connectors which can map a user to groups. | ||||||
| type GroupsConnector interface { | type GroupsConnector interface { | ||||||
| 	Groups(identity storage.Identity) ([]string, error) | 	Groups(identity Identity) ([]string, error) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,7 +14,6 @@ import ( | |||||||
| 	"golang.org/x/oauth2/github" | 	"golang.org/x/oauth2/github" | ||||||
|  |  | ||||||
| 	"github.com/coreos/poke/connector" | 	"github.com/coreos/poke/connector" | ||||||
| 	"github.com/coreos/poke/storage" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const baseURL = "https://api.github.com" | const baseURL = "https://api.github.com" | ||||||
| @@ -85,7 +84,7 @@ func (e *oauth2Error) Error() string { | |||||||
| 	return e.error + ": " + e.errorDescription | 	return e.error + ": " + e.errorDescription | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) { | func (c *githubConnector) HandleCallback(r *http.Request) (identity connector.Identity, state string, err error) { | ||||||
| 	q := r.URL.Query() | 	q := r.URL.Query() | ||||||
| 	if errType := q.Get("error"); errType != "" { | 	if errType := q.Get("error"); errType != "" { | ||||||
| 		return identity, "", &oauth2Error{errType, q.Get("error_description")} | 		return identity, "", &oauth2Error{errType, q.Get("error_description")} | ||||||
| @@ -128,7 +127,7 @@ func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Iden | |||||||
| 	if username == "" { | 	if username == "" { | ||||||
| 		username = user.Login | 		username = user.Login | ||||||
| 	} | 	} | ||||||
| 	identity = storage.Identity{ | 	identity = connector.Identity{ | ||||||
| 		UserID:        strconv.Itoa(user.ID), | 		UserID:        strconv.Itoa(user.ID), | ||||||
| 		Username:      username, | 		Username:      username, | ||||||
| 		Email:         user.Email, | 		Email:         user.Email, | ||||||
| @@ -138,7 +137,7 @@ func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Iden | |||||||
| 	return identity, q.Get("state"), nil | 	return identity, q.Get("state"), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *githubConnector) Groups(identity storage.Identity) ([]string, error) { | func (c *githubConnector) Groups(identity connector.Identity) ([]string, error) { | ||||||
| 	var data connectorData | 	var data connectorData | ||||||
| 	if err := json.Unmarshal(identity.ConnectorData, &data); err != nil { | 	if err := json.Unmarshal(identity.ConnectorData, &data); err != nil { | ||||||
| 		return nil, fmt.Errorf("decode connector data: %v", err) | 		return nil, fmt.Errorf("decode connector data: %v", err) | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"gopkg.in/ldap.v2" | 	"gopkg.in/ldap.v2" | ||||||
|  |  | ||||||
| 	"github.com/coreos/poke/connector" | 	"github.com/coreos/poke/connector" | ||||||
| 	"github.com/coreos/poke/storage" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Config holds the configuration parameters for the LDAP connector. | // Config holds the configuration parameters for the LDAP connector. | ||||||
| @@ -32,6 +31,8 @@ type ldapConnector struct { | |||||||
| 	Config | 	Config | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var _ connector.PasswordConnector = (*ldapConnector)(nil) | ||||||
|  |  | ||||||
| func (c *ldapConnector) do(f func(c *ldap.Conn) error) error { | func (c *ldapConnector) do(f func(c *ldap.Conn) error) error { | ||||||
| 	// TODO(ericchiang): Connection pooling. | 	// TODO(ericchiang): Connection pooling. | ||||||
| 	conn, err := ldap.Dial("tcp", c.Host) | 	conn, err := ldap.Dial("tcp", c.Host) | ||||||
| @@ -43,15 +44,16 @@ func (c *ldapConnector) do(f func(c *ldap.Conn) error) error { | |||||||
| 	return f(conn) | 	return f(conn) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *ldapConnector) Login(username, password string) (storage.Identity, error) { | func (c *ldapConnector) Login(username, password string) (connector.Identity, bool, error) { | ||||||
| 	err := c.do(func(conn *ldap.Conn) error { | 	err := c.do(func(conn *ldap.Conn) error { | ||||||
| 		return conn.Bind(fmt.Sprintf("uid=%s,%s", username, c.BindDN), password) | 		return conn.Bind(fmt.Sprintf("uid=%s,%s", username, c.BindDN), password) | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return storage.Identity{}, err | 		// TODO(ericchiang): Determine when the user has entered invalid credentials. | ||||||
|  | 		return connector.Identity{}, false, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return storage.Identity{Username: username}, nil | 	return connector.Identity{Username: username}, true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *ldapConnector) Close() error { | func (c *ldapConnector) Close() error { | ||||||
|   | |||||||
| @@ -2,12 +2,13 @@ | |||||||
| package mock | package mock | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  |  | ||||||
| 	"github.com/coreos/poke/connector" | 	"github.com/coreos/poke/connector" | ||||||
| 	"github.com/coreos/poke/storage" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // New returns a mock connector which requires no user interaction. It always returns | // New returns a mock connector which requires no user interaction. It always returns | ||||||
| @@ -16,6 +17,11 @@ func New() connector.Connector { | |||||||
| 	return mockConnector{} | 	return mockConnector{} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	_ connector.CallbackConnector = mockConnector{} | ||||||
|  | 	_ connector.GroupsConnector   = mockConnector{} | ||||||
|  | ) | ||||||
|  |  | ||||||
| type mockConnector struct{} | type mockConnector struct{} | ||||||
|  |  | ||||||
| func (m mockConnector) Close() error { return nil } | func (m mockConnector) Close() error { return nil } | ||||||
| @@ -31,16 +37,22 @@ func (m mockConnector) LoginURL(callbackURL, state string) (string, error) { | |||||||
| 	return u.String(), nil | 	return u.String(), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m mockConnector) HandleCallback(r *http.Request) (storage.Identity, string, error) { | var connectorData = []byte("foobar") | ||||||
| 	return storage.Identity{ |  | ||||||
|  | func (m mockConnector) HandleCallback(r *http.Request) (connector.Identity, string, error) { | ||||||
|  | 	return connector.Identity{ | ||||||
| 		UserID:        "0-385-28089-0", | 		UserID:        "0-385-28089-0", | ||||||
| 		Username:      "Kilgore Trout", | 		Username:      "Kilgore Trout", | ||||||
| 		Email:         "kilgore@kilgore.trout", | 		Email:         "kilgore@kilgore.trout", | ||||||
| 		EmailVerified: true, | 		EmailVerified: true, | ||||||
|  | 		ConnectorData: connectorData, | ||||||
| 	}, r.URL.Query().Get("state"), nil | 	}, r.URL.Query().Get("state"), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m mockConnector) Groups(identity storage.Identity) ([]string, error) { | func (m mockConnector) Groups(identity connector.Identity) ([]string, error) { | ||||||
|  | 	if !bytes.Equal(identity.ConnectorData, connectorData) { | ||||||
|  | 		return nil, errors.New("connector data mismatch") | ||||||
|  | 	} | ||||||
| 	return []string{"authors"}, nil | 	return []string{"authors"}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -180,17 +180,14 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) { | |||||||
| 			renderPasswordTmpl(w, state, r.URL.String(), "Invalid credentials") | 			renderPasswordTmpl(w, state, r.URL.String(), "Invalid credentials") | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  | 		redirectURL, err := s.finalizeLogin(identity, state, connID, conn.Connector) | ||||||
| 		groups, ok, err := s.groups(identity, state, conn.Connector) |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | 			log.Printf("Failed to finalize login: %v", err) | ||||||
| 			s.renderError(w, http.StatusInternalServerError, errServerError, "") | 			s.renderError(w, http.StatusInternalServerError, errServerError, "") | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		if ok { |  | ||||||
| 			identity.Groups = groups |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		s.redirectToApproval(w, r, identity, connID, state) | 		http.Redirect(w, r, redirectURL, http.StatusSeeOther) | ||||||
| 	default: | 	default: | ||||||
| 		s.notFound(w, r) | 		s.notFound(w, r) | ||||||
| 	} | 	} | ||||||
| @@ -215,40 +212,30 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request) | |||||||
| 		s.renderError(w, http.StatusInternalServerError, errServerError, "") | 		s.renderError(w, http.StatusInternalServerError, errServerError, "") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	groups, ok, err := s.groups(identity, state, conn.Connector) |  | ||||||
|  | 	redirectURL, err := s.finalizeLogin(identity, state, connID, conn.Connector) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | 		log.Printf("Failed to finalize login: %v", err) | ||||||
| 		s.renderError(w, http.StatusInternalServerError, errServerError, "") | 		s.renderError(w, http.StatusInternalServerError, errServerError, "") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if ok { |  | ||||||
| 		identity.Groups = groups | 	http.Redirect(w, r, redirectURL, http.StatusSeeOther) | ||||||
| 	} |  | ||||||
| 	s.redirectToApproval(w, r, identity, connID, state) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Server) redirectToApproval(w http.ResponseWriter, r *http.Request, identity storage.Identity, connectorID, state string) { | func (s *Server) finalizeLogin(identity connector.Identity, authReqID, connectorID string, conn connector.Connector) (string, error) { | ||||||
| 	updater := func(a storage.AuthRequest) (storage.AuthRequest, error) { | 	claims := storage.Identity{ | ||||||
| 		a.Identity = &identity | 		UserID:        identity.UserID, | ||||||
| 		a.ConnectorID = connectorID | 		Username:      identity.Username, | ||||||
| 		return a, nil | 		Email:         identity.Email, | ||||||
| 	} | 		EmailVerified: identity.EmailVerified, | ||||||
| 	if err := s.storage.UpdateAuthRequest(state, updater); err != nil { |  | ||||||
| 		log.Printf("Failed to updated auth request with identity: %v", err) |  | ||||||
| 		s.renderError(w, http.StatusInternalServerError, errServerError, "") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	http.Redirect(w, r, path.Join(s.issuerURL.Path, "/approval")+"?state="+state, http.StatusSeeOther) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| func (s *Server) groups(identity storage.Identity, authReqID string, conn connector.Connector) ([]string, bool, error) { |  | ||||||
| 	groupsConn, ok := conn.(connector.GroupsConnector) | 	groupsConn, ok := conn.(connector.GroupsConnector) | ||||||
| 	if !ok { | 	if ok { | ||||||
| 		return nil, false, nil |  | ||||||
| 	} |  | ||||||
| 		authReq, err := s.storage.GetAuthRequest(authReqID) | 		authReq, err := s.storage.GetAuthRequest(authReqID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 		log.Printf("get auth request: %v", err) | 			return "", fmt.Errorf("get auth request: %v", err) | ||||||
| 		return nil, false, err |  | ||||||
| 		} | 		} | ||||||
| 		reqGroups := func() bool { | 		reqGroups := func() bool { | ||||||
| 			for _, scope := range authReq.Scopes { | 			for _, scope := range authReq.Scopes { | ||||||
| @@ -258,11 +245,23 @@ func (s *Server) groups(identity storage.Identity, authReqID string, conn connec | |||||||
| 			} | 			} | ||||||
| 			return false | 			return false | ||||||
| 		}() | 		}() | ||||||
| 	if !reqGroups { | 		if reqGroups { | ||||||
| 		return nil, false, nil | 			if claims.Groups, err = groupsConn.Groups(identity); err != nil { | ||||||
|  | 				return "", fmt.Errorf("getting groups: %v", err) | ||||||
| 			} | 			} | ||||||
| 	groups, err := groupsConn.Groups(identity) | 		} | ||||||
| 	return groups, true, err | 	} | ||||||
|  |  | ||||||
|  | 	updater := func(a storage.AuthRequest) (storage.AuthRequest, error) { | ||||||
|  | 		a.Identity = &claims | ||||||
|  | 		a.ConnectorID = connectorID | ||||||
|  | 		a.ConnectorData = identity.ConnectorData | ||||||
|  | 		return a, nil | ||||||
|  | 	} | ||||||
|  | 	if err := s.storage.UpdateAuthRequest(authReqID, updater); err != nil { | ||||||
|  | 		return "", fmt.Errorf("failed to update auth request: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return path.Join(s.issuerURL.Path, "/approval") + "?state=" + authReqID, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) { | func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) { | ||||||
|   | |||||||
| @@ -77,8 +77,6 @@ type Identity struct { | |||||||
| 	Email         string   `json:"email"` | 	Email         string   `json:"email"` | ||||||
| 	EmailVerified bool     `json:"emailVerified"` | 	EmailVerified bool     `json:"emailVerified"` | ||||||
| 	Groups        []string `json:"groups,omitempty"` | 	Groups        []string `json:"groups,omitempty"` | ||||||
|  |  | ||||||
| 	ConnectorData []byte `json:"connectorData,omitempty"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func fromStorageIdentity(i storage.Identity) Identity { | func fromStorageIdentity(i storage.Identity) Identity { | ||||||
| @@ -88,7 +86,6 @@ func fromStorageIdentity(i storage.Identity) Identity { | |||||||
| 		Email:         i.Email, | 		Email:         i.Email, | ||||||
| 		EmailVerified: i.EmailVerified, | 		EmailVerified: i.EmailVerified, | ||||||
| 		Groups:        i.Groups, | 		Groups:        i.Groups, | ||||||
| 		ConnectorData: i.ConnectorData, |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -99,7 +96,6 @@ func toStorageIdentity(i Identity) storage.Identity { | |||||||
| 		Email:         i.Email, | 		Email:         i.Email, | ||||||
| 		EmailVerified: i.EmailVerified, | 		EmailVerified: i.EmailVerified, | ||||||
| 		Groups:        i.Groups, | 		Groups:        i.Groups, | ||||||
| 		ConnectorData: i.ConnectorData, |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -127,6 +123,7 @@ type AuthRequest struct { | |||||||
| 	Identity *Identity `json:"identity,omitempty"` | 	Identity *Identity `json:"identity,omitempty"` | ||||||
| 	// The connector used to login the user. Set when the user authenticates. | 	// The connector used to login the user. Set when the user authenticates. | ||||||
| 	ConnectorID   string `json:"connectorID,omitempty"` | 	ConnectorID   string `json:"connectorID,omitempty"` | ||||||
|  | 	ConnectorData []byte `json:"connectorData,omitempty"` | ||||||
|  |  | ||||||
| 	Expiry time.Time `json:"expiry"` | 	Expiry time.Time `json:"expiry"` | ||||||
| } | } | ||||||
| @@ -149,6 +146,7 @@ func toStorageAuthRequest(req AuthRequest) storage.AuthRequest { | |||||||
| 		State:               req.State, | 		State:               req.State, | ||||||
| 		ForceApprovalPrompt: req.ForceApprovalPrompt, | 		ForceApprovalPrompt: req.ForceApprovalPrompt, | ||||||
| 		ConnectorID:         req.ConnectorID, | 		ConnectorID:         req.ConnectorID, | ||||||
|  | 		ConnectorData:       req.ConnectorData, | ||||||
| 		Expiry:              req.Expiry, | 		Expiry:              req.Expiry, | ||||||
| 	} | 	} | ||||||
| 	if req.Identity != nil { | 	if req.Identity != nil { | ||||||
| @@ -176,6 +174,7 @@ func (cli *client) fromStorageAuthRequest(a storage.AuthRequest) AuthRequest { | |||||||
| 		State:               a.State, | 		State:               a.State, | ||||||
| 		ForceApprovalPrompt: a.ForceApprovalPrompt, | 		ForceApprovalPrompt: a.ForceApprovalPrompt, | ||||||
| 		ConnectorID:         a.ConnectorID, | 		ConnectorID:         a.ConnectorID, | ||||||
|  | 		ConnectorData:       a.ConnectorData, | ||||||
| 		Expiry:              a.Expiry, | 		Expiry:              a.Expiry, | ||||||
| 	} | 	} | ||||||
| 	if a.Identity != nil { | 	if a.Identity != nil { | ||||||
| @@ -199,7 +198,9 @@ type AuthCode struct { | |||||||
| 	State string `json:"state,omitempty"` | 	State string `json:"state,omitempty"` | ||||||
|  |  | ||||||
| 	Identity Identity `json:"identity,omitempty"` | 	Identity Identity `json:"identity,omitempty"` | ||||||
|  |  | ||||||
| 	ConnectorID   string `json:"connectorID,omitempty"` | 	ConnectorID   string `json:"connectorID,omitempty"` | ||||||
|  | 	ConnectorData []byte `json:"connectorData,omitempty"` | ||||||
|  |  | ||||||
| 	Expiry time.Time `json:"expiry"` | 	Expiry time.Time `json:"expiry"` | ||||||
| } | } | ||||||
| @@ -224,6 +225,7 @@ func (cli *client) fromStorageAuthCode(a storage.AuthCode) AuthCode { | |||||||
| 		ClientID:      a.ClientID, | 		ClientID:      a.ClientID, | ||||||
| 		RedirectURI:   a.RedirectURI, | 		RedirectURI:   a.RedirectURI, | ||||||
| 		ConnectorID:   a.ConnectorID, | 		ConnectorID:   a.ConnectorID, | ||||||
|  | 		ConnectorData: a.ConnectorData, | ||||||
| 		Nonce:         a.Nonce, | 		Nonce:         a.Nonce, | ||||||
| 		Scopes:        a.Scopes, | 		Scopes:        a.Scopes, | ||||||
| 		Identity:      fromStorageIdentity(a.Identity), | 		Identity:      fromStorageIdentity(a.Identity), | ||||||
| @@ -237,6 +239,7 @@ func toStorageAuthCode(a AuthCode) storage.AuthCode { | |||||||
| 		ClientID:      a.ClientID, | 		ClientID:      a.ClientID, | ||||||
| 		RedirectURI:   a.RedirectURI, | 		RedirectURI:   a.RedirectURI, | ||||||
| 		ConnectorID:   a.ConnectorID, | 		ConnectorID:   a.ConnectorID, | ||||||
|  | 		ConnectorData: a.ConnectorData, | ||||||
| 		Nonce:         a.Nonce, | 		Nonce:         a.Nonce, | ||||||
| 		Scopes:        a.Scopes, | 		Scopes:        a.Scopes, | ||||||
| 		Identity:      toStorageIdentity(a.Identity), | 		Identity:      toStorageIdentity(a.Identity), | ||||||
|   | |||||||
| @@ -104,12 +104,6 @@ type Identity struct { | |||||||
| 	EmailVerified bool | 	EmailVerified bool | ||||||
|  |  | ||||||
| 	Groups []string | 	Groups []string | ||||||
|  |  | ||||||
| 	// ConnectorData holds data used by the connector for subsequent requests after initial |  | ||||||
| 	// authentication, such as access tokens for upstream provides. |  | ||||||
| 	// |  | ||||||
| 	// This data is never shared with end users, OAuth clients, or through the API. |  | ||||||
| 	ConnectorData []byte |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // AuthRequest represents a OAuth2 client authorization request. It holds the state | // AuthRequest represents a OAuth2 client authorization request. It holds the state | ||||||
| @@ -133,8 +127,11 @@ type AuthRequest struct { | |||||||
| 	// The identity of the end user. Generally nil until the user authenticates | 	// The identity of the end user. Generally nil until the user authenticates | ||||||
| 	// with a backend. | 	// with a backend. | ||||||
| 	Identity *Identity | 	Identity *Identity | ||||||
| 	// The connector used to login the user. Set when the user authenticates. |  | ||||||
|  | 	// The connector used to login the user and any data the connector wishes to persists. | ||||||
|  | 	// Set when the user authenticates. | ||||||
| 	ConnectorID   string | 	ConnectorID   string | ||||||
|  | 	ConnectorData []byte | ||||||
|  |  | ||||||
| 	Expiry time.Time | 	Expiry time.Time | ||||||
| } | } | ||||||
| @@ -145,7 +142,9 @@ type AuthCode struct { | |||||||
|  |  | ||||||
| 	ClientID    string | 	ClientID    string | ||||||
| 	RedirectURI string | 	RedirectURI string | ||||||
|  |  | ||||||
| 	ConnectorID   string | 	ConnectorID   string | ||||||
|  | 	ConnectorData []byte | ||||||
|  |  | ||||||
| 	Nonce string | 	Nonce string | ||||||
|  |  | ||||||
| @@ -163,7 +162,9 @@ type Refresh struct { | |||||||
|  |  | ||||||
| 	// Client this refresh token is valid for. | 	// Client this refresh token is valid for. | ||||||
| 	ClientID string | 	ClientID string | ||||||
|  |  | ||||||
| 	ConnectorID   string | 	ConnectorID   string | ||||||
|  | 	ConnectorData []byte | ||||||
|  |  | ||||||
| 	// Scopes present in the initial request. Refresh requests may specify a set | 	// Scopes present in the initial request. Refresh requests may specify a set | ||||||
| 	// of scopes different from the initial request when refreshing a token, | 	// of scopes different from the initial request when refreshing a token, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user