2
2
Package signature_jwt implements signature verification for MessageBird webhooks.
3
3
4
4
To use define a new validator using your MessageBird Signing key. Can be
5
- retrieved through https://dashboard.messagebird.com/developers/settings.
5
+ retrieved from https://dashboard.messagebird.com/developers/settings.
6
6
This is NOT your API key.
7
7
8
8
You can use the ValidateRequest method, just pass the request and base url as parameters:
@@ -18,6 +18,8 @@ Or use the handler as a middleware for your server:
18
18
http.Handle("/path", validator.Validate(YourHandler, baseUrl))
19
19
20
20
It will reject the requests that contain invalid signatures.
21
+
22
+ For more information, see https://developers.messagebird.com/docs/verify-http-requests
21
23
*/
22
24
package signature_jwt
23
25
@@ -53,26 +55,53 @@ var allowedMethods = []string{
53
55
type Validator struct {
54
56
parser jwt.Parser
55
57
keyFn jwt.Keyfunc
58
+
59
+ skipURLValidation bool
60
+ }
61
+
62
+ type ValidatorOption func (* Validator )
63
+
64
+ // SkipURLValidation instructs Validator to not validate url_hash claim.
65
+ // It is recommended to not skip URL validation to ensure high security.
66
+ // but the ability to skip URL validation is necessary in some cases, e.g.
67
+ // your service is behind proxy or when you want to validate it yourself.
68
+ // Note that if enabled, no query parameters should be trusted.
69
+ func SkipURLValidation () ValidatorOption {
70
+ return func (c * Validator ) {
71
+ c .skipURLValidation = true
72
+ }
56
73
}
57
74
58
75
// NewValidator returns a signature validator object.
59
- func NewValidator (signingKey string ) * Validator {
60
- return & Validator {
76
+ // Signing key can be retrieved from
77
+ // https://dashboard.messagebird.com/developers/settings.
78
+ // Note that this is NOT your API key.
79
+ func NewValidator (signingKey string , opts ... ValidatorOption ) * Validator {
80
+ validator := & Validator {
61
81
parser : jwt.Parser {
62
82
ValidMethods : allowedMethods ,
63
83
},
64
84
keyFn : func (* jwt.Token ) (interface {}, error ) { return []byte (signingKey ), nil },
65
85
}
86
+
87
+ for _ , opt := range opts {
88
+ opt (validator )
89
+ }
90
+
91
+ return validator
66
92
}
67
93
68
- // ValidateSignature is a method that takes care of the signature validation of
69
- // incoming requests.
70
- func (v * Validator ) ValidateSignature (signature , url string , payload []byte ) (* jwt.Token , error ) {
94
+ // ValidateSignature returns the signature token claims when the signature
95
+ // is validated successfully. Otherwise, an error is returned.
96
+ // The provided url is the raw url including the protocol, hostname and
97
+ // query string, e.g. https://example.com/?example=42.
98
+ func (v * Validator ) ValidateSignature (signature , url string , payload []byte ) (jwt.Claims , error ) {
71
99
claims := Claims {
72
- receivedTime : TimeFunc (),
100
+ receivedTime : TimeFunc (),
101
+ skipURLValidation : v .skipURLValidation ,
73
102
}
74
103
75
- if url != "" {
104
+ if ! v . skipURLValidation && url != "" {
76
105
claims .correctURLHash = sha256Hash ([]byte (url ))
77
106
}
78
107
if payload != nil && len (payload ) != 0 {
@@ -82,7 +111,7 @@ func (v *Validator) ValidateSignature(signature, url string, payload []byte) (*j
82
111
if token , err := v .parser .ParseWithClaims (signature , & claims , v .keyFn ); err != nil {
83
112
return nil , fmt .Errorf ("invalid jwt: %w" , err )
84
113
} else {
85
- return token , nil
114
+ return token . Claims , nil
86
115
}
87
116
}
88
117
@@ -95,7 +124,7 @@ func (v *Validator) ValidateRequest(r *http.Request, baseURL string) error {
95
124
}
96
125
97
126
var fullURL string
98
- if baseURL != "" {
127
+ if ! v . skipURLValidation && baseURL != "" {
99
128
base , err := url .Parse (baseURL )
100
129
if err != nil {
101
130
return fmt .Errorf ("error parsing base url: %v" , err )
0 commit comments