-
Notifications
You must be signed in to change notification settings - Fork 8
/
apikey.go
116 lines (107 loc) · 2.81 KB
/
apikey.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
package auth
import (
"fmt"
"net/http"
"os"
"strings"
jwt "github.com/dgrijalva/jwt-go"
"github.com/sergeyt/pandora/modules/config"
"github.com/sergeyt/pandora/modules/send"
log "github.com/sirupsen/logrus"
)
// RequireAPIKey middleware to check API key
func RequireAPIKey(next http.Handler) http.Handler {
hostname := config.Hostname()
allowedReferers := []string{
fmt.Sprintf("http://%s", hostname),
fmt.Sprintf("https://%s", hostname),
"http://localhost",
"https://localhost",
}
apiKeySecret := os.Getenv("API_KEY_SECRET")
if len(apiKeySecret) == 0 {
panic("API_KEY_SECRET is not defined")
}
getAPIKey := func(r *http.Request) string {
apiKey := r.URL.Query().Get("key")
if len(apiKey) > 0 {
return apiKey
}
return r.Header.Get("X-API-Key")
}
appSecret := ""
getSecret := func(token *jwt.Token) (interface{}, error) {
v, ok := token.Header["app_secret"]
if ok {
s, isStr := v.(string)
if !isStr {
return nil, fmt.Errorf("app_secret is string")
}
if len(s) == 0 {
return nil, fmt.Errorf("missing app_secret")
}
appSecret = s
result := []byte(apiKeySecret + s)
return result, nil
}
return nil, fmt.Errorf("app_secret is not defined")
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// code taken from allowed server can make calls without api key
from := r.Header.Get("Referer")
if len(from) > 0 {
for _, a := range allowedReferers {
if strings.HasPrefix(from, a) {
next.ServeHTTP(w, r)
return
}
}
}
apiKey := getAPIKey(r)
if len(apiKey) == 0 {
err := fmt.Errorf("missing API key")
log.Errorf(err.Error())
send.Error(w, err, http.StatusUnauthorized)
return
}
parser := new(jwt.Parser)
parser.SkipClaimsValidation = true
claims := jwt.MapClaims{}
token, err := parser.ParseWithClaims(apiKey, claims, getSecret)
if err != nil {
log.Errorf("jwt.Parser.ParseWithClaims fail: %v", err)
send.Error(w, err, http.StatusUnauthorized)
return
}
if !token.Valid {
err = fmt.Errorf("bad API key, invalid token")
log.Errorf(err.Error())
send.Error(w, err, http.StatusUnauthorized)
return
}
v, ok := claims["app_id"]
if !ok {
err = fmt.Errorf("bad API key, missing app_id")
log.Errorf(err.Error())
send.Error(w, err, http.StatusUnauthorized)
return
}
appID, ok := v.(string)
if !ok {
err = fmt.Errorf("bad API key, app_id is not string")
log.Errorf(err.Error())
send.Error(w, err, http.StatusUnauthorized)
return
}
// TODO check app id from database
knownID := os.Getenv("APP_ID")
knownSecret := os.Getenv("APP_SECRET")
if appID != knownID || appSecret != knownSecret {
err = fmt.Errorf("app %s is not registered", appID)
log.Errorf(err.Error())
send.Error(w, err, http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}