From 3bd802bc6a4b07414907910e25f0c01b4682b32b Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 10 Oct 2024 16:40:40 +0800 Subject: [PATCH 1/2] Add APIKey and APIKeySecret to Twilio --- pkg/lib/config/sms_provider.go | 40 +++++++-- .../testdata/authgear-sms-gateway.tests.yaml | 83 ++++++++++++++++++- 2 files changed, 113 insertions(+), 10 deletions(-) diff --git a/pkg/lib/config/sms_provider.go b/pkg/lib/config/sms_provider.go index 2f4a03a..8cb2a7f 100644 --- a/pkg/lib/config/sms_provider.go +++ b/pkg/lib/config/sms_provider.go @@ -36,10 +36,16 @@ type Provider struct { } type ProviderConfigTwilio struct { + AccountSID string `json:"account_sid,omitempty"` + + // From and MessagingServiceSID are mutually exclusive. From string `json:"from,omitempty"` - AccountSID string `json:"account_sid,omitempty"` - AuthToken string `json:"auth_token,omitempty"` MessagingServiceSID string `json:"messaging_service_sid,omitempty"` + + // AuthToken and (APIKey and APIKeySecret) are mutually exclusive. + AuthToken string `json:"auth_token,omitempty"` + APIKey string `json:"api_key,omitempty"` + APIKeySecret string `json:"api_key_secret,omitempty"` } type ProviderConfigAccessYou struct { @@ -83,18 +89,34 @@ var _ = RootSchema.Add("ProviderConfigTwilio", ` "type": "object", "additionalProperties": false, "properties": { - "from": { "type": "string" }, "account_sid": { "type": "string" }, - "auth_token": {"type": "string"}, - "messaging_service_sid": {"type": "string"} + "from": { "type": "string" }, + "messaging_service_sid": { "type": "string" }, + "auth_token": { "type": "string" }, + "api_key": { "type": "string" }, + "api_key_secret": { "type": "string" } }, - "required": ["account_sid", "auth_token"], - "oneOf": [ + "required": ["account_sid"], + "allOf": [ { - "required": ["from"] + "oneOf": [ + { + "required": ["from"] + }, + { + "required": ["messaging_service_sid"] + } + ] }, { - "required": ["messaging_service_sid"] + "oneOf": [ + { + "required": ["auth_token"] + }, + { + "required": ["api_key", "api_key_secret"] + } + ] } ] } diff --git a/pkg/lib/config/testdata/authgear-sms-gateway.tests.yaml b/pkg/lib/config/testdata/authgear-sms-gateway.tests.yaml index 0f6630c..b96e823 100644 --- a/pkg/lib/config/testdata/authgear-sms-gateway.tests.yaml +++ b/pkg/lib/config/testdata/authgear-sms-gateway.tests.yaml @@ -214,7 +214,7 @@ config: - type: default use_provider: twilio --- -name: failed-twilio-config +name: twilio-both-from-and-messaging-service-sid-present error: |- invalid configuration: /providers/0/twilio: oneOf @@ -232,6 +232,87 @@ config: - type: default use_provider: twilio --- +name: twilio-both-auth-token-and-api-key-present +error: |- + invalid configuration: + /providers/0/twilio: oneOf +config: + providers: + - name: twilio + type: twilio + twilio: + account_sid: "my-account-sid" + messaging_service_sid: "my-messaging-service-sid" + auth_token: "my-auth-token" + api_key: "api-key" + api_key_secret: "api-key-secret" + provider_selector: + switch: + - type: default + use_provider: twilio +--- +name: twilio-auth-token-from +error: null +config: + providers: + - name: twilio + type: twilio + twilio: + account_sid: "my-account-sid" + from: "sender" + auth_token: "my-auth-token" + provider_selector: + switch: + - type: default + use_provider: twilio +--- +name: twilio-auth-token-messaging-service-sid +error: null +config: + providers: + - name: twilio + type: twilio + twilio: + account_sid: "my-account-sid" + messaging_service_sid: "messaging_service_sid" + auth_token: "my-auth-token" + provider_selector: + switch: + - type: default + use_provider: twilio +--- +name: twilio-api-key-from +error: null +config: + providers: + - name: twilio + type: twilio + twilio: + account_sid: "my-account-sid" + from: "sender" + api_key: "api-key" + api_key_secret: "api-key-secret" + provider_selector: + switch: + - type: default + use_provider: twilio +--- +name: twilio-api-key-messaging-service-sid +error: null +config: + providers: + - name: twilio + type: twilio + twilio: + account_sid: "my-account-sid" + messaging_service_sid: "messaging-service-sid" + api_key: "api-key" + api_key_secret: "api-key-secret" + provider_selector: + switch: + - type: default + use_provider: twilio +--- name: missing-required-fields-in-switch-cases error: |- invalid configuration: From 60ad075b308db4a3db278f5509bafd3bc70fade4 Mon Sep 17 00:00:00 2001 From: Louis Chan Date: Thu, 10 Oct 2024 16:47:35 +0800 Subject: [PATCH 2/2] Support Twilio API key and API key secret --- pkg/lib/sms/smsclient.go | 18 +++++++++------- pkg/lib/sms/twilio/smsclient.go | 37 ++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/pkg/lib/sms/smsclient.go b/pkg/lib/sms/smsclient.go index aabcd7e..8efd081 100644 --- a/pkg/lib/sms/smsclient.go +++ b/pkg/lib/sms/smsclient.go @@ -15,14 +15,16 @@ import ( func NewClientFromConfigProvider(p *config.Provider, httpClient *http.Client, logger *slog.Logger) smsclient.RawClient { switch p.Type { case config.ProviderTypeTwilio: - return twilio.NewTwilioClient( - httpClient, - p.Twilio.AccountSID, - p.Twilio.AuthToken, - p.Twilio.From, - p.Twilio.MessagingServiceSID, - logger, - ) + return &twilio.TwilioClient{ + Client: httpClient, + AccountSID: p.Twilio.AccountSID, + AuthToken: p.Twilio.AuthToken, + APIKey: p.Twilio.APIKey, + APIKeySecret: p.Twilio.APIKeySecret, + From: p.Twilio.From, + MessagingServiceSID: p.Twilio.MessagingServiceSID, + Logger: logger, + } case config.ProviderTypeAccessYou: return accessyou.NewAccessYouClient( httpClient, diff --git a/pkg/lib/sms/twilio/smsclient.go b/pkg/lib/sms/twilio/smsclient.go index 673ba8b..d8a1148 100644 --- a/pkg/lib/sms/twilio/smsclient.go +++ b/pkg/lib/sms/twilio/smsclient.go @@ -15,23 +15,18 @@ import ( ) type TwilioClient struct { - Client *http.Client - AccountSID string - AuthToken string + Client *http.Client + + AccountSID string + + AuthToken string + APIKey string + APIKeySecret string + From string MessagingServiceSID string - Logger *slog.Logger -} -func NewTwilioClient(httpClient *http.Client, accountSID string, authToken string, from string, messagingServiceSID string, logger *slog.Logger) *TwilioClient { - return &TwilioClient{ - Client: httpClient, - AccountSID: accountSID, - AuthToken: authToken, - From: from, - MessagingServiceSID: messagingServiceSID, - Logger: logger, - } + Logger *slog.Logger } func (t *TwilioClient) send(options *smsclient.SendOptions) ([]byte, *SendResponse, error) { @@ -56,7 +51,19 @@ func (t *TwilioClient) send(options *smsclient.SendOptions) ([]byte, *SendRespon requestBody := values.Encode() req, _ := http.NewRequest("POST", u.String(), strings.NewReader(requestBody)) - req.SetBasicAuth(t.AccountSID, t.AuthToken) + + // https://www.twilio.com/docs/usage/api#authenticate-with-http + if t.AuthToken != "" { + // When Auth Token is used, username is Account SID, and password is Auth Token. + req.SetBasicAuth(t.AccountSID, t.AuthToken) + } else if t.APIKey != "" { + // When API Key is used, username is API key, and password is API key secret. + req.SetBasicAuth(t.APIKey, t.APIKeySecret) + } else { //nolint: staticcheck + // Normally we should not reach here. + // But in case we do, we do not provide the auth header. + // And Twilio should returns an error response to us in this case. + } resp, err := t.Client.Do(req) if err != nil {