Create providers package with Google default
This commit is contained in:
		
							parent
							
								
									41044ecac0
								
							
						
					
					
						commit
						e2931da853
					
				|  | @ -0,0 +1,62 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/bitly/go-simplejson" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type GoogleProvider struct { | ||||||
|  | 	*ProviderData | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewGoogleProvider(p *ProviderData) *GoogleProvider { | ||||||
|  | 	if p.LoginUrl.String() == "" { | ||||||
|  | 		p.LoginUrl = &url.URL{Scheme: "https", | ||||||
|  | 			Host: "accounts.google.com", | ||||||
|  | 			Path: "/o/oauth2/auth"} | ||||||
|  | 	} | ||||||
|  | 	if p.RedeemUrl.String() == "" { | ||||||
|  | 		p.RedeemUrl = &url.URL{Scheme: "https", | ||||||
|  | 			Host: "accounts.google.com", | ||||||
|  | 			Path: "/o/oauth2/token"} | ||||||
|  | 	} | ||||||
|  | 	if p.Scope == "" { | ||||||
|  | 		p.Scope = "profile email" | ||||||
|  | 	} | ||||||
|  | 	return &GoogleProvider{ProviderData: p} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *GoogleProvider) GetEmailAddress(auth_response *simplejson.Json, | ||||||
|  | 	unused_access_token string) (string, error) { | ||||||
|  | 	idToken, err := auth_response.Get("id_token").String() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	// id_token is a base64 encode ID token payload
 | ||||||
|  | 	// https://developers.google.com/accounts/docs/OAuth2Login#obtainuserinfo
 | ||||||
|  | 	jwt := strings.Split(idToken, ".") | ||||||
|  | 	b, err := jwtDecodeSegment(jwt[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	data, err := simplejson.NewJson(b) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	email, err := data.Get("email").String() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return email, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func jwtDecodeSegment(seg string) ([]byte, error) { | ||||||
|  | 	if l := len(seg) % 4; l > 0 { | ||||||
|  | 		seg += strings.Repeat("=", 4-l) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return base64.URLEncoding.DecodeString(seg) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,94 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"github.com/bitly/go-simplejson" | ||||||
|  | 	"github.com/bmizerany/assert" | ||||||
|  | 	"net/url" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func newGoogleProvider() *GoogleProvider { | ||||||
|  | 	return NewGoogleProvider( | ||||||
|  | 		&ProviderData{ | ||||||
|  | 			LoginUrl:   &url.URL{}, | ||||||
|  | 			RedeemUrl:  &url.URL{}, | ||||||
|  | 			ProfileUrl: &url.URL{}, | ||||||
|  | 			Scope:      ""}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderDefaults(t *testing.T) { | ||||||
|  | 	p := newGoogleProvider() | ||||||
|  | 	assert.NotEqual(t, nil, p) | ||||||
|  | 	assert.Equal(t, "https://accounts.google.com/o/oauth2/auth", | ||||||
|  | 		p.Data().LoginUrl.String()) | ||||||
|  | 	assert.Equal(t, "https://accounts.google.com/o/oauth2/token", | ||||||
|  | 		p.Data().RedeemUrl.String()) | ||||||
|  | 	assert.Equal(t, "", p.Data().ProfileUrl.String()) | ||||||
|  | 	assert.Equal(t, "profile email", p.Data().Scope) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderOverrides(t *testing.T) { | ||||||
|  | 	p := NewGoogleProvider( | ||||||
|  | 		&ProviderData{ | ||||||
|  | 			LoginUrl: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/oauth/auth"}, | ||||||
|  | 			RedeemUrl: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/oauth/token"}, | ||||||
|  | 			ProfileUrl: &url.URL{ | ||||||
|  | 				Scheme: "https", | ||||||
|  | 				Host:   "example.com", | ||||||
|  | 				Path:   "/oauth/profile"}, | ||||||
|  | 			Scope: "profile"}) | ||||||
|  | 	assert.NotEqual(t, nil, p) | ||||||
|  | 	assert.Equal(t, "https://example.com/oauth/auth", | ||||||
|  | 		p.Data().LoginUrl.String()) | ||||||
|  | 	assert.Equal(t, "https://example.com/oauth/token", | ||||||
|  | 		p.Data().RedeemUrl.String()) | ||||||
|  | 	assert.Equal(t, "https://example.com/oauth/profile", | ||||||
|  | 		p.Data().ProfileUrl.String()) | ||||||
|  | 	assert.Equal(t, "profile", p.Data().Scope) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderGetEmailAddress(t *testing.T) { | ||||||
|  | 	p := newGoogleProvider() | ||||||
|  | 	j := simplejson.New() | ||||||
|  | 	j.Set("id_token", "ignored prefix."+base64.URLEncoding.EncodeToString( | ||||||
|  | 		[]byte("{\"email\": \"michael.bland@gsa.gov\"}"))) | ||||||
|  | 	email, err := p.GetEmailAddress(j, "ignored access_token") | ||||||
|  | 	assert.Equal(t, "michael.bland@gsa.gov", email) | ||||||
|  | 	assert.Equal(t, nil, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderGetEmailAddressInvalidEncoding(t *testing.T) { | ||||||
|  | 	p := newGoogleProvider() | ||||||
|  | 	j := simplejson.New() | ||||||
|  | 	j.Set("id_token", "ignored prefix.{\"email\": \"michael.bland@gsa.gov\"}") | ||||||
|  | 	email, err := p.GetEmailAddress(j, "ignored access_token") | ||||||
|  | 	assert.Equal(t, "", email) | ||||||
|  | 	assert.NotEqual(t, nil, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderGetEmailAddressInvalidJson(t *testing.T) { | ||||||
|  | 	p := newGoogleProvider() | ||||||
|  | 	j := simplejson.New() | ||||||
|  | 	j.Set("id_token", "ignored prefix."+base64.URLEncoding.EncodeToString( | ||||||
|  | 		[]byte("{email: michael.bland@gsa.gov}"))) | ||||||
|  | 	email, err := p.GetEmailAddress(j, "ignored access_token") | ||||||
|  | 	assert.Equal(t, "", email) | ||||||
|  | 	assert.NotEqual(t, nil, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGoogleProviderGetEmailAddressEmailMissing(t *testing.T) { | ||||||
|  | 	p := newGoogleProvider() | ||||||
|  | 	j := simplejson.New() | ||||||
|  | 	j.Set("id_token", "ignored prefix."+base64.URLEncoding.EncodeToString( | ||||||
|  | 		[]byte("{\"not_email\": \"missing!\"}"))) | ||||||
|  | 	email, err := p.GetEmailAddress(j, "ignored access_token") | ||||||
|  | 	assert.Equal(t, "", email) | ||||||
|  | 	assert.NotEqual(t, nil, err) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,14 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/url" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ProviderData struct { | ||||||
|  | 	LoginUrl   *url.URL | ||||||
|  | 	RedeemUrl  *url.URL | ||||||
|  | 	ProfileUrl *url.URL | ||||||
|  | 	Scope      string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ProviderData) Data() *ProviderData { return p } | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/bitly/go-simplejson" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Provider interface { | ||||||
|  | 	Data() *ProviderData | ||||||
|  | 	GetEmailAddress(auth_response *simplejson.Json, | ||||||
|  | 		access_token string) (string, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func New(provider string, p *ProviderData) Provider { | ||||||
|  | 	switch provider { | ||||||
|  | 	default: | ||||||
|  | 		return NewGoogleProvider(p) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue