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

Commit

Permalink
Refresh token public app support (#81)
Browse files Browse the repository at this point in the history
* Added RefreshToken,Webapp and public app support for oauth2

* updated readme

* address code review concerns

* address code review concerns

* address code review concerns

* address code review concerns

* address code review concerns

* Small update in README.md

* modified test name and added comments

* updating version in code
  • Loading branch information
manivinesh authored Aug 6, 2019
1 parent d647407 commit aebf1e0
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 16 deletions.
86 changes: 72 additions & 14 deletions FuelSDK-CSharp/ETClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ namespace FuelSDK
/// </summary>
public class ETClient
{
public const string SDKVersion = "FuelSDK-C#-v1.2.1";
public const string SDKVersion = "FuelSDK-C#-v1.3.0";

private FuelSDKConfigurationSection configSection;
public string AuthToken { get; private set; }
public SoapClient SoapClient { get; private set; }
public string InternalAuthToken { get; private set; }
public string RefreshKey { get; private set; }
public string RefreshKey { get; internal set; }
public DateTime AuthTokenExpiration { get; private set; }
public JObject Jwt { get; private set; }
public string EnterpriseId { get; private set; }
Expand Down Expand Up @@ -110,9 +110,42 @@ public ETClient(NameValueCollection parameters = null, RefreshState refreshState
{
configSection.Scope = parameters["scope"];
}
if (parameters.AllKeys.Contains("applicationType"))
{
configSection.ApplicationType = parameters["applicationType"];
}
if (parameters.AllKeys.Contains("authorizationCode"))
{
configSection.AuthorizationCode = parameters["authorizationCode"];
}
if (parameters.AllKeys.Contains("redirectURI"))
{
configSection.RedirectURI = parameters["redirectURI"];
}
}

configSection.ApplicationType = string.IsNullOrEmpty(configSection.ApplicationType) ? "server" : configSection.ApplicationType;

if (configSection.ApplicationType.Equals("public") || configSection.ApplicationType.Equals("web"))
{
if (string.IsNullOrEmpty(configSection.AuthorizationCode) || string.IsNullOrEmpty(configSection.RedirectURI))
{
throw new Exception("AuthorizationCode or RedirectURI is null: For Public/Web Apps, AuthCode and Redirect URI must be provided in config file or passed when instantiating ETClient");
}
}
if (string.IsNullOrEmpty(configSection.ClientId) || string.IsNullOrEmpty(configSection.ClientSecret))
throw new Exception("clientId or clientSecret is null: Must be provided in config file or passed when instantiating ETClient");

if (configSection.ApplicationType.Equals("public"))
{
if(string.IsNullOrEmpty(configSection.ClientId))
throw new Exception("clientId is null: Must be provided in config file or passed when instantiating ETClient");
}
else
{
if (string.IsNullOrEmpty(configSection.ClientId) || string.IsNullOrEmpty(configSection.ClientSecret))
{
throw new Exception("clientId or clientSecret is null: Must be provided in config file or passed when instantiating ETClient");
}
}

// If JWT URL Parameter Used
var organizationFind = false;
Expand Down Expand Up @@ -330,7 +363,38 @@ public void RefreshToken(bool force = false)
RefreshKey = parsedResponse["refreshToken"].Value<string>().Trim();
}

private void RefreshTokenWithOauth2(bool force = false)
internal dynamic CreatePayload(FuelSDKConfigurationSection config)
{
dynamic payload = new JObject();

payload.client_id = config.ClientId;

if (!config.ApplicationType.Equals("public"))
payload.client_secret = config.ClientSecret;

if (!string.IsNullOrEmpty(RefreshKey))
{
payload.grant_type = "refresh_token";
payload.refresh_token = RefreshKey;
}
else if (!config.ApplicationType.Equals("server"))
{
payload.grant_type = "authorization_code";
payload.code = config.AuthorizationCode;
payload.redirect_uri = config.RedirectURI;
}
else
payload.grant_type = "client_credentials";

if (!string.IsNullOrEmpty(config.AccountId))
payload.account_id = config.AccountId;
if (!string.IsNullOrEmpty(config.Scope))
payload.scope = config.Scope;

return payload;
}

internal void RefreshTokenWithOauth2(bool force = false)
{
// workaround to support TLS 1.2 in .NET 4.0
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
Expand All @@ -346,15 +410,7 @@ private void RefreshTokenWithOauth2(bool force = false)

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
dynamic payload = new JObject();
payload.client_id = configSection.ClientId;
payload.client_secret = configSection.ClientSecret;
payload.grant_type = "client_credentials";
if (!string.IsNullOrEmpty(configSection.AccountId))
payload.account_id = configSection.AccountId;
if (!string.IsNullOrEmpty(configSection.Scope))
payload.scope = configSection.Scope;

dynamic payload = CreatePayload(configSection);
streamWriter.Write(payload.ToString());
}

Expand All @@ -372,6 +428,8 @@ private void RefreshTokenWithOauth2(bool force = false)
AuthTokenExpiration = DateTime.Now.AddSeconds(int.Parse(parsedResponse["expires_in"].Value<string>().Trim()));
configSection.SoapEndPoint = parsedResponse["soap_instance_url"].Value<string>().Trim() + "service.asmx";
configSection.RestEndPoint = parsedResponse["rest_instance_url"].Value<string>().Trim();
if (parsedResponse["refresh_token"] != null)
RefreshKey = parsedResponse["refresh_token"].Value<string>().Trim();
}

