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

Support twilio api key #5039

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
45 changes: 41 additions & 4 deletions pkg/lib/config/secret_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,28 +258,65 @@ var _ = SecretConfigSchema.Add("TwilioCredentials", `
"type": "object",
"additionalProperties": false,
"properties": {
"credential_type": {
"type": "string",
"enum": ["api_key", "auth_token"]
},
"account_sid": { "type": "string" },
"auth_token": { "type": "string" },
"api_key_sid": { "type": "string" },
"api_key_secret": { "type": "string" },
"message_service_sid": { "type": "string" }
},
"required": ["account_sid", "auth_token"]
"required": ["account_sid"],
"allOf": [
{
"if": {
"properties": { "credential_type": { "const": "api_key" } },
"required": ["credential_type"]
},
"then": { "required": ["api_key_sid", "api_key_secret"] },
"else": { "required": ["auth_token"] }
}
]
}
`)

type TwilioCredentialType string

const (
TwilioCredentialTypeAPIKey TwilioCredentialType = "api_key"
TwilioCredentialTypeAuthToken TwilioCredentialType = "auth_token"
)

type TwilioCredentials struct {
AccountSID string `json:"account_sid,omitempty"`
AuthToken string `json:"auth_token,omitempty"`
MessagingServiceSID string `json:"message_service_sid,omitempty"`
// For write only, always use GetCredentialType to read the value
CredentialType *TwilioCredentialType `json:"credential_type,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename this to CredentialType_WriteOnly

AccountSID string `json:"account_sid,omitempty"`
AuthToken string `json:"auth_token,omitempty"`
APIKeySID string `json:"api_key_sid,omitempty"`
APIKeySecret string `json:"api_key_secret,omitempty"`
MessagingServiceSID string `json:"message_service_sid,omitempty"`
}

func (c *TwilioCredentials) SensitiveStrings() []string {
return []string{
c.AccountSID,
c.AuthToken,
c.APIKeySID,
c.APIKeySecret,
c.MessagingServiceSID,
}
}

func (c *TwilioCredentials) GetCredentialType() TwilioCredentialType {
if c.CredentialType == nil {
// Old data does not specify credential_type. Treat it as auth_token.
return TwilioCredentialTypeAuthToken
}
return *c.CredentialType
}

var _ = SecretConfigSchema.Add("NexmoCredentials", `
{
"type": "object",
Expand Down
12 changes: 9 additions & 3 deletions pkg/lib/config/secret_update_instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,9 +780,12 @@ type SMSProviderSecretsUpdateInstructionSetData struct {
}

type SMSProviderSecretsUpdateInstructionTwilioCredentials struct {
AccountSID string `json:"accountSID,omitempty"`
AuthToken string `json:"authToken,omitempty"`
MessagingServiceSID string `json:"messagingServiceSID,omitempty"`
CredentialType TwilioCredentialType `json:"credentialType,omitempty"`
AccountSID string `json:"accountSID,omitempty"`
AuthToken string `json:"authToken,omitempty"`
APIKeySID string `json:"apiKeySID,omitempty"`
APIKeySecret string `json:"apiKeySecret,omitempty"`
MessagingServiceSID string `json:"messagingServiceSID,omitempty"`
}

type SMSProviderSecretsUpdateInstructionCustomSMSProvider struct {
Expand Down Expand Up @@ -849,8 +852,11 @@ func (i *SMSProviderSecretsUpdateInstruction) set(currentConfig *SecretConfig) (

if i.SetData.TwilioCredentials != nil {
twilioCredentials := TwilioCredentials{
CredentialType: &i.SetData.TwilioCredentials.CredentialType,
AccountSID: i.SetData.TwilioCredentials.AccountSID,
AuthToken: i.SetData.TwilioCredentials.AuthToken,
APIKeySID: i.SetData.TwilioCredentials.APIKeySID,
APIKeySecret: i.SetData.TwilioCredentials.APIKeySecret,
MessagingServiceSID: i.SetData.TwilioCredentials.MessagingServiceSID,
}
err := upsert(TwilioCredentialsKey, twilioCredentials)
Expand Down
33 changes: 33 additions & 0 deletions pkg/lib/config/testdata/parse_secret_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,36 @@ config:
certificates:
- pem: "-----BEGIN CERTIFICATE-----\nMIIDejCCAmKgAwIBAgIgLKKTB6GZMFHZVUiFIq8LcNIr0p8HFHwKM6r5/BQ/un4w\nDQYJKoZIhvcNAQEFBQAwUDEJMAcGA1UEBhMAMQkwBwYDVQQKDAAxCTAHBgNVBAsM\nADENMAsGA1UEAwwEdGVzdDEPMA0GCSqGSIb3DQEJARYAMQ0wCwYDVQQDDAR0ZXN0\nMB4XDTI0MDgwODA2NTY0OFoXDTM0MDgwOTA2NTY0OFowQTEJMAcGA1UEBhMAMQkw\nBwYDVQQKDAAxCTAHBgNVBAsMADENMAsGA1UEAwwEdGVzdDEPMA0GCSqGSIb3DQEJ\nARYAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5zRfTtkaa7cIsQS+\nF1Dg25wPEvcjHsHcq598n+RzRJzfSLRtYwgEfs0VhyjHfo2O7KhNFh5cqdkEfzwA\nbfxtgVLvy3yUjTMFO0FnJqrO3dkGiOAl654XUlXb4rF8DF1sPnUdd9QEZaZHGV/8\nYuVOc3RV15jsr2jB9rra9//guAQ0CSP4XLJ5m9vf9nJILAHLryFIzDSgOVmhi4Ig\no59e9n3Hemavrta2C5Zj4cP6RNwuCV/i5lQOkzJIgksH9/EZCsR93DMEgkBS5oQQ\nrt9Bzlr03TNGW4n/CYKNULK/osqJd5r5g3zUaQZY2KAan+oSsEXvBjzYtrehN1dm\ndfbUEQIDAQABo08wTTAdBgNVHQ4EFgQUiXG6MG9PSB/clTIuzm8rW+8xLWkwHwYD\nVR0jBBgwFoAUiXG6MG9PSB/clTIuzm8rW+8xLWkwCwYDVR0RBAQwAoIAMA0GCSqG\nSIb3DQEBBQUAA4IBAQBTjdS9po3eEXukksMK6xBL3kQF1MEFUaWcgoN+h497lS9J\nXe1rmWpdZ1Aehp21GQmniRKU8uPLPRQKoX8Mhc/d3fHyv9u0YPns/2Wm8TBzxwHY\nV2KdXZfpBdN+Z5bBRbgtKxx1z2GBfB39S2WCakS9xK8f7fuQPLIZz8eq7so5T8Hm\nTU95acndEpnA0u6/MjbvXtZesTRZCewQw4CkcSLTCzB8dLG55UXHytnISWlCpuAx\n8svq/ryZIi5vhBQFO/hG9s2Q32VvfKt2ZW8qA+gvOxEVDfAEFekKokP0Taiz77Q2\nAVZxEXeABxJGtiMunQTr2q1tCrJQN0d08xlA5jXl\n-----END CERTIFICATE-----\n"
- pem: "-----BEGIN CERTIFICATE-----\nMIIDejCCAmKgAwIBAgIgLKKTB6GZMFHZVUiFIq8LcNIr0p8HFHwKM6r5/BQ/un4w\nDQYJKoZIhvcNAQEFBQAwUDEJMAcGA1UEBhMAMQkwBwYDVQQKDAAxCTAHBgNVBAsM\nADENMAsGA1UEAwwEdGVzdDEPMA0GCSqGSIb3DQEJARYAMQ0wCwYDVQQDDAR0ZXN0\nMB4XDTI0MDgwODA2NTY0OFoXDTM0MDgwOTA2NTY0OFowQTEJMAcGA1UEBhMAMQkw\nBwYDVQQKDAAxCTAHBgNVBAsMADENMAsGA1UEAwwEdGVzdDEPMA0GCSqGSIb3DQEJ\nARYAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5zRfTtkaa7cIsQS+\nF1Dg25wPEvcjHsHcq598n+RzRJzfSLRtYwgEfs0VhyjHfo2O7KhNFh5cqdkEfzwA\nbfxtgVLvy3yUjTMFO0FnJqrO3dkGiOAl654XUlXb4rF8DF1sPnUdd9QEZaZHGV/8\nYuVOc3RV15jsr2jB9rra9//guAQ0CSP4XLJ5m9vf9nJILAHLryFIzDSgOVmhi4Ig\no59e9n3Hemavrta2C5Zj4cP6RNwuCV/i5lQOkzJIgksH9/EZCsR93DMEgkBS5oQQ\nrt9Bzlr03TNGW4n/CYKNULK/osqJd5r5g3zUaQZY2KAan+oSsEXvBjzYtrehN1dm\ndfbUEQIDAQABo08wTTAdBgNVHQ4EFgQUiXG6MG9PSB/clTIuzm8rW+8xLWkwHwYD\nVR0jBBgwFoAUiXG6MG9PSB/clTIuzm8rW+8xLWkwCwYDVR0RBAQwAoIAMA0GCSqG\nSIb3DQEBBQUAA4IBAQBTjdS9po3eEXukksMK6xBL3kQF1MEFUaWcgoN+h497lS9J\nXe1rmWpdZ1Aehp21GQmniRKU8uPLPRQKoX8Mhc/d3fHyv9u0YPns/2Wm8TBzxwHY\nV2KdXZfpBdN+Z5bBRbgtKxx1z2GBfB39S2WCakS9xK8f7fuQPLIZz8eq7so5T8Hm\nTU95acndEpnA0u6/MjbvXtZesTRZCewQw4CkcSLTCzB8dLG55UXHytnISWlCpuAx\n8svq/ryZIi5vhBQFO/hG9s2Q32VvfKt2ZW8qA+gvOxEVDfAEFekKokP0Taiz77Q2\nAVZxEXeABxJGtiMunQTr2q1tCrJQN0d08xlA5jXl\n-----END CERTIFICATE-----\n"
---
name: sms-twilio-auth-token/valid-without-type
error: null
config:
secrets:
- data:
account_sid: abc
auth_token: abc
message_service_sid: abcd
key: sms.twilio
---
name: sms-twilio-auth-token/valid
error: null
config:
secrets:
- data:
credential_type: auth_token
account_sid: abc
auth_token: abc
message_service_sid: abcd
key: sms.twilio
---
name: sms-twilio-api-key/valid
error: null
config:
secrets:
- data:
credential_type: api_key
account_sid: abc
api_key_sid: abc
api_key_secret: abcd
message_service_sid: abcd
key: sms.twilio
11 changes: 11 additions & 0 deletions pkg/lib/infra/sms/client_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import (
)

type TwilioClientCredentials struct {
CredentialType *config.TwilioCredentialType
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the non-pointer type here.

AccountSID string
AuthToken string
APIKeySID string
APIKeySecret string
MessagingServiceSID string
}

Expand Down Expand Up @@ -198,9 +201,13 @@ func (r *ClientResolver) clientsFromAuthgearSecretsYAML() (*nexmo.NexmoClient, *
}

if r.AuthgearSecretsYAMLTwilioCredentials != nil {
credtyp := r.AuthgearSecretsYAMLTwilioCredentials.GetCredentialType()
twilioClientCredentials = &TwilioClientCredentials{
CredentialType: &credtyp,
AccountSID: r.AuthgearSecretsYAMLTwilioCredentials.AccountSID,
AuthToken: r.AuthgearSecretsYAMLTwilioCredentials.AuthToken,
APIKeySID: r.AuthgearSecretsYAMLTwilioCredentials.APIKeySID,
APIKeySecret: r.AuthgearSecretsYAMLTwilioCredentials.APIKeySecret,
MessagingServiceSID: r.AuthgearSecretsYAMLTwilioCredentials.MessagingServiceSID,
}
}
Expand Down Expand Up @@ -229,9 +236,13 @@ func (r *ClientResolver) clientsFromEnv() (*nexmo.NexmoClient, *NexmoClientCrede
}

if r.EnvironmentTwilioCredentials != (config.SMSGatewayEnvironmentTwilioCredentials{}) {
credtyp := config.TwilioCredentialTypeAuthToken
twilioClientCredentials = &TwilioClientCredentials{
CredentialType: &credtyp,
AccountSID: r.EnvironmentTwilioCredentials.AccountSID,
AuthToken: r.EnvironmentTwilioCredentials.AuthToken,
APIKeySID: "",
APIKeySecret: "",
MessagingServiceSID: r.EnvironmentTwilioCredentials.MessagingServiceSID,
}
}
Expand Down
8 changes: 6 additions & 2 deletions pkg/lib/infra/sms/twilio/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ func (t *TwilioClient) send0(ctx context.Context, opts smsapi.SendOptions) ([]by
req, _ := http.NewRequestWithContext(ctx, "POST", u.String(), strings.NewReader(requestBody))

// https://www.twilio.com/docs/usage/api#authenticate-with-http
if t.TwilioCredentials.AuthToken != "" {
switch t.TwilioCredentials.GetCredentialType() {
case config.TwilioCredentialTypeAuthToken:
// When Auth Token is used, username is Account SID, and password is Auth Token.
req.SetBasicAuth(t.TwilioCredentials.AccountSID, t.TwilioCredentials.AuthToken)
} else {
case config.TwilioCredentialTypeAPIKey:
// When API Key is used, username is API Key SID, and password is API Key Secret.
req.SetBasicAuth(t.TwilioCredentials.APIKeySID, t.TwilioCredentials.APIKeySecret)
default:
// 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.
Expand Down
21 changes: 21 additions & 0 deletions pkg/portal/graphql/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,37 @@ var samlSpSigningSecret = graphql.NewObject(graphql.ObjectConfig{
},
})

var twilioCredentialType = graphql.NewEnum(graphql.EnumConfig{
Name: "TwilioCredentialType",
Values: graphql.EnumValueConfigMap{
string(config.TwilioCredentialTypeAPIKey): &graphql.EnumValueConfig{
Value: config.TwilioCredentialTypeAPIKey,
},
string(config.TwilioCredentialTypeAuthToken): &graphql.EnumValueConfig{
Value: config.TwilioCredentialTypeAuthToken,
},
},
})

var smsProviderTwilioCredentials = graphql.NewObject(graphql.ObjectConfig{
Name: "SMSProviderTwilioCredentials",
Description: "Twilio credentials",
Fields: graphql.Fields{
"credentialType": &graphql.Field{
Type: graphql.NewNonNull(twilioCredentialType),
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to write a custom resolver that calls GetCredentialType()?

"accountSID": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"authToken": &graphql.Field{
Type: graphql.String,
},
"apiKeySID": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"apiKeySecret": &graphql.Field{
Type: graphql.String,
},
"messagingServiceSID": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
Expand Down
9 changes: 9 additions & 0 deletions pkg/portal/graphql/app_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,21 @@ var smsProviderSecretsSetDataInput = graphql.NewInputObject(graphql.InputObjectC
var smsProviderTwilioCredentialsInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SMSProviderTwilioCredentialsInput",
Fields: graphql.InputObjectConfigFieldMap{
"credentialType": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(twilioCredentialType),
},
"accountSID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"authToken": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
"apiKeySID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"apiKeySecret": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
"messagingServiceSID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
Expand Down
9 changes: 9 additions & 0 deletions pkg/portal/graphql/sms_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,21 @@ var smsProviderConfigurationInput = graphql.NewInputObject(graphql.InputObjectCo
var smsProviderConfigurationTwilioInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SMSProviderConfigurationTwilioInput",
Fields: graphql.InputObjectConfigFieldMap{
"credentialType": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(twilioCredentialType),
},
"accountSID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"authToken": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"apiKeySID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"apiKeySecret": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't authToken, apiKeySID, apiKeySecret all be nullable?

},
"messagingServiceSID": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
Expand Down
12 changes: 9 additions & 3 deletions pkg/portal/model/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,12 @@ type SAMLSpSigningSecrets struct {
}

type SMSProviderTwilioCredentials struct {
AccountSID string `json:"accountSID,omitempty"`
AuthToken *string `json:"authToken,omitempty"`
MessagingServiceSID string `json:"messagingServiceSID,omitempty"`
CredentialType config.TwilioCredentialType `json:"credentialType,omitempty"`
AccountSID string `json:"accountSID,omitempty"`
AuthToken *string `json:"authToken,omitempty"`
APIKeySID string `json:"apiKeySID,omitempty"`
APIKeySecret *string `json:"apiKeySecret,omitempty"`
MessagingServiceSID string `json:"messagingServiceSID,omitempty"`
}

type SMSProviderCustomSMSProviderConfigs struct {
Expand Down Expand Up @@ -268,11 +271,14 @@ func NewSecretConfig(secretConfig *config.SecretConfig, unmaskedSecrets []config
smsProviderSecrets := &SMSProviderSecrets{}
if twilioCredentials, ok := secretConfig.LookupData(config.TwilioCredentialsKey).(*config.TwilioCredentials); ok {
smsProviderSecrets.TwilioCredentials = &SMSProviderTwilioCredentials{
CredentialType: twilioCredentials.GetCredentialType(),
AccountSID: twilioCredentials.AccountSID,
APIKeySID: twilioCredentials.APIKeySID,
MessagingServiceSID: twilioCredentials.MessagingServiceSID,
}
if _, exist := unmaskedSecretsSet[config.TwilioCredentialsKey]; exist {
smsProviderSecrets.TwilioCredentials.AuthToken = &twilioCredentials.AuthToken
smsProviderSecrets.TwilioCredentials.APIKeySecret = &twilioCredentials.APIKeySecret
}

}
Expand Down
11 changes: 8 additions & 3 deletions pkg/portal/model/sms.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package model

import "github.com/authgear/authgear-server/pkg/lib/config"

type SMSProviderConfigurationInput struct {
Twilio *SMSProviderConfigurationTwilioInput `json:"twilio,omitempty"`
Webhook *SMSProviderConfigurationWebhookInput `json:"webhook,omitempty"`
Deno *SMSProviderConfigurationDenoInput `json:"deno,omitempty"`
}

type SMSProviderConfigurationTwilioInput struct {
AccountSID string `json:"accountSID,omitempty"`
AuthToken string `json:"authToken,omitempty"`
MessagingServiceSID *string `json:"messagingServiceSID,omitempty"`
CredentialType config.TwilioCredentialType `json:"credentialType,omitempty"`
AccountSID string `json:"accountSID,omitempty"`
AuthToken string `json:"authToken,omitempty"`
APIKeySID string `json:"apiKeySID,omitempty"`
APIKeySecret string `json:"apiKeySecret,omitempty"`
MessagingServiceSID *string `json:"messagingServiceSID,omitempty"`
}

type SMSProviderConfigurationWebhookInput struct {
Expand Down
3 changes: 3 additions & 0 deletions pkg/portal/sms/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ func (s *Service) sendByTwilio(
messagingServiceSID = *cfg.MessagingServiceSID
}
twilioClient := twilio.NewTwilioClient(&config.TwilioCredentials{
CredentialType: &cfg.CredentialType,
AccountSID: cfg.AccountSID,
AuthToken: cfg.AuthToken,
APIKeySID: cfg.APIKeySID,
APIKeySecret: cfg.APIKeySecret,
MessagingServiceSID: messagingServiceSID,
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
.providerGrid {
@apply grid gap-4 grid-cols-2;
}

.textFieldInOption {
@apply ml-6 my-1;
}
Loading