forked from evcc-io/evcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
redirect.go
99 lines (79 loc) · 2.08 KB
/
redirect.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
package oauth2redirect
import (
"crypto/rand"
"fmt"
"io"
"net/http"
"sync"
"github.com/connectorjs/evcm/util"
"github.com/gorilla/mux"
)
var instance *Handler
// Handler manages a dynamic map of routes for handling the redirect during
// OAuth authentication. When a route is registered a token OAuth state is returned.
// On GET request the generic handler identifies route and target handler
// by request state obtained from the request and delegates to the registered handler.
type Handler struct {
mu sync.Mutex
secret []byte
routes map[string]http.HandlerFunc
}
func generateSecret() ([]byte, error) {
var b [16]byte
_, err := io.ReadFull(rand.Reader, b[:])
return b[:], err
}
func init() {
secret, err := generateSecret()
if err != nil {
panic(err)
}
instance = &Handler{
secret: secret,
routes: make(map[string]http.HandlerFunc),
}
}
// SetupRouter connects the redirect handler to the router
func SetupRouter(router *mux.Router) {
router.Methods(http.MethodGet).HandlerFunc(instance.handle)
}
// Register registers a specific handler with the redirect handler
func Register(handler http.HandlerFunc) string {
return instance.register(handler)
}
func (a *Handler) register(handler http.HandlerFunc) string {
a.mu.Lock()
defer a.mu.Unlock()
state := util.NewState()
key := state.Encrypt(a.secret)
a.routes[key] = handler
return key
}
func (a *Handler) handle(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
if q.Has("error") {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "error: %s: %s\n", q.Get("error"), q.Get("error_description"))
return
}
state, err := util.DecryptState(q.Get("state"), a.secret)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "failed to decrypt state")
return
}
if err := state.Validate(); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "invalid state")
return
}
a.mu.Lock()
handler := a.routes[q.Get("state")]
a.mu.Unlock()
if handler == nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "no handler found")
return
}
handler(w, r)
}