diff --git a/samples/features/security/azure-active-directory-auth/token/App.config b/samples/features/security/azure-active-directory-auth/token/App.config new file mode 100644 index 00000000..a1637035 --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/App.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/features/security/azure-active-directory-auth/token/Program.cs b/samples/features/security/azure-active-directory-auth/token/Program.cs new file mode 100644 index 00000000..e34dad08 --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/Program.cs @@ -0,0 +1,38 @@ +using System; +using System.Data; +using System.Data.SqlClient; + +namespace ClinicService +{ + class Program + { + static void Main() + { + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(); + builder["Data Source"] = "aad-managed-demo.database.windows.net"; // replace with your server name + builder["Initial Catalog"] = "demo"; // replace with your database name + builder["Connect Timeout"] = 30; + + string accessToken = TokenFactory.GetAccessToken(); + if (accessToken == null) + { + Console.WriteLine("Fail to acuire the token to the database."); + } + using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) + { + try + { + connection.AccessToken = accessToken; + connection.Open(); + Console.WriteLine("Connected to the database"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + Console.WriteLine("Please press any key to stop"); + Console.ReadKey(); + } + } +} \ No newline at end of file diff --git a/samples/features/security/azure-active-directory-auth/token/Properties/AssemblyInfo.cs b/samples/features/security/azure-active-directory-auth/token/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..bab8f890 --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Token")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Token")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fd1b972e-0e8a-43df-94a4-5f9d0ae1a9aa")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/features/security/azure-active-directory-auth/token/Token.csproj b/samples/features/security/azure-active-directory-auth/token/Token.csproj new file mode 100644 index 00000000..86072916 --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/Token.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {FD1B972E-0E8A-43DF-94A4-5F9D0AE1A9AA} + Exe + Properties + Token + Token + v4.6 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.9.10826.1824\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + + + False + ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.9.10826.1824\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll + + + + + + + + + + + + + + + + + + + + Designer + + + + + \ No newline at end of file diff --git a/samples/features/security/azure-active-directory-auth/token/TokenFactory.cs b/samples/features/security/azure-active-directory-auth/token/TokenFactory.cs new file mode 100644 index 00000000..4f90a035 --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/TokenFactory.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Globalization; +using Microsoft.IdentityModel.Clients.ActiveDirectory; +using System.Net.Http; +using System.Threading; +using System.Net.Http.Headers; +using System.Web.Script.Serialization; +using System.Security.Cryptography.X509Certificates; +using System.Configuration; + +namespace ClinicService +{ + class TokenFactory + { + // + // The Client ID is used by the application to uniquely identify itself to Azure AD. + // The Cert Name is the subject name of the certificate used to authenticate this application to Azure AD. + // The Tenant is the name of the Azure AD tenant in which this application is registered. + // The AAD Instance is the instance of Azure, for example public Azure or Azure China. + // The Authority is the sign-in URL of the tenant. + // + private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"]; + private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"]; + private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"]; + private static string certName = ConfigurationManager.AppSettings["ida:CertName"]; + + static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant); + + // + // To authenticate to the To Do list service, the client needs to know the service's App ID URI. + // To contact the To Do list service we need it's URL as well. + // + private static string sqlDBResourceId = ConfigurationManager.AppSettings["sqldb:ResourceId"]; + //private static string todoListBaseAddress = ConfigurationManager.AppSettings["todo:TodoListBaseAddress"]; + + private static HttpClient httpClient = new HttpClient(); + private static AuthenticationContext authContext = null; + private static ClientAssertionCertificate certCred = null; + private static string token; + + public static string GetAccessToken() + { + // + // Create the authentication context to be used to acquire tokens. + // + authContext = new AuthenticationContext(authority); + + // + // Initialize the Certificate Credential to be used by ADAL. + // First find the matching certificate in the cert store. + // + + X509Certificate2 cert = null; + X509Store store = new X509Store(StoreLocation.CurrentUser); + try + { + store.Open(OpenFlags.ReadOnly); + // Place all certificates in an X509Certificate2Collection object. + X509Certificate2Collection certCollection = store.Certificates; + // Find unexpired certificates. + X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false); + // From the collection of unexpired certificates, find the ones with the correct name. + X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, false); + if (signingCert.Count == 0) + { + throw new Exception("Cannot find certificate: " + certName); + } + // Return the first certificate in the collection, has the right name and is current. + cert = signingCert[0]; + } + finally + { + store.Close(); + } + + // Then create the certificate credential. + certCred = new ClientAssertionCertificate(clientId, cert); + AcquireToken().Wait(); + return token; + } + + static async Task AcquireToken() + { + // + // Get an access token from Azure AD using client credentials. + // If the attempt to get a token fails because the server is unavailable, retry twice after 3 seconds each. + // + AuthenticationResult result = null; + int retryCount = 0; + bool retry = false; + token = null; + + do + { + retry = false; + try + { + // ADAL includes an in memory cache, so this call will only send a message to the server if the cached token is expired. + result = await authContext.AcquireTokenAsync(sqlDBResourceId, certCred); + } + catch (AdalException ex) + { + if (ex.ErrorCode == "temporarily_unavailable") + { + retry = true; + retryCount++; + Thread.Sleep(3000); + } + + Console.WriteLine( + String.Format("An error occurred while acquiring a token\nTime: {0}\nError: {1}\nRetry: {2}\n", + DateTime.Now.ToString(), + ex.ToString(), + retry.ToString())); + } + + } while ((retry == true) && (retryCount < 3)); + + if (result == null) + { + Console.WriteLine("Canceling attempt to contact To Do list service.\n"); + return; + } + + token = result.AccessToken; + Console.WriteLine("The access token obtained is:"); + Console.WriteLine("--------------------------------------------------"); + Console.WriteLine(result.AccessToken); + Console.WriteLine("--------------------------------------------------"); + } + } +} diff --git a/samples/features/security/azure-active-directory-auth/token/packages.config b/samples/features/security/azure-active-directory-auth/token/packages.config new file mode 100644 index 00000000..f20e584b --- /dev/null +++ b/samples/features/security/azure-active-directory-auth/token/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file