Windows Azure Pack Authentication Using Node.js

459 views Asked by At

I am trying to customize Windows Azure Pack using REST API. To authenticate the API, I need the token. I am able run the SampleAuthApplication (Code below), but that is in .NET . I want to implement the same thing in Node.js

Here is the Program.cs

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Net;
using System.Net.Security;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;

namespace RDFEAPICallsv2
{
    class Program
    {
        static void Main(string[] args)
        {
            //// Replace these values 
            string EnvironmentToUse = "https://wap";
            string domainName = "anant.ad";
            string userName = "anant/administrator";
            string password = "pass@word";

            ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(delegate { return true; });

            //// for Admin Authentication
            string windowsAuthSiteEndPoint = EnvironmentToUse + ":30072";
            string token = GetWindowsAuthToken(windowsAuthSiteEndPoint, domainName, userName, password, true);

            //// for Tenant Authentication
            string tenantServiceEndpoint = EnvironmentToUse + ":30071";
            //string tenantToken = GetAspAuthToken(tenantServiceEndpoint, userName, password);


            // Use the tokens obtained above in the Http client to make the calls

            //var httpClient = new HttpClient();
            //httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }


        public static string GetWindowsAuthToken(string windowsAuthSiteEndpoint, string domainName, string userName, string password, bool shouldImpersonate)
        {
            string token = null;
                    Impersonation.Impersonate(domainName, userName, password, () => token = GetWindowsToken(windowsAuthSiteEndpoint));
            return token;
        }

        private static string GetWindowsToken(string windowsAuthSiteEndPoint)
        {
            var identityProviderEndpoint = new EndpointAddress(new Uri(windowsAuthSiteEndPoint + "/wstrust/issue/windowstransport"));
            var identityProviderBinding = new WS2007HttpBinding(SecurityMode.Transport);
            identityProviderBinding.Security.Message.EstablishSecurityContext = false;
            identityProviderBinding.Security.Message.ClientCredentialType = MessageCredentialType.None;
            identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

            var trustChannelFactory = new WSTrustChannelFactory(identityProviderBinding, identityProviderEndpoint)
            {
                TrustVersion = TrustVersion.WSTrust13,
            };

            trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication() { CertificateValidationMode = X509CertificateValidationMode.None };
            var channel = trustChannelFactory.CreateChannel();

            var rst = new RequestSecurityToken(RequestTypes.Issue)
            {
                AppliesTo = new EndpointReference("http://azureservices/AdminSite"),
                KeyType = KeyTypes.Bearer,
            };

            RequestSecurityTokenResponse rstr = null;
            SecurityToken token = null;

            token = channel.Issue(rst, out rstr);
            var tokenString = (token as GenericXmlSecurityToken).TokenXml.InnerText;
            var jwtString = Encoding.UTF8.GetString(Convert.FromBase64String(tokenString));

            return jwtString;
        }


        static string GetAspAuthToken(string authSiteEndPoint, string userName, string password)
        {

            var identityProviderEndpoint = new EndpointAddress(new Uri(authSiteEndPoint + "/wstrust/issue/usernamemixed"));

            var identityProviderBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
            identityProviderBinding.Security.Message.EstablishSecurityContext = false;
            identityProviderBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

            var trustChannelFactory = new WSTrustChannelFactory(identityProviderBinding, identityProviderEndpoint)
            {
                TrustVersion = TrustVersion.WSTrust13,
            };
            //This line is only if we're using self-signed certs in the installation 
            trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication() { CertificateValidationMode = X509CertificateValidationMode.None };

            trustChannelFactory.Credentials.SupportInteractive = false;
            trustChannelFactory.Credentials.UserName.UserName = userName;
            trustChannelFactory.Credentials.UserName.Password = password;

            var channel = trustChannelFactory.CreateChannel();
            var rst = new RequestSecurityToken(RequestTypes.Issue)
            {
                AppliesTo = new EndpointReference("http://azureservices/TenantSite"),
                TokenType = "urn:ietf:params:oauth:token-type:jwt",
                KeyType = KeyTypes.Bearer,
            };

            RequestSecurityTokenResponse rstr = null;
            SecurityToken token = null;


            token = channel.Issue(rst, out rstr);
            var tokenString = (token as GenericXmlSecurityToken).TokenXml.InnerText;
            var jwtString = Encoding.UTF8.GetString(Convert.FromBase64String(tokenString));

            return jwtString;
        }
    }
}

This is Impersonation.cs

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;

namespace RDFEAPICallsv2
{
    public static class Impersonation
    {
        public enum LogonType
        {
            /// <summary>
            /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on  
            /// by a terminal server, remote shell, or similar process.
            /// This logon type has the additional expense of caching logon information for disconnected operations; 
            /// therefore, it is inappropriate for some client/server applications,
            /// such as a mail server.
            /// </summary>
            LOGON32_LOGON_INTERACTIVE = 2,

