diff --git a/config/config.go b/config/config.go index fdec57f601..bc29695435 100644 --- a/config/config.go +++ b/config/config.go @@ -97,8 +97,10 @@ type Config struct { Twilio struct { Enable bool `public:"true" info:"Enables sending and processing of Voice and SMS messages through the Twilio notification provider."` - AccountSID string - AuthToken string `password:"true" info:"The primary Auth Token for Twilio. Must be primary (not secondary) for request valiation."` + AccountSID string + AuthToken string `password:"true" info:"The primary Auth Token for Twilio. Must be primary unless Alternate Auth Token is set. This token is used for outgoing requests."` + AlternateAuthToken string `password:"true" info:"An alternate Auth Token for validating incoming requests. During a key change, set this to the Primary, and Auth Token to the Secondary, then promote and clear this field."` + FromNumber string `public:"true" info:"The Twilio number to use for outgoing notifications."` MessagingServiceSID string `public:"true" info:"If set, replaces the use of From Number for SMS notifications."` @@ -428,6 +430,7 @@ func (cfg Config) Validate() error { validateKey("Slack.ClientSecret", cfg.Slack.ClientSecret), validateKey("Twilio.AccountSID", cfg.Twilio.AccountSID), validateKey("Twilio.AuthToken", cfg.Twilio.AuthToken), + validateKey("Twilio.AlternateAuthToken", cfg.Twilio.AlternateAuthToken), validateKey("GitHub.ClientID", cfg.GitHub.ClientID), validateKey("GitHub.ClientSecret", cfg.GitHub.ClientSecret), validateKey("Slack.AccessToken", cfg.Slack.AccessToken), diff --git a/graphql2/mapconfig.go b/graphql2/mapconfig.go index b22b18b021..85a8bd35fe 100644 --- a/graphql2/mapconfig.go +++ b/graphql2/mapconfig.go @@ -67,7 +67,8 @@ func MapConfigValues(cfg config.Config) []ConfigValue { {ID: "Slack.InteractiveMessages", Type: ConfigTypeBoolean, Description: "Enable interactive messages (e.g. buttons).", Value: fmt.Sprintf("%t", cfg.Slack.InteractiveMessages)}, {ID: "Twilio.Enable", Type: ConfigTypeBoolean, Description: "Enables sending and processing of Voice and SMS messages through the Twilio notification provider.", Value: fmt.Sprintf("%t", cfg.Twilio.Enable)}, {ID: "Twilio.AccountSID", Type: ConfigTypeString, Description: "", Value: cfg.Twilio.AccountSID}, - {ID: "Twilio.AuthToken", Type: ConfigTypeString, Description: "The primary Auth Token for Twilio. Must be primary (not secondary) for request valiation.", Value: cfg.Twilio.AuthToken, Password: true}, + {ID: "Twilio.AuthToken", Type: ConfigTypeString, Description: "The primary Auth Token for Twilio. Must be primary unless Alternate Auth Token is set. This token is used for outgoing requests.", Value: cfg.Twilio.AuthToken, Password: true}, + {ID: "Twilio.AlternateAuthToken", Type: ConfigTypeString, Description: "An alternate Auth Token for validating incoming requests. During a key change, set this to the Primary, and Auth Token to the Secondary, then promote and clear this field.", Value: cfg.Twilio.AlternateAuthToken, Password: true}, {ID: "Twilio.FromNumber", Type: ConfigTypeString, Description: "The Twilio number to use for outgoing notifications.", Value: cfg.Twilio.FromNumber}, {ID: "Twilio.MessagingServiceSID", Type: ConfigTypeString, Description: "If set, replaces the use of From Number for SMS notifications.", Value: cfg.Twilio.MessagingServiceSID}, {ID: "Twilio.DisableTwoWaySMS", Type: ConfigTypeBoolean, Description: "Disables SMS reply codes for alert messages.", Value: fmt.Sprintf("%t", cfg.Twilio.DisableTwoWaySMS)}, @@ -299,6 +300,8 @@ func ApplyConfigValues(cfg config.Config, vals []ConfigValueInput) (config.Confi cfg.Twilio.AccountSID = v.Value case "Twilio.AuthToken": cfg.Twilio.AuthToken = v.Value + case "Twilio.AlternateAuthToken": + cfg.Twilio.AlternateAuthToken = v.Value case "Twilio.FromNumber": cfg.Twilio.FromNumber = v.Value case "Twilio.MessagingServiceSID": diff --git a/notification/twilio/validation.go b/notification/twilio/validation.go index 854c8fa545..950b62399d 100644 --- a/notification/twilio/validation.go +++ b/notification/twilio/validation.go @@ -25,7 +25,14 @@ func validateRequest(req *http.Request) error { calcSig := Signature(cfg.Twilio.AuthToken, config.RequestURL(req), req.PostForm) if !hmac.Equal([]byte(sig), calcSig) { - return errors.New("invalid X-Twilio-Signature") + if cfg.Twilio.AlternateAuthToken == "" { + return errors.New("invalid X-Twilio-Signature") + } + + calcSig = Signature(cfg.Twilio.AlternateAuthToken, config.RequestURL(req), req.PostForm) + if !hmac.Equal([]byte(sig), calcSig) { + return errors.New("invalid X-Twilio-Signature") + } } return nil diff --git a/web/src/schema.d.ts b/web/src/schema.d.ts index ce6ed69b77..f5d18276fc 100644 --- a/web/src/schema.d.ts +++ b/web/src/schema.d.ts @@ -1032,6 +1032,7 @@ type ConfigID = | 'Twilio.Enable' | 'Twilio.AccountSID' | 'Twilio.AuthToken' + | 'Twilio.AlternateAuthToken' | 'Twilio.FromNumber' | 'Twilio.MessagingServiceSID' | 'Twilio.DisableTwoWaySMS'