116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package sms_provider
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/supabase/auth/internal/conf"
|
|
"github.com/supabase/auth/internal/utilities"
|
|
)
|
|
|
|
const (
|
|
defaultMessagebirdApiBase = "https://rest.messagebird.com"
|
|
)
|
|
|
|
type MessagebirdProvider struct {
|
|
Config *conf.MessagebirdProviderConfiguration
|
|
APIPath string
|
|
}
|
|
|
|
type MessagebirdResponseRecipients struct {
|
|
TotalSentCount int `json:"totalSentCount"`
|
|
}
|
|
|
|
type MessagebirdResponse struct {
|
|
ID string `json:"id"`
|
|
Recipients MessagebirdResponseRecipients `json:"recipients"`
|
|
}
|
|
|
|
type MessagebirdError struct {
|
|
Code int `json:"code"`
|
|
Description string `json:"description"`
|
|
Parameter string `json:"parameter"`
|
|
}
|
|
|
|
type MessagebirdErrResponse struct {
|
|
Errors []MessagebirdError `json:"errors"`
|
|
}
|
|
|
|
func (t MessagebirdErrResponse) Error() string {
|
|
return t.Errors[0].Description
|
|
}
|
|
|
|
// Creates a SmsProvider with the Messagebird Config
|
|
func NewMessagebirdProvider(config conf.MessagebirdProviderConfiguration) (SmsProvider, error) {
|
|
if err := config.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
apiPath := defaultMessagebirdApiBase + "/messages"
|
|
return &MessagebirdProvider{
|
|
Config: &config,
|
|
APIPath: apiPath,
|
|
}, nil
|
|
}
|
|
|
|
func (t *MessagebirdProvider) 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 Messagebird", channel)
|
|
}
|
|
}
|
|
|
|
// Send an SMS containing the OTP with Messagebird's API
|
|
func (t *MessagebirdProvider) SendSms(phone string, message string) (string, error) {
|
|
body := url.Values{
|
|
"originator": {t.Config.Originator},
|
|
"body": {message},
|
|
"recipients": {phone},
|
|
"type": {"sms"},
|
|
"datacoding": {"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")
|
|
r.Header.Add("Authorization", "AccessKey "+t.Config.AccessKey)
|
|
res, err := client.Do(r)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if res.StatusCode == http.StatusBadRequest || res.StatusCode == http.StatusForbidden || res.StatusCode == http.StatusUnauthorized || res.StatusCode == http.StatusUnprocessableEntity {
|
|
resp := &MessagebirdErrResponse{}
|
|
if err := json.NewDecoder(res.Body).Decode(resp); err != nil {
|
|
return "", err
|
|
}
|
|
return "", resp
|
|
}
|
|
defer utilities.SafeClose(res.Body)
|
|
|
|
// validate sms status
|
|
resp := &MessagebirdResponse{}
|
|
derr := json.NewDecoder(res.Body).Decode(resp)
|
|
if derr != nil {
|
|
return "", derr
|
|
}
|
|
|
|
if resp.Recipients.TotalSentCount == 0 {
|
|
return "", fmt.Errorf("messagebird error: total sent count is 0")
|
|
}
|
|
|
|
return resp.ID, nil
|
|
}
|
|
|
|
func (t *MessagebirdProvider) VerifyOTP(phone, code string) error {
|
|
return fmt.Errorf("VerifyOTP is not supported for Messagebird")
|
|
}
|