forked from dexidp/dex
-
Notifications
You must be signed in to change notification settings - Fork 0
/
connector_github.go
145 lines (127 loc) · 3.55 KB
/
connector_github.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package connector
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
"net/url"
"path"
"strconv"
chttp "github.com/coreos/go-oidc/http"
"github.com/coreos/go-oidc/oauth2"
"github.com/coreos/go-oidc/oidc"
)
const (
GitHubConnectorType = "github"
githubAuthURL = "https://github.com/login/oauth/authorize"
githubTokenURL = "https://github.com/login/oauth/access_token"
githubAPIUserURL = "https://api.github.com/user"
)
func init() {
RegisterConnectorConfigType(GitHubConnectorType, func() ConnectorConfig { return &GitHubConnectorConfig{} })
}
type GitHubConnectorConfig struct {
ID string `json:"id"`
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
}
func (cfg *GitHubConnectorConfig) ConnectorID() string {
return cfg.ID
}
func (cfg *GitHubConnectorConfig) ConnectorType() string {
return GitHubConnectorType
}
func (cfg *GitHubConnectorConfig) Connector(ns url.URL, lf oidc.LoginFunc, tpls *template.Template) (Connector, error) {
ns.Path = path.Join(ns.Path, httpPathCallback)
oauth2Conn, err := newGitHubConnector(cfg.ClientID, cfg.ClientSecret, ns.String())
if err != nil {
return nil, err
}
return &OAuth2Connector{
id: cfg.ID,
loginFunc: lf,
cbURL: ns,
conn: oauth2Conn,
}, nil
}
type githubOAuth2Connector struct {
clientID string
clientSecret string
client *oauth2.Client
}
func newGitHubConnector(clientID, clientSecret, cbURL string) (oauth2Connector, error) {
config := oauth2.Config{
Credentials: oauth2.ClientCredentials{ID: clientID, Secret: clientSecret},
AuthURL: githubAuthURL,
TokenURL: githubTokenURL,
Scope: []string{"user:email"},
AuthMethod: oauth2.AuthMethodClientSecretPost,
RedirectURL: cbURL,
}
cli, err := oauth2.NewClient(http.DefaultClient, config)
if err != nil {
return nil, err
}
return &githubOAuth2Connector{
clientID: clientID,
clientSecret: clientSecret,
client: cli,
}, nil
}
// standard error form returned by github
type githubError struct {
Message string `json:"message"`
}
func (err githubError) Error() string {
return fmt.Sprintf("github: %s", err.Message)
}
func (c *githubOAuth2Connector) Client() *oauth2.Client {
return c.client
}
func (c *githubOAuth2Connector) Identity(cli chttp.Client) (oidc.Identity, error) {
req, err := http.NewRequest("GET", githubAPIUserURL, nil)
if err != nil {
return oidc.Identity{}, err
}
resp, err := cli.Do(req)
if err != nil {
return oidc.Identity{}, fmt.Errorf("get: %v", err)
}
defer resp.Body.Close()
switch {
case resp.StatusCode >= 400 && resp.StatusCode < 600:
// attempt to decode error from github
var authErr githubError
if err := json.NewDecoder(resp.Body).Decode(&authErr); err != nil {
return oidc.Identity{}, oauth2.NewError(oauth2.ErrorAccessDenied)
}
return oidc.Identity{}, authErr
case resp.StatusCode == http.StatusOK:
default:
return oidc.Identity{}, fmt.Errorf("unexpected status from providor %s", resp.Status)
}
var user struct {
Login string `json:"login"`
ID int64 `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
return oidc.Identity{}, fmt.Errorf("getting user info: %v", err)
}
name := user.Name
if name == "" {
name = user.Login
}
return oidc.Identity{
ID: strconv.FormatInt(user.ID, 10),
Name: name,
Email: user.Email,
}, nil
}
func (c *githubOAuth2Connector) Healthy() error {
return nil
}
func (c *githubOAuth2Connector) TrustedEmailProvider() bool {
return false
}