Facebook Authentication Provider
* will not re-prompt if the email permission is denied, or if you previously authorized the same FB app without the email scope.
This commit is contained in:
		
							parent
							
								
									3a79827af2
								
							
						
					
					
						commit
						a0763477c5
					
				|  | @ -30,6 +30,7 @@ Valid providers are : | ||||||
| 
 | 
 | ||||||
| * [Google](#google-auth-provider) *default* | * [Google](#google-auth-provider) *default* | ||||||
| * [Azure](#azure-auth-provider) | * [Azure](#azure-auth-provider) | ||||||
|  | * [Facebook](#facebook-auth-provider) | ||||||
| * [GitHub](#github-auth-provider) | * [GitHub](#github-auth-provider) | ||||||
| * [GitLab](#gitlab-auth-provider) | * [GitLab](#gitlab-auth-provider) | ||||||
| * [LinkedIn](#linkedin-auth-provider) | * [LinkedIn](#linkedin-auth-provider) | ||||||
|  | @ -87,6 +88,11 @@ Note: The user is checked against the group members list on initial authenticati | ||||||
| The Azure AD auth provider uses `openid` as it default scope. It uses `https://graph.windows.net` as a default protected resource. It call to `https://graph.windows.net/me` to get the email address of the user that logs in. | The Azure AD auth provider uses `openid` as it default scope. It uses `https://graph.windows.net` as a default protected resource. It call to `https://graph.windows.net/me` to get the email address of the user that logs in. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | ### Facebook Auth Provider | ||||||
|  | 
 | ||||||
|  | 1. Create a new FB App from <https://developers.facebook.com/> | ||||||
|  | 2. Under FB Login, set your Valid OAuth redirect URIs to `https://internal.yourcompany.com/oauth2/callback` | ||||||
|  | 
 | ||||||
| ### GitHub Auth Provider | ### GitHub Auth Provider | ||||||
| 
 | 
 | ||||||
| 1. Create a new project: https://github.com/settings/developers | 1. Create a new project: https://github.com/settings/developers | ||||||
|  |  | ||||||
							
								
								
									
										19
									
								
								api/api.go
								
								
								
								
							
							
						
						
									
										19
									
								
								api/api.go
								
								
								
								
							|  | @ -1,6 +1,7 @@ | ||||||
| package api | package api | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"log" | 	"log" | ||||||
|  | @ -31,6 +32,24 @@ func Request(req *http.Request) (*simplejson.Json, error) { | ||||||
| 	return data, nil | 	return data, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func RequestJson(req *http.Request, v interface{}) error { | ||||||
|  | 	resp, err := http.DefaultClient.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Printf("%s %s %s", req.Method, req.URL, err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	resp.Body.Close() | ||||||
|  | 	log.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if resp.StatusCode != 200 { | ||||||
|  | 		return fmt.Errorf("got %d %s", resp.StatusCode, body) | ||||||
|  | 	} | ||||||
|  | 	return json.Unmarshal(body, v) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) { | func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) { | ||||||
| 	req, err := http.NewRequest("GET", url, nil) | 	req, err := http.NewRequest("GET", url, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,80 @@ | ||||||
|  | package providers | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 
 | ||||||
|  | 	"github.com/bitly/oauth2_proxy/api" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type FacebookProvider struct { | ||||||
|  | 	*ProviderData | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewFacebookProvider(p *ProviderData) *FacebookProvider { | ||||||
|  | 	p.ProviderName = "Facebook" | ||||||
|  | 	if p.LoginURL.String() == "" { | ||||||
|  | 		p.LoginURL = &url.URL{Scheme: "https", | ||||||
|  | 			Host: "www.facebook.com", | ||||||
|  | 			Path: "/v2.5/dialog/oauth", | ||||||
|  | 			// ?granted_scopes=true
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.RedeemURL.String() == "" { | ||||||
|  | 		p.RedeemURL = &url.URL{Scheme: "https", | ||||||
|  | 			Host: "graph.facebook.com", | ||||||
|  | 			Path: "/v2.5/oauth/access_token", | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.ProfileURL.String() == "" { | ||||||
|  | 		p.ProfileURL = &url.URL{Scheme: "https", | ||||||
|  | 			Host: "graph.facebook.com", | ||||||
|  | 			Path: "/v2.5/me", | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if p.ValidateURL.String() == "" { | ||||||
|  | 		p.ValidateURL = p.ProfileURL | ||||||
|  | 	} | ||||||
|  | 	if p.Scope == "" { | ||||||
|  | 		p.Scope = "public_profile email" | ||||||
|  | 	} | ||||||
|  | 	return &FacebookProvider{ProviderData: p} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getFacebookHeader(access_token string) http.Header { | ||||||
|  | 	header := make(http.Header) | ||||||
|  | 	header.Set("Accept", "application/json") | ||||||
|  | 	header.Set("x-li-format", "json") | ||||||
|  | 	header.Set("Authorization", fmt.Sprintf("Bearer %s", access_token)) | ||||||
|  | 	return header | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *FacebookProvider) GetEmailAddress(s *SessionState) (string, error) { | ||||||
|  | 	if s.AccessToken == "" { | ||||||
|  | 		return "", errors.New("missing access token") | ||||||
|  | 	} | ||||||
|  | 	req, err := http.NewRequest("GET", p.ProfileURL.String()+"?fields=name,email", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	req.Header = getFacebookHeader(s.AccessToken) | ||||||
|  | 
 | ||||||
|  | 	type result struct { | ||||||
|  | 		Email string | ||||||
|  | 	} | ||||||
|  | 	var r result | ||||||
|  | 	err = api.RequestJson(req, &r) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	if r.Email == "" { | ||||||
|  | 		return "", errors.New("no email") | ||||||
|  | 	} | ||||||
|  | 	return r.Email, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *FacebookProvider) ValidateSessionState(s *SessionState) bool { | ||||||
|  | 	return validateToken(p, s.AccessToken, getFacebookHeader(s.AccessToken)) | ||||||
|  | } | ||||||
|  | @ -22,6 +22,8 @@ func New(provider string, p *ProviderData) Provider { | ||||||
| 		return NewMyUsaProvider(p) | 		return NewMyUsaProvider(p) | ||||||
| 	case "linkedin": | 	case "linkedin": | ||||||
| 		return NewLinkedInProvider(p) | 		return NewLinkedInProvider(p) | ||||||
|  | 	case "facebook": | ||||||
|  | 		return NewFacebookProvider(p) | ||||||
| 	case "github": | 	case "github": | ||||||
| 		return NewGitHubProvider(p) | 		return NewGitHubProvider(p) | ||||||
| 	case "azure": | 	case "azure": | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue