forked from qvest-digital/loginsrv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
manager.go
156 lines (129 loc) · 3.79 KB
/
manager.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
146
147
148
149
150
151
152
153
154
155
156
package oauth2
import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/kernle32dll/loginsrv/model"
)
// Manager has the responsibility to handle the user user requests in an oauth flow.
// It has to pick the right configuration and start the oauth redirecting.
type Manager struct {
configs map[string]Config
startFlow func(cfg Config, w http.ResponseWriter) error
authenticate func(cfg Config, r *http.Request) (TokenInfo, error)
}
// NewManager creates a new Manager
func NewManager() *Manager {
return &Manager{
configs: map[string]Config{},
startFlow: StartFlow,
authenticate: Authenticate,
}
}
// Handle is managing the oauth flow.
// Dependent on the code parameter of the url, the oauth flow is started or
// the call is interpreted as the redirect callback and the token exchange is done.
// Return parameters:
// startedFlow - true, if this was the initial call to start the oauth flow
// authenticated - if the authentication was successful or not
// userInfo - the user info from the provider in case of a successful authentication
// err - an error
func (manager *Manager) Handle(w http.ResponseWriter, r *http.Request) (
startedFlow bool,
authenticated bool,
userInfo model.UserInfo,
err error) {
if r.FormValue("error") != "" {
return false, false, model.UserInfo{}, fmt.Errorf("error: %v", r.FormValue("error"))
}
cfg, err := manager.GetConfigFromRequest(r)
if err != nil {
return false, false, model.UserInfo{}, err
}
if r.FormValue("code") != "" {
tokenInfo, err := manager.authenticate(cfg, r)
if err != nil {
return false, false, model.UserInfo{}, err
}
userInfo, _, err := cfg.Provider.GetUserInfo(tokenInfo)
if err != nil {
return false, false, model.UserInfo{}, err
}
return false, true, userInfo, err
}
manager.startFlow(cfg, w)
return true, false, model.UserInfo{}, nil
}
// GetConfigFromRequest returns the oauth configuration matching the current path.
// The configuration name is taken from the last path segment.
func (manager *Manager) GetConfigFromRequest(r *http.Request) (Config, error) {
configName := manager.getConfigNameFromPath(r.URL.Path)
cfg, exist := manager.configs[configName]
if !exist {
return Config{}, fmt.Errorf("no oauth configuration for %v", configName)
}
if cfg.RedirectURI == "" {
cfg.RedirectURI = redirectURIFromRequest(r)
}
return cfg, nil
}
func (manager *Manager) getConfigNameFromPath(path string) string {
parts := strings.Split(path, "/")
return parts[len(parts)-1]
}
// AddConfig for a provider
func (manager *Manager) AddConfig(providerName string, opts map[string]string) error {
p, exist := GetProvider(providerName)
if !exist {
return fmt.Errorf("no provider for name %v", providerName)
}
cfg := Config{
Provider: p,
AuthURL: p.AuthURL,
TokenURL: p.TokenURL,
}
clientID, exist := opts["client_id"]
if !exist {
return fmt.Errorf("missing parameter client_id")
}
cfg.ClientID = clientID
clientSecret, exist := opts["client_secret"]
if !exist {
return fmt.Errorf("missing parameter client_secret")
}
cfg.ClientSecret = clientSecret
if scope, exist := opts["scope"]; exist {
cfg.Scope = scope
} else {
cfg.Scope = p.DefaultScopes
}
if redirectURI, exist := opts["redirect_uri"]; exist {
cfg.RedirectURI = redirectURI
}
manager.configs[providerName] = cfg
return nil
}
// GetConfigs of the manager
func (manager *Manager) GetConfigs() map[string]Config {
return manager.configs
}
func redirectURIFromRequest(r *http.Request) string {
u := url.URL{}
u.Path = r.URL.Path
if ffh := r.Header.Get("X-Forwarded-Host"); ffh == "" {
u.Host = r.Host
} else {
u.Host = ffh
}
if ffp := r.Header.Get("X-Forwarded-Proto"); ffp == "" {
if r.TLS != nil {
u.Scheme = "https"
} else {
u.Scheme = "http"
}
} else {
u.Scheme = ffp
}
return u.String()
}