chatai/auth_v2.169.0/internal/api/sms_provider/vonage.go

106 lines
2.6 KiB
Go

package sms_provider
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/supabase/auth/internal/conf"
"github.com/supabase/auth/internal/utilities"
"golang.org/x/exp/utf8string"
)
const (
defaultVonageApiBase = "https://rest.nexmo.com"
)
type VonageProvider struct {
Config *conf.VonageProviderConfiguration
APIPath string
}
type VonageResponseMessage struct {
MessageID string `json:"message-id"`
Status string `json:"status"`
ErrorText string `json:"error-text"`
}
type VonageResponse struct {
Messages []VonageResponseMessage `json:"messages"`
}
// Creates a SmsProvider with the Vonage Config
func NewVonageProvider(config conf.VonageProviderConfiguration) (SmsProvider, error) {
if err := config.Validate(); err != nil {
return nil, err
}
apiPath := defaultVonageApiBase + "/sms/json"
return &VonageProvider{
Config: &config,
APIPath: apiPath,
}, nil
}
func (t *VonageProvider) SendMessage(phone, message, channel, otp string) (string, error) {
switch channel {
case SMSProvider:
return t.SendSms(phone, message)
default:
return "", fmt.Errorf("channel type %q is not supported for Vonage", channel)
}
}
// Send an SMS containing the OTP with Vonage's API
func (t *VonageProvider) SendSms(phone string, message string) (string, error) {
body := url.Values{
"from": {t.Config.From},
"to": {phone},
"text": {message},
"api_key": {t.Config.ApiKey},
"api_secret": {t.Config.ApiSecret},
}
isMessageContainUnicode := !utf8string.NewString(message).IsASCII()
if isMessageContainUnicode {
body.Set("type", "unicode")
}
client := &http.Client{Timeout: defaultTimeout}
r, err := http.NewRequest("POST", t.APIPath, strings.NewReader(body.Encode()))
if err != nil {
return "", err
}
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
res, err := client.Do(r)
if err != nil {
return "", err
}
defer utilities.SafeClose(res.Body)
resp := &VonageResponse{}
derr := json.NewDecoder(res.Body).Decode(resp)
if derr != nil {
return "", derr
}
if len(resp.Messages) <= 0 {
return "", errors.New("vonage error: Internal Error")
}
// A status of zero indicates success; a non-zero value means something went wrong.
if resp.Messages[0].Status != "0" {
return resp.Messages[0].MessageID, fmt.Errorf("vonage error: %v (status: %v) for message %s", resp.Messages[0].ErrorText, resp.Messages[0].Status, resp.Messages[0].MessageID)
}
return resp.Messages[0].MessageID, nil
}
func (t *VonageProvider) VerifyOTP(phone, code string) error {
return fmt.Errorf("VerifyOTP is not supported for Vonage")
}