-
Notifications
You must be signed in to change notification settings - Fork 2
/
auth_client.go
98 lines (86 loc) · 3.53 KB
/
auth_client.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
package internal
import (
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"strconv"
"strings"
)
const (
XAuthServiceAuthResponseJWTHeader = "x-auth-jwt"
)
type AuthClient struct {
XRequestID string
Conf *Config
}
func (d *AuthClient) RequestJWT(origReqHeaders map[string]string) {
proxywasm.LogInfof("%s: Requesting JWT from Auth Service", d.XRequestID)
// Now actually call the Auth Service.
_, err := proxywasm.DispatchHttpCall(
d.Conf.AuthClusterName,
[][2]string{
{"accept", "*/*"},
{":authority", d.Conf.AuthAuthority},
{":method", "GET"},
{":path", "/base64/RkFLRV9KV1QK"}, // get Httpbin to return some fake data
{AuthHeader, origReqHeaders[AuthHeader]}, // Copy auth header from original request to auth against
},
nil,
nil,
d.Conf.AuthTimeout,
d.authCallback,
)
if err != nil {
proxywasm.LogCriticalf("%s: failed to call AuthService: %v", d.XRequestID, err)
// We want to resume the intercepted request even if we couldn't get an authentication header
_ = proxywasm.ResumeHttpRequest()
}
}
func (d *AuthClient) authCallback(_, _, _ int) {
proxywasm.LogInfof("%s: Got response from AuthService", d.XRequestID)
responseStatus := uint32(500)
// We want to always resume the intercepted request regardless of success/fail to avoid indefinitely blocking anything
defer func() {
if responseStatus != 200 {
responseErr := proxywasm.SendHttpResponse(responseStatus, [][2]string{{"generated-by", "My WASM plugin"}}, []byte("Failed to add JWT"), -1)
if responseErr == nil {
return // Need to skip calling ResumeHttpRequest to avoid sending this to upstream service
}
proxywasm.LogErrorf("%s: failed to send %d back to client: %v", d.XRequestID, responseStatus, responseErr)
}
if err := proxywasm.ResumeHttpRequest(); err != nil {
proxywasm.LogCriticalf("%s: failed to ResumeHttpRequest after calling auth: %v", d.XRequestID, err)
}
}()
// Get the response headers from our call to AuthService
headers, err := proxywasm.GetHttpCallResponseHeaders()
if err != nil {
proxywasm.LogCriticalf("%s: failed to GetHttpCallResponseHeaders from auth response: %v", d.XRequestID, err)
return
}
// Convert to map to make it easier to get specific headers
authResponseHeaders := headerArrayToMap(headers)
// Note we're using `:status` instead of just `status`. This is the same for any HTTP-transport-specific headers like ':method', ':path', ':authority', ...
// You don't need the ':' prefix for headers like 'user-agent', 'accept, ...
if authResponseHeaders[":status"] == "200" {
proxywasm.LogInfof("%s: AuthService gave successful (200) response", d.XRequestID)
body, err := proxywasm.GetHttpCallResponseBody(0, 1024)
if err != nil {
proxywasm.LogCriticalf("%s: failed to GetHttpCallResponseBody for auth response: %v", d.XRequestID, err)
return
}
jwt := strings.Trim(string(body), "\r\n")
proxywasm.LogInfof("%s: adding new header to original request: '%s=%s'", d.XRequestID, XAuthServiceAuthResponseJWTHeader, jwt)
if err := proxywasm.AddHttpRequestHeader(XAuthServiceAuthResponseJWTHeader, jwt); err != nil {
proxywasm.LogCriticalf("%s: failed to add header '%v' to request: %v", d.XRequestID, XAuthServiceAuthResponseJWTHeader, err)
return
}
responseStatus = 200
return
}
if len(authResponseHeaders[":status"]) > 0 {
status, err := strconv.ParseInt(authResponseHeaders[":status"], 10, 0)
if err == nil {
responseStatus = uint32(status)
}
}
proxywasm.LogErrorf("%s: AuthService failed this request - status:%s", d.XRequestID, authResponseHeaders[":status"])
}