            /// <summary>
            /// This logon type is intended for high performance servers to authenticate plaintext passwords.
            /// The LogonUser function does not cache credentials for this logon type.
            /// </summary>
            LOGON32_LOGON_NETWORK = 3,

            /// <summary>
            /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without 
            /// their direct intervention. This type is also for higher performance servers that process many plaintext
            /// authentication attempts at a time, such as mail or Web servers. 
            /// The LogonUser function does not cache credentials for this logon type.
            /// </summary>
            LOGON32_LOGON_BATCH = 4,

            /// <summary>
            /// Indicates a service-type logon. The account provided must have the service privilege enabled. 
            /// </summary>
            LOGON32_LOGON_SERVICE = 5,

            /// <summary>
            /// This logon type is for GINA DLLs that log on users who will be interactively using the computer. 
            /// This logon type can generate a unique audit record that shows when the workstation was unlocked. 
            /// </summary>
            LOGON32_LOGON_UNLOCK = 7,

            /// <summary>
            /// This logon type preserves the name and password in the authentication package, which allows the server to make 
            /// connections to other network servers while impersonating the client. A server can accept plaintext credentials 
            /// from a client, call LogonUser, verify that the user can access the system across the network, and still 
            /// communicate with other servers.
            /// NOTE: Windows NT:  This value is not supported. 
            /// </summary>
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8,

            /// <summary>
            /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
            /// The new logon session has the same local identifier but uses different credentials for other network connections. 
            /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
            /// NOTE: Windows NT:  This value is not supported. 
            /// </summary>
            LOGON32_LOGON_NEW_CREDENTIALS = 9,
        }

        public enum LogonProvider
        {
            /// <summary>
            /// Use the standard logon provider for the system. 
            /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name 
            /// is not in UPN format. In this case, the default provider is NTLM. 
            /// NOTE: Windows 2000/NT:   The default security provider is NTLM.
            /// </summary>
            LOGON32_PROVIDER_DEFAULT = 0,

            /// <summary>
            /// Win NT 3.5
            /// </summary>
            LOGON32_PROVIDER_WINNT35 = 1,

            /// <summary>
            /// Win NT 4.0
            /// </summary>
            LOGON32_PROVIDER_WINNT40 = 2,

            /// <summary>
            /// Win NT 5.0
            /// </summary>
            LOGON32_PROVIDER_WINNT50 = 3
        }

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool LogonUser(
            string username,
            string domain,
            string password,
            LogonType logonType,
            LogonProvider logonProvider,
            out SafeTokenHandle token);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CloseHandle(IntPtr handle);

        public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle()
                : base(true)
            {
            }

            protected override bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }

        public static void Impersonate(string domain, string username, string password, Action handler)
        {
            SafeTokenHandle safeHandle;
            var result = LogonUser(
                username,
                domain == null ? "." : domain,
                password,
                LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
                LogonProvider.LOGON32_PROVIDER_WINNT50,
                out safeHandle);

            if (!result)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new Win32Exception(ret);
            }

            using (safeHandle)
            using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(safeHandle.DangerousGetHandle()))
            {
                handler();
            }
        }
    }
}

1

There are 1 answers

1
Will Shao - MSFT On BEST ANSWER

identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;

According to your WCF code, seems that this sample used the windows authentication. If you wanna use node.js to achieve to this result, you maybe need to use the passport.js(https://github.com/auth0/passport-windowsauth) implement the windows authentication. Also, you can use other idp to authentication your application. Please refer to the official document :https://msdn.microsoft.com/en-us/library/dn508295.aspx