public FuelReturn AddSubscribersToList(string emailAddress, string subscriberKey, IEnumerable<int> listIDs) { return ProcessAddSubscriberToList(emailAddress, subscriberKey, listIDs); }
Expand Down
36 changes: 35 additions & 1 deletion FuelSDK-CSharp/FuelSDKConfigurationSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public string ClientId
/// Gets or sets the client secret.
/// </summary>
/// <value>The client secret.</value>
[ConfigurationProperty("clientSecret", IsRequired = true)]
[ConfigurationProperty("clientSecret")]
public string ClientSecret
{
get { return (string)this["clientSecret"]; }
Expand Down Expand Up @@ -100,6 +100,40 @@ public string Scope
set { this["scope"] = value; }
}

/// <summary>
/// Gets or sets the Application Type.
/// </summary>
/// <value>Application Type</value>
[ConfigurationProperty("applicationType", DefaultValue = "server")]
public string ApplicationType
{
get { return (string)this["applicationType"]; }
set { this["applicationType"] = value; }
}

/// <summary>
/// Gets or sets the Authorization Code.
/// </summary>
/// <value>Authorization Code</value>
[ConfigurationProperty("authorizationCode", DefaultValue = "")]
public string AuthorizationCode
{
get { return (string)this["authorizationCode"]; }
set { this["authorizationCode"] = value; }
}

/// <summary>
/// Gets or sets the Redirect URL.
/// </summary>
/// <value>Authorization Code</value>
[ConfigurationProperty("redirectURI", DefaultValue = "")]
public string RedirectURI
{
get { return (string)this["redirectURI"]; }
set { this["redirectURI"] = value; }
}


/// <summary>
/// Clone this instance.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions FuelSDK-CSharp/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: InternalsVisibleTo("FuelSDK-Test")]
2 changes: 2 additions & 0 deletions FuelSDK-Test/FuelSDK.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
Expand Down Expand Up @@ -61,6 +62,7 @@
<Compile Include="ETTriggeredSendDefinitionTest.cs" />
<Compile Include="ETUnsubEventTest.cs" />
<Compile Include="FuelSDKConfigurationSectionTest.cs" />
<Compile Include="RefreshTokenTest.cs" />
<Compile Include="StackKeyTest.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
112 changes: 112 additions & 0 deletions FuelSDK-Test/RefreshTokenTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using NUnit.Framework;
using System;
using FuelSDK;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Collections.Specialized;

