git.iko.soy/typeable/oauth2-proxy
on master help sign in
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.

commita0763477c54f30741e7976499a53c9fbe897455b authorJehiah Czebotar <jehiah@gmail.com> dateThu 23 Jun 2016, 08:29 (10 years ago) parent3a79827
4 files changed+107
README.md+6 api/api.go+19 providers/facebook.go+80 providers/providers.go+2
README.md+6
@@ -30,6 +30,7 @@ Valid providers are :
3030
3131* [Google](#google-auth-provider) *default*
3232* [Azure](#azure-auth-provider)
33+* [Facebook](#facebook-auth-provider)
3334* [GitHub](#github-auth-provider)
3435* [GitLab](#gitlab-auth-provider)
3536* [LinkedIn](#linkedin-auth-provider)
@@ -87,6 +88,11 @@ Note: The user is checked against the group members list on initial authenticati
8788The 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.
8889
8990
91+### Facebook Auth Provider
92+
93+1. Create a new FB App from <https://developers.facebook.com/>
94+2. Under FB Login, set your Valid OAuth redirect URIs to `https://internal.yourcompany.com/oauth2/callback`
95+
9096### GitHub Auth Provider
9197
92981. Create a new project: https://github.com/settings/developers
api/api.go+19
@@ -1,6 +1,7 @@
11package api
22
33import (
4+ "encoding/json"
45 "fmt"
56 "io/ioutil"
67 "log"
@@ -31,6 +32,24 @@ func Request(req *http.Request) (*simplejson.Json, error) {
3132 return data, nil
3233}
3334
35+func RequestJson(req *http.Request, v interface{}) error {
36+ resp, err := http.DefaultClient.Do(req)
37+ if err != nil {
38+ log.Printf("%s %s %s", req.Method, req.URL, err)
39+ return err
40+ }
41+ body, err := ioutil.ReadAll(resp.Body)
42+ resp.Body.Close()
43+ log.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
44+ if err != nil {
45+ return err
46+ }
47+ if resp.StatusCode != 200 {
48+ return fmt.Errorf("got %d %s", resp.StatusCode, body)
49+ }
50+ return json.Unmarshal(body, v)
51+}
52+
3453func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) {
3554 req, err := http.NewRequest("GET", url, nil)
3655 if err != nil {
providers/facebook.gonew file+80
@@ -0,0 +1,80 @@
1+package providers
2+
3+import (
4+ "errors"
5+ "fmt"
6+ "net/http"
7+ "net/url"
8+
9+ "github.com/bitly/oauth2_proxy/api"
10+)
11+
12+type FacebookProvider struct {
13+ *ProviderData
14+}
15+
16+func NewFacebookProvider(p *ProviderData) *FacebookProvider {
17+ p.ProviderName = "Facebook"
18+ if p.LoginURL.String() == "" {
19+ p.LoginURL = &url.URL{Scheme: "https",
20+ Host: "www.facebook.com",
21+ Path: "/v2.5/dialog/oauth",
22+ // ?granted_scopes=true
23+ }
24+ }
25+ if p.RedeemURL.String() == "" {
26+ p.RedeemURL = &url.URL{Scheme: "https",
27+ Host: "graph.facebook.com",
28+ Path: "/v2.5/oauth/access_token",
29+ }
30+ }
31+ if p.ProfileURL.String() == "" {
32+ p.ProfileURL = &url.URL{Scheme: "https",
33+ Host: "graph.facebook.com",
34+ Path: "/v2.5/me",
35+ }
36+ }
37+ if p.ValidateURL.String() == "" {
38+ p.ValidateURL = p.ProfileURL
39+ }
40+ if p.Scope == "" {
41+ p.Scope = "public_profile email"
42+ }
43+ return &FacebookProvider{ProviderData: p}
44+}
45+
46+func getFacebookHeader(access_token string) http.Header {
47+ header := make(http.Header)
48+ header.Set("Accept", "application/json")
49+ header.Set("x-li-format", "json")
50+ header.Set("Authorization", fmt.Sprintf("Bearer %s", access_token))
51+ return header
52+}
53+
54+func (p *FacebookProvider) GetEmailAddress(s *SessionState) (string, error) {
55+ if s.AccessToken == "" {
56+ return "", errors.New("missing access token")
57+ }
58+ req, err := http.NewRequest("GET", p.ProfileURL.String()+"?fields=name,email", nil)
59+ if err != nil {
60+ return "", err
61+ }
62+ req.Header = getFacebookHeader(s.AccessToken)
63+
64+ type result struct {
65+ Email string
66+ }
67+ var r result
68+ err = api.RequestJson(req, &r)
69+ if err != nil {
70+ return "", err
71+ }
72+ if r.Email == "" {
73+ return "", errors.New("no email")
74+ }
75+ return r.Email, nil
76+}
77+
78+func (p *FacebookProvider) ValidateSessionState(s *SessionState) bool {
79+ return validateToken(p, s.AccessToken, getFacebookHeader(s.AccessToken))
80+}
providers/providers.go+2
@@ -22,6 +22,8 @@ func New(provider string, p *ProviderData) Provider {
2222 return NewMyUsaProvider(p)
2323 case "linkedin":
2424 return NewLinkedInProvider(p)
25+ case "facebook":
26+ return NewFacebookProvider(p)
2527 case "github":
2628 return NewGitHubProvider(p)
2729 case "azure":