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

feat: public oauth #735

Merged
merged 26 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e4e5199
feat: oauth ruby (#733)
manisha1997 Nov 26, 2024
7635646
feat: auth ruby (#734)
manisha1997 Dec 9, 2024
447a3af
Merge branch 'main' into public-oauth
manisha1997 Dec 9, 2024
a42273b
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
b669c3a
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
5a1ff9d
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
711b3d0
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
24aa946
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
7894b81
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
ae94805
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
87f8163
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
d724815
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
5dba75a
feat: add oauth support for ruby
manisha1997 Dec 9, 2024
b5d575f
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
6bb7c21
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
e32a807
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
a8b3392
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
7236def
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
8682d1f
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
448aac4
feat: add oauth support for ruby
manisha1997 Dec 10, 2024
c6dce6a
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
e506902
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
b0a8a5e
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
a8e90cf
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
290d040
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
6d66e2e
feat: add oauth support for ruby
manisha1997 Dec 11, 2024
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
8 changes: 8 additions & 0 deletions .github/workflows/test-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ jobs:
TWILIO_API_SECRET: ${{ secrets.TWILIO_CLUSTER_TEST_API_KEY_SECRET }}
TWILIO_FROM_NUMBER: ${{ secrets.TWILIO_FROM_NUMBER }}
TWILIO_TO_NUMBER: ${{ secrets.TWILIO_TO_NUMBER }}
TWILIO_ORGS_ACCOUNT_SID: ${{ secrets.TWILIO_ORGS_ACCOUNT_SID }}
TWILIO_ORGS_CLIENT_SECRET: ${{ secrets.TWILIO_ORGS_CLIENT_SECRET }}
TWILIO_ORGS_CLIENT_ID: ${{ secrets.TWILIO_ORGS_CLIENT_ID }}
TWILIO_ORG_SID: ${{ secrets.TWILIO_ORG_SID }}
TWILIO_USER_SID: ${{ secrets.TWILIO_USER_SID }}
TWILIO_CLIENT_SECRET: ${{ secrets.TWILIO_CLIENT_SECRET }}
TWILIO_CLIENT_ID: ${{ secrets.TWILIO_CLIENT_ID }}
TWILIO_MESSAGE_SID: ${{ secrets.TWILIO_MESSAGE_SID }}
run: make cluster-test

- name: Fix code coverage paths
Expand Down
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Lint/AmbiguousBlockAssociation:
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
- 'cluster/*'
- twilio-ruby.gemspec

Layout/HeredocIndentation:
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ docker-push:
docker push twilio/twilio-ruby:${CURRENT_TAG}

cluster-test:
bundle exec rspec ./cluster_spec.rb
bundle exec rspec ./cluster/cluster_spec.rb
bundle exec rspec ./cluster/cluster_oauth_spec.rb
bundle exec rspec ./cluster/cluster_orgs_spec.rb

prettier:
bundle exec rubocop -A -d --cache true --parallel
19 changes: 19 additions & 0 deletions cluster/cluster_oauth_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'rspec/matchers'
require 'twilio-ruby'
require './lib/twilio-ruby/credential/client_credential_provider'

describe 'Cluster Test' do
before(:each) do
@account_sid = ENV['TWILIO_ACCOUNT_SID']
@client_secret = ENV['TWILIO_CLIENT_SECRET']
@client_id = ENV['TWILIO_CLIENT_ID']
@message_sid = ENV['TWILIO_MESSAGE_SID']
@credential = Twilio::REST::ClientCredentialProvider.new(@client_id, @client_secret)
@client = Twilio::REST::Client.new(@account_sid).credential_provider(@credential)
end

it 'can fetch a message' do
response = @client.messages(@message_sid).fetch
expect(response).to_not be_nil
end
end
40 changes: 40 additions & 0 deletions cluster/cluster_orgs_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'rspec/matchers'
require 'twilio-ruby'
require './lib/twilio-ruby/credential/client_credential_provider'

describe 'Cluster Test' do
before(:each) do
@client_secret = ENV['TWILIO_ORGS_CLIENT_SECRET']
@client_id = ENV['TWILIO_ORGS_CLIENT_ID']
@org_sid = ENV['TWILIO_ORG_SID']
@user_sid = ENV['TWILIO_USER_SID']
@account_sid = ENV['TWILIO_ORGS_ACCOUNT_SID']
@credential = Twilio::REST::ClientCredentialProvider.new(@client_id, @client_secret)
@client = Twilio::REST::Client.new.credential_provider(@credential)
end

it 'can list accounts' do
response = @client.preview_iam.organizations(@org_sid).accounts.list
expect(response).to_not be_nil
end

it 'can fetch specific account' do
response = @client.preview_iam.organizations(@org_sid).accounts(@account_sid).fetch
expect(response).to_not be_nil
end

it 'can access role assignments list' do
response = @client.preview_iam.organizations(@org_sid).role_assignments.list(scope: @account_sid)
expect(response).to_not be_nil
end

it 'can get user list' do
response = @client.preview_iam.organizations(@org_sid).users.list
expect(response).to_not be_nil
end

it 'can get single user' do
response = @client.preview_iam.organizations(@org_sid).users(@user_sid).fetch
expect(response).to_not be_nil
end
end
2 changes: 0 additions & 2 deletions cluster_spec.rb → cluster/cluster_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require 'rspec/matchers'
require 'twilio-ruby'

# rubocop:disable Metrics/BlockLength
describe 'Cluster Test' do
before(:each) do
@account_sid = ENV['TWILIO_ACCOUNT_SID']
Expand Down Expand Up @@ -74,4 +73,3 @@
expect(@client.events.v1.sinks(sink.sid).delete).to eq(true)
end
end
# rubocop:enable Metrics/BlockLength
13 changes: 13 additions & 0 deletions examples/public_oauth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'twilio-ruby'
require 'twilio-ruby/credential/client_credential_provider'

credential_provider = Twilio::REST::ClientCredentialProvider.new(ENV['CLIENT_ID'], ENV['CLIENT_SECRET'])
# passing account sid is not mandatory
client = Twilio::REST::Client.new(ENV['ACCOUNT_SID']).credential_provider(credential_provider)

# send messages
client.messages.create(
from: ENV['TWILIO_PHONE_NUMBER'],
to: ENV['PHONE_NUMBER'],
body: 'Hello from Ruby!'
)
19 changes: 19 additions & 0 deletions lib/twilio-ruby/auth_strategy/auth_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Twilio
module REST
class AuthStrategy
attr_accessor :auth_type

def initialize(auth_type)
@auth_type = auth_type
end

def auth_string
raise NotImplementedError, 'Subclasses must implement this method'
end

def requires_authentication
raise NotImplementedError, 'Subclasses must implement this method'
end
end
end
end
17 changes: 17 additions & 0 deletions lib/twilio-ruby/auth_strategy/no_auth_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Twilio
module REST
class NoAuthStrategy < AuthStrategy
def initialize
super(AuthType::NONE)
end

def auth_string
''
end

def requires_authentication
false
end
end
end
end
39 changes: 39 additions & 0 deletions lib/twilio-ruby/auth_strategy/token_auth_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require_relative 'auth_strategy'
require_relative './../credential/auth_type'
require 'jwt'
module Twilio
module REST
class TokenAuthStrategy < AuthStrategy
attr_accessor :token_manager, :token, :lock

def initialize(token_manager)
super(AuthType::ORGS_TOKEN)
@token = nil
@token_manager = token_manager
@lock = Mutex.new
end

def auth_string
token = fetch_token
"Bearer #{token}"
end

def fetch_token
@lock.synchronize do
@token = @token_manager.fetch_access_token if @token.nil? || token_expired? || @token == ''
return @token
end
end

def token_expired?
decoded_token = ::JWT.decode(@token, nil, false)
exp = decoded_token[0]['exp']
Time.at(exp) < Time.now
end

def requires_authentication
true
end
end
end
end
9 changes: 8 additions & 1 deletion lib/twilio-ruby/base/client_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class ClientBase
# rubocop:enable Style/ClassVars

attr_accessor :http_client, :username, :password, :account_sid, :auth_token, :region, :edge, :logger,
:user_agent_extensions
:user_agent_extensions, :credentials

# rubocop:disable Metrics/ParameterLists
def initialize(username = nil, password = nil, account_sid = nil, region = nil, http_client = nil, logger = nil,
Expand All @@ -22,6 +22,11 @@ def initialize(username = nil, password = nil, account_sid = nil, region = nil,
@logger = logger || Twilio.logger
@user_agent_extensions = user_agent_extensions || []
end

def credential_provider(credential_provider = nil)
@credentials = credential_provider
self
end
# rubocop:enable Metrics/ParameterLists

##
Expand All @@ -47,6 +52,8 @@ def request(host, port, method, uri, params = {}, data = {}, headers = {}, auth
@logger.debug("Request Params:#{params}")
end

auth = @credentials.to_auth_strategy.auth_string unless @credentials.nil?

response = @http_client.request(
host,
port,
Expand Down
17 changes: 17 additions & 0 deletions lib/twilio-ruby/credential/auth_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Twilio
module REST
class AuthType
BASIC = 'basic'
ORGS_TOKEN = 'orgs_token'
API_KEY = 'api_key'
NOAUTH = 'noauth'
CLIENT_CREDENTIALS = 'client_credentials'

def to_s
name
end
end
end
end
28 changes: 28 additions & 0 deletions lib/twilio-ruby/credential/client_credential_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require_relative 'credential_provider'
require_relative 'auth_type'
require_relative './../http/client_token_manager'
require_relative './../auth_strategy/token_auth_strategy'
module Twilio
module REST
class ClientCredentialProvider < CredentialProvider
attr_accessor :grant_type, :client_id, :client_secret, :orgs_token, :auth_strategy

def initialize(client_id, client_secret, orgs_token = nil)
super(AuthType::ORGS_TOKEN)
raise ArgumentError, 'client_id and client_secret are required' if client_id.nil? || client_secret.nil?

@grant_type = 'client_credentials'
@client_id = client_id
@client_secret = client_secret
@orgs_token = orgs_token
@auth_strategy = nil
end

def to_auth_strategy
@orgs_token = ClientTokenManager.new(@grant_type, @client_id, @client_secret) if @orgs_token.nil?
@auth_strategy = TokenAuthStrategy.new(@orgs_token) if @auth_strategy.nil?
@auth_strategy
end
end
end
end
11 changes: 11 additions & 0 deletions lib/twilio-ruby/credential/credential_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Twilio
module REST
class CredentialProvider
attr_accessor :auth_type

def initialize(auth_type)
@auth_type = auth_type
end
end
end
end
30 changes: 30 additions & 0 deletions lib/twilio-ruby/credential/orgs_credential_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require_relative 'credential_provider'
require_relative 'auth_type'
require_relative './../http/org_token_manager'
require_relative './../auth_strategy/token_auth_strategy'
module Twilio
module REST
class OrgsCredentialProvider < CredentialProvider
attr_accessor :grant_type, :client_id, :client_secret, :orgs_token, :auth_strategy

def initialize(client_id, client_secret, orgs_token = nil)
super(AuthType::ORGS_TOKEN)
raise ArgumentError, 'client_id and client_secret are required' if client_id.nil? || client_secret.nil?

@grant_type = 'client_credentials'
@client_id = client_id
@client_secret = client_secret
@orgs_token = orgs_token
@auth_strategy = nil
end

def to_auth_strategy
@orgs_token = OrgTokenManager.new(@grant_type, @client_id, @client_secret) if @orgs_token.nil?
@auth_strategy = TokenAuthStrategy.new(@orgs_token) if @auth_strategy.nil?
@auth_strategy
end
end
end
end
1 change: 0 additions & 1 deletion lib/twilio-ruby/framework/rest/domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def absolute_url(uri)

def request(method, uri, params = {}, data = {}, headers = {}, auth = nil, timeout = nil)
url = uri.match(/^http/) ? uri : absolute_url(uri)

@client.request(
@base_url,
@port,
Expand Down
1 change: 1 addition & 0 deletions lib/twilio-ruby/framework/rest/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def process_response(response)
end

def load_page(payload)
return payload['Resources'] if payload['Resources']
if payload['meta'] && payload['meta']['key']
return payload[payload['meta']['key']]
else
Expand Down
28 changes: 28 additions & 0 deletions lib/twilio-ruby/http/client_token_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Twilio
module REST
class ClientTokenManager
attr_accessor :grant_type, :client_id, :client_secret, :code, :redirect_uri, :audience, :refresh_token, :scope

def initialize(grant_type, client_id, client_secret, code = nil, redirect_uri = nil, audience = nil,
refresh_token = nil, scope = nil)
raise ArgumentError, 'client_id and client_secret are required' if client_id.nil? || client_secret.nil?

@grant_type = grant_type
@client_id = client_id
@client_secret = client_secret
@code = code
@redirect_uri = redirect_uri
@audience = audience
@refresh_token = refresh_token
@scope = scope
end

def fetch_access_token
client = Twilio::REST::Client.new
token_instance = client.preview_iam.v1.token.create(grant_type: @grant_type,
client_id: @client_id, client_secret: @client_secret)
token_instance.access_token
end
end
end
end
9 changes: 9 additions & 0 deletions lib/twilio-ruby/http/http_client.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'faraday'
require 'faraday/middleware'

module Twilio
module HTTP
Expand Down Expand Up @@ -79,9 +80,17 @@ def send(request)
end

def request(host, port, method, url, params = {}, data = {}, headers = {}, auth = nil, timeout = nil)
headers['Authorization'] = auth if jwt_token?(auth)
request = Twilio::Request.new(host, port, method, url, params, data, headers, auth, timeout)
_request(request)
end

def jwt_token?(auth)
return false unless auth.is_a?(String)

parts = auth.split('.')
parts.length == 3
end
end
end
end
Loading
Loading