Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new SMS provider Textlocal #342

Merged
merged 7 commits into from
Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ Controls the number of digits of the sms otp sent.

`SMS_PROVIDER` - `string`

Available options are: `twilio`, `messagebird`, and `vonage`
Available options are: `twilio`, `messagebird`, `textlocal`, and `vonage`

Then you can use your [twilio credentials](https://www.twilio.com/docs/usage/requests-to-twilio#credentials):

Expand Down
2 changes: 2 additions & 0 deletions api/sms_provider/sms_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ func GetSmsProvider(config conf.Configuration) (SmsProvider, error) {
return NewTwilioProvider(config.Sms.Twilio)
case "messagebird":
return NewMessagebirdProvider(config.Sms.Messagebird)
case "textlocal":
return NewTextlocalProvider(config.Sms.Textlocal)
case "vonage":
return NewVonageProvider(config.Sms.Vonage)
default:
Expand Down
82 changes: 82 additions & 0 deletions api/sms_provider/textlocal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package sms_provider

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"

"github.com/netlify/gotrue/conf"
)

const (
defaultTextLocalApiBase = "https://api.textlocal.in"
)

type TextlocalProvider struct {
Config *conf.TextlocalProviderConfiguration
APIPath string
}

type TextlocalError struct {
Code int `json:"code"`
Message string `json:"message"`
}

type TextlocalResponse struct {
Status string `json:"status"`
Errors []TextlocalError `json:"errors"`
}

// Creates a SmsProvider with the Textlocal Config
func NewTextlocalProvider(config conf.TextlocalProviderConfiguration) (SmsProvider, error) {
if err := config.Validate(); err != nil {
return nil, err
}

apiPath := defaultTextLocalApiBase + "/send"
return &TextlocalProvider{
Config: &config,
APIPath: apiPath,
}, nil
}

// Send an SMS containing the OTP with Textlocal's API
func (t TextlocalProvider) SendSms(phone string, message string) error {
body := url.Values{
"sender": {t.Config.Sender},
"apikey": {t.Config.ApiKey},
"message": {message},
"numbers": {phone},
}

client := &http.Client{}
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 res.Body.Close()

resp := &TextlocalResponse{}
derr := json.NewDecoder(res.Body).Decode(resp)
if derr != nil {
return derr
}

if len(resp.Errors) == 0 {
return errors.New("Textlocal error: Internal Error")
devkiran marked this conversation as resolved.
Show resolved Hide resolved
}

if resp.Status != "success" {
return fmt.Errorf("Textlocal error: %v (code: %v)", resp.Errors[0].Message, resp.Errors[0].Code)
}

return nil
}
17 changes: 16 additions & 1 deletion conf/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ type SmsProviderConfiguration struct {
Template string `json:"template"`
Twilio TwilioProviderConfiguration `json:"twilio"`
Messagebird MessagebirdProviderConfiguration `json:"messagebird"`
Textlocal TextlocalProviderConfiguration `json:"textlocal"`
Vonage VonageProviderConfiguration `json:"vonage"`
}

Expand All @@ -146,6 +147,11 @@ type MessagebirdProviderConfiguration struct {
Originator string `json:"originator" split_words:"true"`
}

type TextlocalProviderConfiguration struct {
ApiKey string `json:"api_key" split_words:"true"`
Sender string `json:"sender" split_words:"true"`
}

type VonageProviderConfiguration struct {
ApiKey string `json:"api_key" split_words:"true"`
ApiSecret string `json:"api_secret" split_words:"true"`
Expand Down Expand Up @@ -375,6 +381,16 @@ func (t *MessagebirdProviderConfiguration) Validate() error {
return nil
}

func (t *TextlocalProviderConfiguration) Validate() error {
if t.ApiKey == "" {
return errors.New("Missing Textlocal API key")
}
if t.Sender == "" {
return errors.New("Missing Textlocal sender")
}
return nil
}

func (t *VonageProviderConfiguration) Validate() error {
if t.ApiKey == "" {
return errors.New("Missing Vonage API key")
Expand All @@ -385,6 +401,5 @@ func (t *VonageProviderConfiguration) Validate() error {
if t.From == "" {
return errors.New("Missing Vonage 'from' parameter")
}

return nil
}
2 changes: 2 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ GOTRUE_SMS_TWILIO_MESSAGE_SERVICE_SID=""
GOTRUE_SMS_TEMPLATE="This is from supabase. Your code is {{ .Code }} ."
GOTRUE_SMS_MESSAGEBIRD_ACCESS_KEY=""
GOTRUE_SMS_MESSAGEBIRD_ORIGINATOR=""
GOTRUE_SMS_TEXTLOCAL_API_KEY=""
GOTRUE_SMS_TEXTLOCAL_SENDER=""
GOTRUE_SMS_VONAGE_API_KEY=""
GOTRUE_SMS_VONAGE_API_SECRET=""
GOTRUE_SMS_VONAGE_FROM=""
Expand Down