Skip to content

Commit

Permalink
Update Ruby SDK with correct URLs (#55)
Browse files Browse the repository at this point in the history
* Updated Ruby SDK to use two auth urls - connect & auth

created two clients to point to both urls

updated existing tests

with the advent of Connect 3.0 initial calls are need to be sent to connect.smartcar.com

cont: while any subsequent calls are sent to auth.smartcar.com

* added small test to check proper creation of auth_client

* added two more tests to inspect urls for both clients

* chore: rubocop suggested formatting changes

running Inspecting 24 files
........................

24 files inspected, no offenses detected fixes these formatting error locally

* chore: fix selenium for connect 3.0

* chore: update selenium for conenct 3

* chore: remove unwanted test

* chore: update readme to clarify distinct auth urls

* chore: update README for better clarity

---------

Co-authored-by: s-ashwinkumar <[email protected]>
  • Loading branch information
JacobAndrewSmith92 and s-ashwinkumar authored Mar 7, 2023
1 parent 53831a5 commit 60b8fa1
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 28 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ not have access to the dashboard, please

- Create a new `AuthClient` object with your `client_id`, `client_secret`,
`redirect_uri`.
-
- Redirect the user to Smartcar Connect using `get_auth_url` with required `scope` or with one
of our frontend SDKs.
- The user will login, and then accept or deny your `scope`'s permissions.
Expand Down Expand Up @@ -76,6 +77,9 @@ Setup the environment variables for SMARTCAR_CLIENT_ID, SMARTCAR_CLIENT_SECRET a
export SMARTCAR_CLIENT_ID=<client id>
export SMARTCAR_CLIENT_SECRET=<client secret>
export SMARTCAR_REDIRECT_URI=<redirect URI>
# Optional ENV variables
export SMARTCAR_CONNECT_ORIGIN=(default_value: connect.smartcar.com): Used as the host for the URL that starts the Connect/OAuth2 flow
export SMARTCAR_AUTH_ORIGIN=(default_value: auth.smartcar.com): Used as the host for the token exchange requests
```

Example Usage for calling the reports API with oAuth token
Expand Down
3 changes: 2 additions & 1 deletion lib/smartcar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class ConfigNotFound < StandardError; end
}.freeze

# Path for smartcar oauth
AUTH_ORIGIN = 'https://connect.smartcar.com'
CONNECT_ORIGIN = 'https://connect.smartcar.com'
AUTH_ORIGIN = 'https://auth.smartcar.com'
%w[success code test live force auto metric imperial].each do |constant|
# Constant to represent the value
const_set(constant.upcase, constant.freeze)
Expand Down
38 changes: 24 additions & 14 deletions lib/smartcar/auth_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Smartcar
class AuthClient
include Smartcar::Utils

attr_reader :redirect_uri, :client_id, :client_secret, :scope, :mode, :flags, :origin
attr_reader :redirect_uri, :client_id, :client_secret, :scope, :mode, :flags, :auth_origin, :connect_origin

# Constructor for a client object
#
Expand All @@ -23,7 +23,8 @@ def initialize(options)
options[:redirect_uri] ||= get_config('SMARTCAR_REDIRECT_URI')
options[:client_id] ||= get_config('SMARTCAR_CLIENT_ID')
options[:client_secret] ||= get_config('SMARTCAR_CLIENT_SECRET')
options[:origin] = ENV['SMARTCAR_AUTH_ORIGIN'] || AUTH_ORIGIN
options[:auth_origin] = ENV['SMARTCAR_AUTH_ORIGIN'] || AUTH_ORIGIN
options[:connect_origin] = ENV['SMARTCAR_CONNECT_ORIGIN'] || CONNECT_ORIGIN
options[:mode] = determine_mode(options[:test_mode], options[:mode]) || 'live'
super
end
Expand Down Expand Up @@ -57,7 +58,7 @@ def initialize(options)
def get_auth_url(scope, options = {})
initialize_auth_parameters(scope, options)
add_single_select_options(options[:single_select])
client.auth_code.authorize_url(@auth_parameters)
connect_client.auth_code.authorize_url(@auth_parameters)
end

# Generates the tokens hash using the code returned in oauth process.
Expand All @@ -70,9 +71,9 @@ def get_auth_url(scope, options = {})
def exchange_code(code, options = {})
set_token_url(options[:flags])

token_hash = client.auth_code
.get_token(code, redirect_uri: redirect_uri)
.to_hash
token_hash = auth_client.auth_code
.get_token(code, redirect_uri: redirect_uri)
.to_hash

json_to_ostruct(token_hash)
rescue OAuth2::Error => e
Expand All @@ -88,7 +89,7 @@ def exchange_code(code, options = {})
def exchange_refresh_token(token, options = {})
set_token_url(options[:flags])

token_object = OAuth2::AccessToken.from_hash(client, { refresh_token: token })
token_object = OAuth2::AccessToken.from_hash(auth_client, { refresh_token: token })
token_object = token_object.refresh!

json_to_ostruct(token_object.to_hash)
Expand All @@ -101,7 +102,7 @@ def exchange_refresh_token(token, options = {})
#
# @return [Boolean]
def expired?(expires_at)
OAuth2::AccessToken.from_hash(client, { expires_at: expires_at }).expired?
OAuth2::AccessToken.from_hash(auth_client, { expires_at: expires_at }).expired?
end

private
Expand All @@ -117,7 +118,7 @@ def set_token_url(flags)
params[:flags] = build_flags(flags) if flags
# Note - The inbuild interface to get the token does not allow any way to pass additional
# URL params. Hence building the token URL with the flags and setting it in client.
client.options[:token_url] = client.connection.build_url('/oauth/token', params).request_uri
auth_client.options[:token_url] = auth_client.connection.build_url('/oauth/token', params).request_uri
end

def initialize_auth_parameters(scope, options)
Expand All @@ -144,13 +145,22 @@ def add_single_select_options(single_select)
end
end

# gets the Oauth Client object
# gets the Oauth Client object configured with auth.connect.smartcar.com
#
# @return [OAuth2::Client] A Oauth Client object.
def client
@client ||= OAuth2::Client.new(client_id,
client_secret,
site: origin)
def auth_client
@auth_client ||= OAuth2::Client.new(client_id,
client_secret,
site: auth_origin)
end

# gets the Oauth Client object configured with connect.smartcar.com
#
# @return [OAuth2::Client] A Oauth Client object.
def connect_client
@connect_client ||= OAuth2::Client.new(client_id,
client_secret,
site: connect_origin)
end
end
end
7 changes: 0 additions & 7 deletions spec/smartcar/e2e/auth_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,6 @@
expect(new_tokens.access_token).not_to eq(old_tokens.access_token)
expect(new_tokens.refresh_token.length).to eq(36)
expect(new_tokens.token_type).to eq('Bearer')

expect { subject.exchange_refresh_token(old_tokens.refresh_token) }.to(raise_error do |error|
expect(error.type).to eq('invalid_grant')
expect(error.message).to eq('invalid_grant: - Invalid or expired refresh token.')
expect(error.request_id.length).to eq(36)
expect(error.status_code).to eq(400)
end)
end
end

Expand Down
5 changes: 3 additions & 2 deletions spec/smartcar/helpers/auth_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ def run_auth_flow(authorization_url, test_email = nil, make = nil)
options = Selenium::WebDriver::Firefox::Options.new(args: ['-headless'])
driver = Selenium::WebDriver.for(:firefox, capabilities: [options])
driver.navigate.to authorization_url
driver.find_element(css: 'button#continue-button').click
driver.find_element(css: "button.brand-selector-button[data-make=\"#{make}\"]").click
driver.manage.timeouts.implicit_wait = 10
driver.find_element(:css, 'button#continue-button').click
driver.find_element(css: "button[id=\"#{make}\"]").click
driver.find_element(css: 'input[id=username]').send_keys(email)
driver.find_element(css: 'input[id=password').send_keys('password')
driver.find_element(css: 'button[id=sign-in-button]').click
Expand Down
37 changes: 33 additions & 4 deletions spec/smartcar/unit/auth_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
let(:obj) { double('dummy object for client') }

before do
allow(subject).to receive_message_chain(:client, :auth_code).and_return(obj)
allow(subject).to receive_message_chain(:connect_client, :auth_code).and_return(obj)
end

context 'constructor' do
Expand Down Expand Up @@ -101,13 +101,42 @@
end
end

context 'client' do
context 'connect_client' do
before do
allow(subject).to receive(:client).and_call_original
allow(subject).to receive(:connect_client).and_call_original
end
it 'should create OAuth2::Client object' do
expect(OAuth2::Client).to receive(:new)
subject.send(:client)
subject.send(:connect_client)
end
end
context 'it should check the base url for connect_client and auth_client' do
it 'verifies the auth_url' do
client = Smartcar::AuthClient.new({
redirect_uri: 'test_url',
client_id: 'SMARTCAR_CLIENT_ID',
client_secret: 'SMARTCAR_CLIENT_SECRET'
})
url = client.instance_variable_get(:@auth_origin)
expect(url).to eq('https://auth.smartcar.com')
end
it 'verifies the connect_url' do
client = Smartcar::AuthClient.new({
redirect_uri: 'test_url',
client_id: 'SMARTCAR_CLIENT_ID',
client_secret: 'SMARTCAR_CLIENT_SECRET'
})
url = client.instance_variable_get(:@connect_origin)
expect(url).to eq('https://connect.smartcar.com')
end
end
context 'auth_client' do
before do
allow(subject).to receive(:auth_client).and_call_original
end
it 'should create OAuth2::Client object' do
expect(OAuth2::Client).to receive(:new)
subject.send(:auth_client)
end
end
end

0 comments on commit 60b8fa1

Please sign in to comment.