Skip to content

Commit

Permalink
Rewrite oauth module
Browse files Browse the repository at this point in the history
- Tried to be clear about OAuth2 vs OAuth in all places.
- Allow users to be locked from OAuth logins (if done manually for some
  reason other than failed logins)
- Cleaned up some docs and wording around the previously very confusing
  (now hopefully only somewhat confusing) oauth2 module.
  • Loading branch information
aarondl committed Mar 9, 2018
1 parent 634892e commit 1112987
Show file tree
Hide file tree
Showing 15 changed files with 732 additions and 266 deletions.
5 changes: 5 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ type Config struct {
// LogoutOK is the redirect path after a log out.
LogoutOK string

// OAuth2LoginOK is the redirect path after a successful oauth2 login
OAuth2LoginOK string
// OAuth2LoginNotOK is the redirect path after an unsuccessful oauth2 login
OAuth2LoginNotOK string

// RecoverOK is the redirect path after a successful recovery of a password.
RecoverOK string

Expand Down
4 changes: 2 additions & 2 deletions events.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ type Event int
const (
EventRegister Event = iota
EventAuth
EventOAuth
EventOAuth2
EventAuthFail
EventOAuthFail
EventOAuth2Fail
EventRecoverStart
EventRecoverEnd
EventGetUser
Expand Down
4 changes: 2 additions & 2 deletions events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ func TestEventString(t *testing.T) {
}{
{EventRegister, "EventRegister"},
{EventAuth, "EventAuth"},
{EventOAuth, "EventOAuth"},
{EventOAuth2, "EventOAuth2"},
{EventAuthFail, "EventAuthFail"},
{EventOAuthFail, "EventOAuthFail"},
{EventOAuth2Fail, "EventOAuth2Fail"},
{EventRecoverStart, "EventRecoverStart"},
{EventRecoverEnd, "EventRecoverEnd"},
{EventGetUser, "EventGetUser"},
Expand Down
58 changes: 49 additions & 9 deletions internal/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ type User struct {
AttemptCount int
LastAttempt time.Time
Locked time.Time
OAuthToken string
OAuthRefresh string
OAuthExpiry time.Time

OAuth2UID string
OAuth2Provider string
OAuth2Token string
OAuth2Refresh string
OAuth2Expiry time.Time

Arbitrary map[string]string
}
Expand All @@ -43,9 +46,12 @@ func (m User) GetConfirmed() bool { return m.Confirmed }
func (m User) GetAttemptCount() int { return m.AttemptCount }
func (m User) GetLastAttempt() time.Time { return m.LastAttempt }
func (m User) GetLocked() time.Time { return m.Locked }
func (m User) GetOAuthToken() string { return m.OAuthToken }
func (m User) GetOAuthRefresh() string { return m.OAuthRefresh }
func (m User) GetOAuthExpiry() time.Time { return m.OAuthExpiry }
func (m User) IsOAuth2User() bool { return len(m.OAuth2Provider) != 0 }
func (m User) GetOAuth2UID() string { return m.OAuth2UID }
func (m User) GetOAuth2Provider() string { return m.OAuth2Provider }
func (m User) GetOAuth2AccessToken() string { return m.OAuth2Token }
func (m User) GetOAuth2RefreshToken() string { return m.OAuth2Refresh }
func (m User) GetOAuth2Expiry() time.Time { return m.OAuth2Expiry }
func (m User) GetArbitrary() map[string]string { return m.Arbitrary }

func (m *User) PutPID(email string) { m.Email = email }
Expand All @@ -61,9 +67,11 @@ func (m *User) PutConfirmed(confirmed bool) { m.Confirmed = confirmed }
func (m *User) PutAttemptCount(attemptCount int) { m.AttemptCount = attemptCount }
func (m *User) PutLastAttempt(attemptTime time.Time) { m.LastAttempt = attemptTime }
func (m *User) PutLocked(locked time.Time) { m.Locked = locked }
func (m *User) PutOAuthToken(oAuthToken string) { m.OAuthToken = oAuthToken }
func (m *User) PutOAuthRefresh(oAuthRefresh string) { m.OAuthRefresh = oAuthRefresh }
func (m *User) PutOAuthExpiry(oAuthExpiry time.Time) { m.OAuthExpiry = oAuthExpiry }
func (m *User) PutOAuth2UID(uid string) { m.OAuth2UID = uid }
func (m *User) PutOAuth2Provider(provider string) { m.OAuth2Provider = provider }
func (m *User) PutOAuth2AccessToken(token string) { m.OAuth2Token = token }
func (m *User) PutOAuth2RefreshToken(refresh string) { m.OAuth2Refresh = refresh }
func (m *User) PutOAuth2Expiry(expiry time.Time) { m.OAuth2Expiry = expiry }
func (m *User) PutArbitrary(arb map[string]string) { m.Arbitrary = arb }

// ServerStorer should be valid for any module storer defined in authboss.
Expand Down Expand Up @@ -115,6 +123,38 @@ func (s *ServerStorer) Save(ctx context.Context, user authboss.User) error {
return nil
}

// NewFromOAuth2 finds a user with the given details, or returns a new one
func (s *ServerStorer) NewFromOAuth2(ctx context.Context, provider string, details map[string]string) (authboss.OAuth2User, error) {
uid := details["uid"]
email := details["email"]
name := details["name"]
pid := authboss.MakeOAuth2PID(provider, uid)

u, ok := s.Users[pid]
if ok {
u.Username = name
u.Email = email
return u, nil
}

return &User{
OAuth2UID: uid,
OAuth2Provider: provider,
Email: email,
Username: name,
}, nil
}

// SaveOAuth2 creates a user if not found, or updates one that exists.
func (s *ServerStorer) SaveOAuth2(ctx context.Context, user authboss.OAuth2User) error {
u := user.(*User)

pid := authboss.MakeOAuth2PID(u.OAuth2Provider, u.OAuth2UID)
// Since we don't have to differentiate between insert/update in a map, we just overwrite
s.Users[pid] = u
return nil
}

// LoadByConfirmToken finds a user by his confirm token
func (s *ServerStorer) LoadByConfirmToken(ctx context.Context, token string) (authboss.ConfirmableUser, error) {
for _, v := range s.Users {
Expand Down
2 changes: 1 addition & 1 deletion lock/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ type Lock struct {
func (l *Lock) Init(ab *authboss.Authboss) error {
l.Authboss = ab

// Events
l.Events.Before(authboss.EventAuth, l.BeforeAuth)
l.Events.Before(authboss.EventOAuth, l.BeforeAuth)
l.Events.After(authboss.EventAuth, l.AfterAuthSuccess)
l.Events.After(authboss.EventAuthFail, l.AfterAuthFail)

Expand Down
25 changes: 6 additions & 19 deletions oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ import (
"golang.org/x/oauth2"
)

// FormValue constants
const (
FormValueOAuth2State = "state"
)

/*
OAuth2Provider is the entire configuration
required to authenticate with this provider.
Expand All @@ -23,22 +18,14 @@ AdditionalParams can be used to specify extra parameters to tack on to the
end of the initial request, this allows for provider specific oauth options
like access_type=offline to be passed to the provider.
Callback gives the config and the token allowing an http client using the
authenticated token to be created. Because each OAuth2 implementation has a different
API this must be handled for each provider separately. It is used to return two things
specifically: UID (the ID according to the provider) and the Email address.
The UID must be passed back or there will be an error as it is the means of identifying the
user in the system, e-mail is optional but should be returned in systems using
emailing. The keys authboss.StoreOAuth2UID and authboss.StoreEmail can be used to set
these values in the map returned by the callback.
In addition to the required values mentioned above any additional
values that you wish to have in your user struct can be included here, such as the
Name of the user at the endpoint. This will be passed back in the Arbitrary()
function if it exists.
FindUserDetails gives the config and the token allowing an http client using the
authenticated token to be created, a call is then made to a known endpoint that will
return details about the user we've retrieved the token for. Those details are returned
as a map[string]string and subsequently passed into OAuth2ServerStorer.NewFromOAuth2.
API this must be handled for each provider separately.
*/
type OAuth2Provider struct {
OAuth2Config *oauth2.Config
AdditionalParams url.Values
Callback func(context.Context, oauth2.Config, *oauth2.Token) (map[string]string, error)
FindUserDetails func(context.Context, oauth2.Config, *oauth2.Token) (map[string]string, error)
}
Loading

0 comments on commit 1112987

Please sign in to comment.