Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
Implemented RefreshToken, Public and Web App support for OAuth2.0 (#98)
Browse files Browse the repository at this point in the history
* Implemented RefreshToken, Public and Web App support for OAuth2.0

Implemented RefreshToken, Public and Web App support for OAuth2.0, Rewrote the spec for client.rb, Updated the README.md file and bumped the SDK version from 1.2.0 to 1.3.0

* Small refactor in client_spec.rb

* Refactored a test in client_spec.rb

* Refactored the message of an exception raise in client.rb

* Added a template file for Public/Web App config, Small changes in client_spec.rb

Added a template file for Public/Web App config for the 'for public and web integrations, access_token and refresh_token/should differ if refresh token is enforced' integration test and made small changes in client_spec.rb
  • Loading branch information
sfcbetiuc authored and manivinesh committed Aug 6, 2019
1 parent fcf04dc commit 7bfa956
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 113 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ lib/bundler/man
pkg
rdoc
spec/reports
spec/public_or_web_integration_credentials.rb
test/tmp
test/version_tmp
tmp
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
sfmc-fuelsdk-ruby (1.2.0)
sfmc-fuelsdk-ruby (1.3.0)
json (~> 1.8, >= 1.8.1)
jwt (~> 1.0, >= 1.0.0)
savon (= 2.2.0)
Expand Down
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@ ExactTarget Fuel SDK / SalesforceMarektingCloudSDK for Ruby
## Overview ##
The Fuel SDK for Ruby provides easy access to ExactTarget's Fuel API Family services, including a collection of REST APIs and a SOAP API. These APIs provide access to ExactTarget functionality via common collection types such as array/hash.

## New Features in Version 1.3.0 ##
- **Added Refresh Token support for OAuth2 authentication**
- **Added Web/Public App support for OAuth2 authentication**

More details on Access Tokens for Web/Public Apps can be found [here](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-app.htm)

Example of instantiating the Client class:

```
myclient = MarketingCloudSDK::Client.new({
'client' => {
'id' => '<CLIENT_ID>',
'secret' => '<CLIENT_SECRET>',
'request_token_url' => '<AUTH TENANT SPECIFIC ENDPOINT>',
'soap_endpoint' => '<SOAP TENANT SPECIFIC ENDPOINT>',
'base_api_url' => '<REST TENANT SPECIFIC ENDPOINT>',
'use_oAuth2_authentication' => true,
'account_id' => <TARGET_ACCOUNT_ID>,
'scope' => '<PERMISSION_LIST>',
'application_type' => '<APPLICATION_TYPE>',
'redirect_URI' => '<REDIRECT_URI_FOR_PUBLIC/WEB_APP>',
'authorization_code' => '<AUTHORIZATION_CODE_FOR_PUBLIC/WEB_APP>'
}
})
```

* application_type can have one of the following values: `server`, `public`, `web`. The default value of application_type is `server`.


## New Features in Version 1.2.0 ##
- **OAuth2 authentication support** - [More Details](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/integration-considerations.htm)
Expand Down Expand Up @@ -65,7 +93,7 @@ gem build marketingcloudsdk.gemspec
Install the newly built gem

```ruby
gem install marketingcloudsdk-1.2.0.gem
gem install marketingcloudsdk-1.3.0.gem
```

If you have not registered your application or you need to lookup your Application Key or Application Signature values, please go to App Center at [Code@: ExactTarget's Developer Community](http://code.exacttarget.com/appcenter "Code@ App Center").
Expand Down
91 changes: 69 additions & 22 deletions lib/marketingcloudsdk/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,21 @@ def unpack raw
class Client
attr_accessor :debug, :access_token, :auth_token, :internal_token, :refresh_token,
:id, :secret, :signature, :base_api_url, :package_name, :package_folders, :parent_folders, :auth_token_expiration,
:request_token_url, :soap_endpoint, :use_oAuth2_authentication, :account_id, :scope
:request_token_url, :soap_endpoint, :use_oAuth2_authentication, :account_id, :scope, :application_type, :authorization_code, :redirect_URI

include MarketingCloudSDK::Soap
include MarketingCloudSDK::Rest

def jwt= encoded_jwt
raise 'Require app signature to decode JWT' unless self.signature
decoded_jwt = JWT.decode(encoded_jwt, self.signature, true)
decoded_jwt_first = decoded_jwt.first

self.auth_token = decoded_jwt['request']['user']['oauthToken']
self.internal_token = decoded_jwt['request']['user']['internalOauthToken']
self.refresh_token = decoded_jwt['request']['user']['refreshToken']
self.auth_token_expiration = Time.new + decoded_jwt['request']['user']['expiresIn']
self.package_name = decoded_jwt['request']['application']['package']
self.auth_token = decoded_jwt_first['request']['user']['oauthToken']
self.internal_token = decoded_jwt_first['request']['user']['internalOauthToken']
self.refresh_token = decoded_jwt_first['request']['user']['refreshToken']
self.auth_token_expiration = Time.new + decoded_jwt_first['request']['user']['expiresIn']
self.package_name = decoded_jwt_first['request']['application']['package']
end

def initialize(params={}, debug=false)
Expand All @@ -108,6 +109,9 @@ def initialize(params={}, debug=false)
self.use_oAuth2_authentication = client_config["use_oAuth2_authentication"]
self.account_id = client_config["account_id"]
self.scope = client_config["scope"]
self.application_type = client_config["application_type"]
self.authorization_code = client_config["authorization_code"]
self.redirect_URI = client_config["redirect_URI"]
end

# Set a default value in case no 'client' params is sent
Expand All @@ -123,6 +127,26 @@ def initialize(params={}, debug=false)
end
end

if application_type.to_s.strip.empty?
self.application_type = 'server'
end

if ['web', 'public'].include? application_type
if authorization_code.to_s.strip.empty? or redirect_URI.to_s.strip.empty?
raise 'authorization_code or redirect_URI is null: For Public/Web Apps, the authorization_code and redirect_URI must be passed when instantiating Client'
end
end

if application_type == 'public'
if id.to_s.strip.empty?
raise 'id is null: id must be passed when instantiating Client'
end
else
if id.to_s.strip.empty? or secret.to_s.strip.empty?
raise 'id and secret must pe passed when instantiating Client'
end
end

self.jwt = params['jwt'] if params['jwt']
self.refresh_token = params['refresh_token'] if params['refresh_token']

Expand All @@ -133,7 +157,6 @@ def initialize(params={}, debug=false)

def refresh force=false
@refresh_mutex.synchronize do
raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)

if (self.use_oAuth2_authentication == true)
self.refreshWithOAuth2(force)
Expand Down Expand Up @@ -169,43 +192,67 @@ def refresh force=false
end

def refreshWithOAuth2 force=false
raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
#If we don't already have a token or the token expires within 5 min(300 seconds)
if (self.access_token.nil? || Time.new + 300 > self.auth_token_expiration || force) then
payload = Hash.new.tap do |h|
h['client_id']= id
h['client_secret'] = secret
h['grant_type'] = 'client_credentials'

if (not self.account_id.to_s.strip.empty?)then
h['account_id'] = account_id
end

if (not self.scope.to_s.strip.empty?)then
h['scope'] = scope
end
end
payload = createPayload

options = Hash.new.tap do |h|
h['data'] = payload
h['content_type'] = 'application/json'
end

self.request_token_url += '/v2/token'
auth_endpoint = request_token_url + '/v2/token'

response = post(request_token_url, options)
response = post(auth_endpoint, options)
raise "Unable to refresh token: #{response['message']}" unless response.has_key?('access_token')

self.access_token = response['access_token']
self.auth_token_expiration = Time.new + response['expires_in']
self.soap_endpoint = response['soap_instance_url'] + 'service.asmx'
self.base_api_url = response['rest_instance_url']

if response.has_key?('refresh_token')
self.refresh_token = response['refresh_token']
end

return true
else
return false
end
end

def createPayload
payload = Hash.new.tap do |h|
h['client_id'] = id

if application_type != 'public'
h['client_secret'] = secret
end

if !refresh_token.to_s.strip.empty?
h['grant_type'] = 'refresh_token'
h['refresh_token'] = refresh_token
elsif ['web', 'public'].include? application_type
h['grant_type'] = 'authorization_code'
h['code'] = authorization_code
h['redirect_uri'] = redirect_URI
else
h['grant_type'] = 'client_credentials'
end

unless account_id.to_s.strip.empty?
h['account_id'] = account_id
end

unless scope.to_s.strip.empty?
h['scope'] = scope
end
end

payload
end

def refresh!
refresh true
end
Expand Down
2 changes: 1 addition & 1 deletion lib/marketingcloudsdk/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@
=end

module MarketingCloudSDK
VERSION = "1.2.0"
VERSION = "1.3.0"
end
Loading

0 comments on commit 7bfa956

Please sign in to comment.