Add call verification

This commit is contained in:
binwiederhier 2023-05-16 22:27:48 -04:00
parent 496d6e74b0
commit 2c81773d01
11 changed files with 93 additions and 74 deletions

View file

@ -112,6 +112,7 @@ var (
errHTTPBadRequestPhoneNumberInvalid = &errHTTP{40033, http.StatusBadRequest, "invalid request: phone number invalid", "https://ntfy.sh/docs/publish/#phone-calls", nil}
errHTTPBadRequestPhoneNumberNotVerified = &errHTTP{40034, http.StatusBadRequest, "invalid request: phone number not verified, or no matching verified numbers found", "https://ntfy.sh/docs/publish/#phone-calls", nil}
errHTTPBadRequestAnonymousCallsNotAllowed = &errHTTP{40035, http.StatusBadRequest, "invalid request: anonymous phone calls are not allowed", "https://ntfy.sh/docs/publish/#phone-calls", nil}
errHTTPBadRequestPhoneNumberVerifyChannelInvalid = &errHTTP{40036, http.StatusBadRequest, "invalid request: verification channel must be 'sms' or 'call'", "https://ntfy.sh/docs/publish/#phone-calls", nil}
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}

View file

@ -523,12 +523,13 @@ func (s *Server) maybeRemoveMessagesAndExcessReservations(r *http.Request, v *vi
func (s *Server) handleAccountPhoneNumberVerify(w http.ResponseWriter, r *http.Request, v *visitor) error {
u := v.User()
req, err := readJSONWithLimit[apiAccountPhoneNumberRequest](r.Body, jsonBodyBytesLimit, false)
req, err := readJSONWithLimit[apiAccountPhoneNumberVerifyRequest](r.Body, jsonBodyBytesLimit, false)
if err != nil {
return err
}
if !phoneNumberRegex.MatchString(req.Number) {
} else if !phoneNumberRegex.MatchString(req.Number) {
return errHTTPBadRequestPhoneNumberInvalid
} else if req.Channel != "sms" && req.Channel != "call" {
return errHTTPBadRequestPhoneNumberVerifyChannelInvalid
}
// Check user is allowed to add phone numbers
if u == nil || (u.IsUser() && u.Tier == nil) {
@ -545,7 +546,7 @@ func (s *Server) handleAccountPhoneNumberVerify(w http.ResponseWriter, r *http.R
}
// Actually add the unverified number, and send verification
logvr(v, r).Tag(tagAccount).Field("phone_number", req.Number).Debug("Sending phone number verification")
if err := s.verifyPhoneNumber(v, r, req.Number); err != nil {
if err := s.verifyPhoneNumber(v, r, req.Number, req.Channel); err != nil {
return err
}
return s.writeJSON(w, newSuccessResponse())
@ -553,7 +554,7 @@ func (s *Server) handleAccountPhoneNumberVerify(w http.ResponseWriter, r *http.R
func (s *Server) handleAccountPhoneNumberAdd(w http.ResponseWriter, r *http.Request, v *visitor) error {
u := v.User()
req, err := readJSONWithLimit[apiAccountPhoneNumberRequest](r.Body, jsonBodyBytesLimit, false)
req, err := readJSONWithLimit[apiAccountPhoneNumberAddRequest](r.Body, jsonBodyBytesLimit, false)
if err != nil {
return err
}
@ -572,7 +573,7 @@ func (s *Server) handleAccountPhoneNumberAdd(w http.ResponseWriter, r *http.Requ
func (s *Server) handleAccountPhoneNumberDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
u := v.User()
req, err := readJSONWithLimit[apiAccountPhoneNumberRequest](r.Body, jsonBodyBytesLimit, false)
req, err := readJSONWithLimit[apiAccountPhoneNumberAddRequest](r.Body, jsonBodyBytesLimit, false)
if err != nil {
return err
}

View file

@ -18,13 +18,14 @@ const (
<Response>
<Pause length="1"/>
<Say loop="3">
You have a notification from notify on topic %s. Message:
You have a message from notify on topic %s. Message:
<break time="1s"/>
%s
<break time="1s"/>
End message.
End of message.
<break time="1s"/>
This message was sent by user %s. It will be repeated up to three times.
This message was sent by user %s. It will be repeated three times.
To unsubscribe from calls like this, remove your phone number in the notify web app.
<break time="3s"/>
</Say>
<Say>Goodbye.</Say>
@ -97,11 +98,11 @@ func (s *Server) callPhoneInternal(data url.Values) (string, error) {
return string(response), nil
}
func (s *Server) verifyPhoneNumber(v *visitor, r *http.Request, phoneNumber string) error {
ev := logvr(v, r).Tag(tagTwilio).Field("twilio_to", phoneNumber).Debug("Sending phone verification")
func (s *Server) verifyPhoneNumber(v *visitor, r *http.Request, phoneNumber, channel string) error {
ev := logvr(v, r).Tag(tagTwilio).Field("twilio_to", phoneNumber).Field("twilio_channel", channel).Debug("Sending phone verification")
data := url.Values{}
data.Set("To", phoneNumber)
data.Set("Channel", "sms")
data.Set("Channel", channel)
requestURL := fmt.Sprintf("%s/v2/Services/%s/Verifications", s.config.TwilioVerifyBaseURL, s.config.TwilioVerifyService)
req, err := http.NewRequest(http.MethodPost, requestURL, strings.NewReader(data.Encode()))
if err != nil {

View file

@ -311,9 +311,14 @@ type apiAccountTokenResponse struct {
Expires int64 `json:"expires,omitempty"` // Unix timestamp
}
type apiAccountPhoneNumberRequest struct {
type apiAccountPhoneNumberVerifyRequest struct {
Number string `json:"number"`
Channel string `json:"channel"`
}
type apiAccountPhoneNumberAddRequest struct {
Number string `json:"number"`
Code string `json:"code,omitempty"` // Only supplied in "verify" call
Code string `json:"code,omitempty"`
}
type apiAccountTier struct {