namespace FuelSDK.Test
{
/// <summary>
/// These tests are specific for OAuth2 Public/Web Apps, So Config should be modified accordingly.
/// </summary>
[TestFixture()]
public class RefreshTokenTest
{
ETClient client;
FuelSDKConfigurationSection config;

[OneTimeSetUp]
public void Setup()
{
client = new ETClient();
config = new FuelSDKConfigurationSection();
config.AuthorizationCode = "Auth_Code_For_OAuth2_WebApp";
config.RedirectURI = "www.google.com";
config.ClientId = "OAUTH2_CLIENTID";
config.ClientSecret = "OAUTH2_CLIENT_SECRET";

}

[Test()]
public void AuthTokenAndRefreshTokenShouldDifferIfRefreshTokenIsEnforced()
{
var token = client.AuthToken;
var refreshToken = client.RefreshKey;
client.RefreshTokenWithOauth2(true);
var token1 = client.AuthToken;
var refreshToken1 = client.RefreshKey;


Assert.AreNotEqual(token, token1);
Assert.AreNotEqual(refreshToken, refreshToken1);
}

[Test()]
public void AuthPayloadShouldHavePublicAppAttributes()
{
config.ApplicationType = "public";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.client_id.ToString(), config.ClientId);
Assert.AreEqual(payload.redirect_uri.ToString(), config.RedirectURI);
Assert.AreEqual(payload.code.ToString(), config.AuthorizationCode);
Assert.AreEqual(payload.grant_type.ToString(), "authorization_code");
}

[Test()]
public void AuthPayloadForPublicApp_ShouldNotHaveClientSecret()
{
config.ApplicationType = "public";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.client_secret, null);
}

[Test()]
public void AuthPayloadShouldHaveWebAppAttributes()
{
config.ApplicationType = "web";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.grant_type.ToString(), "authorization_code");
Assert.AreEqual(payload.client_id.ToString(), config.ClientId);
Assert.AreEqual(payload.client_secret.ToString(), config.ClientSecret);
Assert.AreEqual(payload.redirect_uri.ToString(), config.RedirectURI);
Assert.AreEqual(payload.code.ToString(), config.AuthorizationCode);
}

[Test()]
public void AuthPayloadShouldHaveServerAppAttributes()
{
config.ApplicationType = "server";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.grant_type.ToString(), "client_credentials");
Assert.AreEqual(payload.client_id.ToString(), config.ClientId);
Assert.AreEqual(payload.client_secret.ToString(), config.ClientSecret);
}

[Test()]
public void AuthPayloadForServerApp_ShouldNotHaveCodeAndRedirectURI()
{
config.ApplicationType = "server";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.code, null);
Assert.AreEqual(payload.redirect_uri, null);
}

[Test()]
public void AuthPayloadWithRefreshToken_ShouldHaveRefreshTokenAttribute()
{
client.RefreshKey = "REFRESH_KEY";

config.ApplicationType = "public";
dynamic payload = client.CreatePayload(config);

Assert.AreEqual(payload.grant_type.ToString(), "refresh_token");
Assert.AreEqual(payload.refresh_token.ToString(), client.RefreshKey);
}
}
}
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ For Legacy authentication, use the below example for `App.config`
</configuration>
```

For OAuth2 authentication [More Details][here](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm)., use the below example for `App.config`
For OAuth2 authentication, use the below example for `App.config`(More details can be found [here](https://developer.salesforce.com/docs/atlas.en-us.mc-app-development.meta/mc-app-development/access-token-s2s.htm))
```
<?xml version="1.0"?>
<configuration>
Expand All @@ -61,6 +61,9 @@ For OAuth2 authentication [More Details][here](https://developer.salesforce.com/
authEndPoint="YOUR_AUTH_TSE"
restEndPoint="YOUR_REST_TSE"
useOAuth2Authentication="true"
applicationType="server"||"public"||"web" //if you are using oauth2 for public or web app. By default, this will be "server"
authorizationCode="AUTHORIZATION_CODE"
redirectURI="REDIRECT_URI_FOR_PUBLIC/WEB_APP"
accountId="TARGET_ACCOUNT_ID"
scope="DATA_ACCESS_PERMISSIONS" />
</configuration>
Expand Down

0 comments on commit aebf1e0

Please sign in to comment.