Skip to content

Commit 06f7f92

Browse files
SNOW-1825501 SNOW-1917238 OAuth Authorization Code flow, OAuth Client Credential flow and Programmatic Access Token authentications (#1154)
Co-authored-by: Patryk Cyrek <[email protected]>
1 parent 2a186f4 commit 06f7f92

File tree

112 files changed

+5755
-599
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+5755
-599
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ name: DotNet Build and Test
33
# Triggers the workflow on push or pull request events but only for the master branch
44
on:
55
push:
6-
branches: [ master ]
6+
branches: [ master, oauth_flows ]
77
pull_request:
8-
branches: [ master ]
8+
branches: [ master, oauth_flows ]
99
workflow_dispatch:
1010
inputs:
1111
logLevel:
15 Bytes
Binary file not shown.

Snowflake.Data.Tests/AuthenticationTests/AuthConnectionString.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ static class AuthConnectionString
1717
public static readonly string SsoUser = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_BROWSER_USER");
1818
public static readonly string Host = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_HOST");
1919
public static readonly string SsoPassword = Environment.GetEnvironmentVariable("SNOWFLAKE_TEST_OKTA_PASS");
20+
public static readonly string SnowflakeUser = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_SNOWFLAKE_USER");
21+
public static readonly string SnowflakeRole = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_ROLE");
22+
2023

2124
private static SFSessionProperties GetBaseConnectionParameters()
2225
{
@@ -29,7 +32,20 @@ private static SFSessionProperties GetBaseConnectionParameters()
2932
{SFSessionProperty.DB, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_DATABASE") },
3033
{SFSessionProperty.SCHEMA, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_SCHEMA") },
3134
{SFSessionProperty.WAREHOUSE, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_WAREHOUSE") },
35+
{SFSessionProperty.MINPOOLSIZE, "0"},
36+
{SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL, "false"}
37+
};
38+
return properties;
39+
}
40+
41+
public static SFSessionProperties GetSnowflakeLoginCredentials()
42+
{
43+
var properties = new SFSessionProperties()
44+
{
45+
{ SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID") },
46+
{ SFSessionProperty.PASSWORD, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_USER_PASSWORD") }
3247
};
48+
3349
return properties;
3450
}
3551

@@ -41,6 +57,57 @@ public static SFSessionProperties GetExternalBrowserConnectionString()
4157
return properties;
4258
}
4359

60+
public static SFSessionProperties GetOAuthExternalAuthorizationCodeConnectionString()
61+
{
62+
var properties = GetBaseConnectionParameters();
63+
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH_AUTHORIZATION_CODE");
64+
properties.Add(SFSessionProperty.OAUTHCLIENTID, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
65+
properties.Add(SFSessionProperty.OAUTHCLIENTSECRET, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_SECRET"));
66+
properties.Add(SFSessionProperty.OAUTHREDIRECTURI, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_REDIRECT_URI"));
67+
properties.Add(SFSessionProperty.OAUTHAUTHORIZATIONURL, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_AUTH_URL"));
68+
properties.Add(SFSessionProperty.OAUTHTOKENREQUESTURL, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_TOKEN"));
69+
properties.Add(SFSessionProperty.USER, SsoUser);
70+
71+
return properties;
72+
}
73+
74+
public static SFSessionProperties GetOAuthSnowflakeAuthorizationCodeConnectionParameters()
75+
{
76+
var properties = GetBaseConnectionParameters();
77+
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH_AUTHORIZATION_CODE");
78+
properties.Add(SFSessionProperty.OAUTHCLIENTID, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_CLIENT_ID"));
79+
properties.Add(SFSessionProperty.OAUTHCLIENTSECRET, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_CLIENT_SECRET"));
80+
properties.Add(SFSessionProperty.OAUTHREDIRECTURI, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_REDIRECT_URI"));
81+
properties[SFSessionProperty.ROLE] = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_ROLE");
82+
properties.Add(SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
83+
84+
return properties;
85+
}
86+
87+
public static SFSessionProperties GetOAuthSnowflakeAuthorizationCodeWilidcardsConnectionParameters()
88+
{
89+
var properties = GetBaseConnectionParameters();
90+
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH_AUTHORIZATION_CODE");
91+
properties.Add(SFSessionProperty.OAUTHCLIENTID, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_WILDCARDS_CLIENT_ID"));
92+
properties.Add(SFSessionProperty.OAUTHCLIENTSECRET, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_WILDCARDS_CLIENT_SECRET"));
93+
properties[SFSessionProperty.ROLE] = Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_INTERNAL_OAUTH_SNOWFLAKE_ROLE");
94+
properties.Add(SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
95+
96+
return properties;
97+
}
98+
99+
public static SFSessionProperties GetOAuthExternalClientCredentialParameters()
100+
{
101+
var properties = GetBaseConnectionParameters();
102+
properties.Add(SFSessionProperty.AUTHENTICATOR, "OAUTH_CLIENT_CREDENTIALS");
103+
properties.Add(SFSessionProperty.OAUTHCLIENTID, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
104+
properties.Add(SFSessionProperty.OAUTHCLIENTSECRET, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_SECRET"));
105+
properties.Add(SFSessionProperty.OAUTHTOKENREQUESTURL, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_TOKEN"));
106+
properties.Add(SFSessionProperty.USER, Environment.GetEnvironmentVariable("SNOWFLAKE_AUTH_TEST_EXTERNAL_OAUTH_OKTA_CLIENT_ID"));
107+
108+
return properties;
109+
}
110+
44111
public static SFSessionProperties GetOauthConnectionString(string token)
45112
{
46113
var properties = GetBaseConnectionParameters();
@@ -71,6 +138,13 @@ public static SFSessionProperties GetKeyPairFromFileContentParameters(string pri
71138
return properties;
72139
}
73140

141+
public static SFSessionProperties GetPatConnectionParameters()
142+
{
143+
var properties = GetBaseConnectionParameters();
144+
properties.Add(SFSessionProperty.AUTHENTICATOR, "PROGRAMMATIC_ACCESS_TOKEN");
145+
properties.Add(SFSessionProperty.USER, SsoUser);
146+
return properties;
147+
}
74148

75149
public static SFSessionProperties GetKeyPairFromFilePathConnectionString(string privateKeyPath)
76150
{

Snowflake.Data.Tests/AuthenticationTests/AuthTestHelper.cs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Data;
55
using NUnit.Framework;
66
using Snowflake.Data.Client;
7+
using Snowflake.Data.Core.CredentialManager;
78
using Snowflake.Data.Log;
89

910
namespace Snowflake.Data.AuthenticationTests
@@ -61,6 +62,36 @@ public void ConnectAndExecuteSimpleQuery(string connectionString)
6162
}
6263
}
6364

65+
public string ConnectUsingOktaConnectionAndExecuteCustomCommand(string command, bool returnToken = false)
66+
{
67+
try
68+
{
69+
using (IDbConnection conn = new SnowflakeDbConnection())
70+
{
71+
var parameters = AuthConnectionString.GetOktaConnectionString();
72+
conn.ConnectionString = AuthConnectionString.ConvertToConnectionString(parameters);
73+
conn.Open();
74+
Assert.AreEqual(ConnectionState.Open, conn.State);
75+
using (IDbCommand dbCommand = conn.CreateCommand())
76+
{
77+
dbCommand.CommandText = command;
78+
using (var reader = dbCommand.ExecuteReader())
79+
{
80+
if (returnToken && reader.Read())
81+
{
82+
return reader["token_secret"].ToString();
83+
}
84+
}
85+
}
86+
}
87+
}
88+
catch (SnowflakeDbException e)
89+
{
90+
_exception = e;
91+
}
92+
return null;
93+
}
94+
6495
public Thread GetConnectAndExecuteSimpleQueryThread(string parameters)
6596
{
6697
return new Thread(() => ConnectAndExecuteSimpleQuery(parameters));
@@ -124,11 +155,17 @@ private void StartNodeProcess(string path, TimeSpan timeout)
124155
}
125156
}
126157

158+
internal void RemoveTokenFromCache(string tokenHost, string user, TokenType tokenType)
159+
{
160+
var cacheKey = SnowflakeCredentialManagerFactory.GetSecureCredentialKey(tokenHost, user, tokenType);
161+
SnowflakeCredentialManagerFactory.GetCredentialManager().RemoveCredentials(cacheKey);
162+
}
163+
127164
private void ProvideCredentials(string scenario, string login, string password)
128165
{
129166
try
130167
{
131-
StartNodeProcess($"/externalbrowser/provideBrowserCredentials.js {scenario} {login} {password}", TimeSpan.FromSeconds(15));
168+
StartNodeProcess($"/externalbrowser/provideBrowserCredentials.js {scenario} {login} {password}", TimeSpan.FromSeconds(25));
132169
}
133170
catch (Exception e)
134171
{
@@ -137,4 +174,3 @@ private void ProvideCredentials(string scenario, string login, string password)
137174
}
138175
}
139176
}
140-

Snowflake.Data.Tests/AuthenticationTests/OauthConnectionTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ public void TestAuthenticateUsingOauthInvalidToken()
4040
authTestHelper.VerifyExceptionIsThrown("Invalid OAuth access token");
4141
}
4242

43-
[Test, Ignore("Skipped, waits for SNOW-1893041")]
43+
[Test, Ignore("Skipped, waits for SNOW-1893041")]
4444
public void TestAuthenticateUsingOauthMismatchedUser()
4545
{
4646
AuthTestHelper authTestHelper = new AuthTestHelper();
4747

4848
string token = AuthConnectionString.GetOauthToken();
4949
var parameters = AuthConnectionString.GetOauthConnectionString(token);
50-
parameters.Add(SFSessionProperty.USER, "fakeAccount");
50+
parameters[SFSessionProperty.USER] = "fakeAccount";
5151
parameters.Add(SFSessionProperty.POOLINGENABLED, "false");
52-
parameters.Add(SFSessionProperty.MINPOOLSIZE, "0");
52+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
5353

5454
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
5555
authTestHelper.VerifyExceptionIsThrown("The user you were trying to authenticate as differs from the user tied to the access token");
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System.Threading;
2+
using NUnit.Framework;
3+
using Snowflake.Data.Core;
4+
using Snowflake.Data.Core.CredentialManager;
5+
using Snowflake.Data.Tests;
6+
7+
namespace Snowflake.Data.AuthenticationTests
8+
{
9+
[NonParallelizable, IgnoreOnCI]
10+
public class OktaAuthorizationCodeTest
11+
{
12+
private string _connectionString = "";
13+
private string _login = AuthConnectionString.SsoUser;
14+
private string _password = AuthConnectionString.SsoPassword;
15+
16+
[SetUp, IgnoreOnCI]
17+
public void SetUp()
18+
{
19+
AuthTestHelper authTestHelper = new AuthTestHelper();
20+
_login = AuthConnectionString.SsoUser;
21+
_password = AuthConnectionString.SsoPassword;
22+
authTestHelper.CleanBrowserProcess();
23+
var parameters = AuthConnectionString.GetOAuthExternalAuthorizationCodeConnectionString();
24+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
25+
}
26+
27+
[Test, IgnoreOnCI]
28+
public void TestAuthenticateOktaAuthorizationCodeSuccessful()
29+
30+
{
31+
AuthTestHelper authTestHelper = new AuthTestHelper();
32+
33+
Thread connectThread = authTestHelper.GetConnectAndExecuteSimpleQueryThread(_connectionString);
34+
Thread provideCredentialsThread = authTestHelper.GetProvideCredentialsThread("success", _login, _password);
35+
36+
authTestHelper.ConnectAndProvideCredentials(provideCredentialsThread, connectThread);
37+
authTestHelper.VerifyExceptionIsNotThrown();
38+
}
39+
40+
[Test, IgnoreOnCI]
41+
public void TestAuthenticateOktaAuthorizationCodeMismatchedUser()
42+
{
43+
AuthTestHelper authTestHelper = new AuthTestHelper();
44+
45+
var parameters = AuthConnectionString.GetOAuthExternalAuthorizationCodeConnectionString();
46+
parameters[SFSessionProperty.USER] = "differentUser";
47+
48+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
49+
50+
Thread connectThread = authTestHelper.GetConnectAndExecuteSimpleQueryThread(_connectionString);
51+
Thread provideCredentialsThread = authTestHelper.GetProvideCredentialsThread("success", _login, _password);
52+
53+
authTestHelper.ConnectAndProvideCredentials(provideCredentialsThread, connectThread);
54+
authTestHelper.VerifyExceptionIsThrown("The user you were trying to authenticate as differs from the user tied to the access token");
55+
}
56+
57+
[Test, IgnoreOnCI]
58+
public void TestAuthenticateOktaAuthorizationCodeWrongCredentials()
59+
{
60+
AuthTestHelper authTestHelper = new AuthTestHelper();
61+
62+
var parameters = AuthConnectionString.GetOAuthExternalAuthorizationCodeConnectionString();
63+
parameters.Add(SFSessionProperty.BROWSER_RESPONSE_TIMEOUT, "15");
64+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
65+
66+
_login = "itsnotanaccount.com";
67+
_password = "fakepassword";
68+
69+
Thread connectThread = authTestHelper.GetConnectAndExecuteSimpleQueryThread(_connectionString);
70+
Thread provideCredentialsThread = authTestHelper.GetProvideCredentialsThread("fail", _login, _password);
71+
72+
authTestHelper.ConnectAndProvideCredentials(provideCredentialsThread, connectThread);
73+
authTestHelper.VerifyExceptionIsThrown("Browser response timed out after 15 seconds");
74+
}
75+
76+
[Test, IgnoreOnCI]
77+
public void TestAuthenticateOktaAuthorizationCodeTimeout()
78+
{
79+
AuthTestHelper authTestHelper = new AuthTestHelper();
80+
81+
var parameters = AuthConnectionString.GetOAuthExternalAuthorizationCodeConnectionString();
82+
parameters.Add(SFSessionProperty.BROWSER_RESPONSE_TIMEOUT, "1");
83+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
84+
85+
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
86+
authTestHelper.VerifyExceptionIsThrown("Browser response timed out after 1 seconds");
87+
}
88+
89+
[Test, IgnoreOnCI]
90+
public void TestAuthenticateOktaAuthorizationCodeWithTokenCache()
91+
{
92+
AuthTestHelper authTestHelper = new AuthTestHelper();
93+
var parameters = AuthConnectionString.GetOAuthExternalAuthorizationCodeConnectionString();
94+
parameters.Add(SFSessionProperty.BROWSER_RESPONSE_TIMEOUT, "10");
95+
parameters.Add(SFSessionProperty.POOLINGENABLED, "false");
96+
parameters[SFSessionProperty.CLIENT_STORE_TEMPORARY_CREDENTIAL] = "true";
97+
var host = parameters[SFSessionProperty.HOST];
98+
99+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
100+
Thread connectThread = authTestHelper.GetConnectAndExecuteSimpleQueryThread(_connectionString);
101+
Thread provideCredentialsThread = authTestHelper.GetProvideCredentialsThread("success", _login, _password);
102+
103+
try
104+
{
105+
authTestHelper.ConnectAndProvideCredentials(provideCredentialsThread, connectThread);
106+
authTestHelper.VerifyExceptionIsNotThrown();
107+
108+
authTestHelper.CleanBrowserProcess();
109+
110+
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
111+
authTestHelper.VerifyExceptionIsNotThrown();
112+
}
113+
finally
114+
{
115+
authTestHelper.RemoveTokenFromCache(host, _login, TokenType.OAuthAccessToken);
116+
authTestHelper.RemoveTokenFromCache(host, _login, TokenType.OAuthRefreshToken);
117+
}
118+
}
119+
}
120+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using NUnit.Framework;
2+
using Snowflake.Data.Tests;
3+
using Snowflake.Data.Core;
4+
5+
namespace Snowflake.Data.AuthenticationTests
6+
{
7+
[NonParallelizable, IgnoreOnCI]
8+
public class OktaClientsCredentialsTest
9+
{
10+
private string _connectionString = "";
11+
12+
[Test, IgnoreOnCI]
13+
public void TestAuthenticateOktaClientCredentialsSuccessful()
14+
{
15+
AuthTestHelper authTestHelper = new AuthTestHelper();
16+
var parameters = AuthConnectionString.GetOAuthExternalClientCredentialParameters();
17+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
18+
19+
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
20+
authTestHelper.VerifyExceptionIsNotThrown();
21+
}
22+
23+
24+
[Test, IgnoreOnCI]
25+
public void TestAuthenticateOktaClientCredentialsMismatchedUsername()
26+
{
27+
AuthTestHelper authTestHelper = new AuthTestHelper();
28+
var parameters = AuthConnectionString.GetOAuthExternalClientCredentialParameters();
29+
parameters[SFSessionProperty.USER] = "differentUser";
30+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
31+
32+
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
33+
authTestHelper.VerifyExceptionIsThrown("The user you were trying to authenticate as differs from the user tied to the access token");
34+
}
35+
36+
37+
[Test, IgnoreOnCI]
38+
public void TestAuthenticateOktaClientCredentialsUnauthorized()
39+
{
40+
AuthTestHelper authTestHelper = new AuthTestHelper();
41+
var parameters = AuthConnectionString.GetOAuthExternalClientCredentialParameters();
42+
parameters[SFSessionProperty.OAUTHCLIENTID] = "invalidClientId";
43+
_connectionString = AuthConnectionString.ConvertToConnectionString(parameters);
44+
45+
authTestHelper.ConnectAndExecuteSimpleQuery(_connectionString);
46+
authTestHelper.VerifyExceptionIsThrown("Error on getting an OAuth token from IDP:");
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)