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();
}
}
}
}
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