Establishing a Single Sign On Session with a vCenter Server
vSphere uses single sign on to provide a single point of authentication for clients. vSphere includes the vCenter Single Sign On Server. To use vCenter Single Sign On, your client obtains a SAML token (Security Assertion Markup Language) from the vCenter Single Sign On Server and passes the token to the vCenter Server in the login request. The token represents the client and contains claims that support client authentication. Components in the vSphere environment perform operations based on the original authentication. For information about obtaining a vCenter Single Sign On token from the vCenter Single Sign On Server, see vCenter Single Sign On Programming Guide.
To use single sign on, your client calls the LoginByToken method. Your client must send a SAML token to the vCenter Server by embedding the token in the SOAP header for the LoginByToken request. During the login sequence, your client must save and restore the HTTP session cookie. The vCenter Single Sign On SDK contains sample code that demonstrates how to use the LoginByToken method.
The following sections describe examples of using the LoginByToken method to establish a vCenter Single Sign On session with a vCenter Server.
LoginByToken (C# Example)
The following sections describe a C# example of using the LoginByToken method.
vCenter Server Single Sign On Session
After you obtain a SAML token from the vCenter Single Sign On Server, you can use the vSphere API method LoginByToken to establish a single sign on session with a vCenter Server. To establish a vCenter Server session that is based on SAML token authentication, the client must embed the SAML token in the SOAP header of the LoginByToken request. The C# LoginByToken example uses the following .NET services to support a single sign on session.
The sample creates a custom policy assertion derived from the SecurityPolicyAssertion class. The custom assertion contains the SAML token and X509 certificate.
The sample defines a custom output filter derived from the SendSecurityFilter class. The custom filter adds the token and certificate to the outgoing SOAP message.
The sample uses the ServicePointManager to specify SSL3 and HTTP 100-Continue behavior.
The sample uses the ConfigurationManager to specify certificate metadata (password and certificate type).
The sample uses the CookieContainer class to manage vCenter session cookies.
Persistent vCenter Sessions
A persistent vCenter session relies on a session cookie. When the vCenter Server receives a connection request (SessionManager.RetrieveServiceContent), the Server creates a session cookie and returns it in the HTTP header of the response. The client-side .NET framework embeds the cookie in HTTP messages that the client sends to the Server.
The LoginByToken request includes the SAML token and client certificate security assertions for client authentication. After successful login, the authentication overhead is no longer needed. The client resets the VimService context to eliminate the security overhead. Subsequent client requests will contain the session cookie, which is enough to support the persistent, authenticated session.
Sample Code
The code examples in the following sections show how to use the LoginByToken method with a holder-of-key security token. The code examples are based on the LoginByTokenSample project contained in the vCenter Single Sign On SDK. The project is located in the dotnet samples directory (SDK/ssoclient/dotnet/cs/samples/LoginByToken).
Project file LoginByToken.csproj
Sample code LoginByTokenSample.cs
SOAP header manipulation code CustomSecurityAssertionHok.cs
Using LoginByToken
The example program uses the following elements and general steps:
LoginByTokenSample Constructor
The LoginByTokenSample class constructor creates the following elements to set up access to the vCenter Server.
VimService object – Provides access to vSphere API methods and support for security policies and session cookie managment. It also stores the vCenter Server URL.
CookieContainer – Provides local storage for the vCenter session cookie.
ManagedObjectReference – Manually created ManagedObjectReference to retrieve a ServiceInstance at the beginning of the session.
The following code fragment shows the LoginByTokenSample constructor.
Example: LoginByTokenSample Constructor
 
// Global variables
private VimService _service;
private ManagedObjectReference _svcRef;
private ServiceContent _sic;
private string _serverUrl;
 
 
public LoginByTokenSample(string serverUrl)
{
_service = new VimService();
_service.Url = serverUrl;
_serverUrl = serverUrl;
_service.CookieContainer = new System.Net.CookieContainer();
_svcRef = new ManagedObjectReference();
_svcRef.type = "ServiceInstance";
_svcRef.Value = "ServiceInstance";
}
Token Acquisition
The client must obtain a SAML token from a vCenter Single Sign On Server. See the vCenter Single Sign On Programming Guide.
Security Policies
The LoginByToken sample creates a custom policy assertion that is derived from the .NET class SecurityPolicyAssertion. The assertion class gives the .NET framework access to the SAML token and the X509 certificate.
The sample performs the following operations to set up the security policy and message handling.
Sets the ServicePointManager properties to specify SSL3 and HTTP 100-Continue response handling. 100-Continue response handling supports more efficient communication between the client and vCenter Server. When the client-side .NET framework sends a request to the Server, it sends the request header and waits for a 100-Continue response from the Server. After it receives that response, it sends the request body to the Server.
Creates an X509Certificate2 object, specifies the certificate file, and imports the certificate. The certificate file specification indicates a PKCS #12 format file (Public-Key Cryptography Standards) – PfxCertificateFile. The file contains the client’s private key and public certificate. The PfxCertificateFile setting is defined in the app.config file in the LoginByToken project. The definition specifies the location of the file.
Custom Security Assertion
The following code fragment shows the LoginByTokenSample class method GetSecurityPolicyAssertionForHokToken. The method returns a CustomSecurityAssertionHok instance which overrides the .NET class SecurityPolicyAssertion. The security assertion contains the SAML token and the X509 certificate token. This code is taken from the LoginByToken project file samples/LoginByToken/CustomSecurityAssertionHok.cs.
Example: Setting Up Security Policies
 
private SecurityPolicyAssertion GetSecurityPolicyAssertionForHokToken(XmlElement xmlToken)
{
 
//When this property is set to true, client requests that use the POST method
//expect to receive a 100-Continue response from the server to indicate that
//the client should send the data to be posted. This mechanism allows clients
//to avoid sending large amounts of data over the network when the server,
//based on the request headers, intends to reject the request
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
 
X509Certificate2 certificateToBeAdded = new X509Certificate2();
string certificateFile = ConfigurationManager.AppSettings["PfxCertificateFile"];
string password = ConfigurationManager.AppSettings["PfxCertificateFilePassword"];
certificateToBeAdded.Import(certificateFile, password ?? string.Empty, X509KeyStorageFlags.MachineKeySet);
 
var customSecurityAssertion = new CustomSecurityAssertionHok();
customSecurityAssertion.BinaryToken = xmlToken;
customSecurityAssertion.TokenType = strSamlV2TokenType;
customSecurityAssertion.SecurityToken = new X509SecurityToken(certificateToBeAdded);
 
return customSecurityAssertion;
}
Custom Output Filter
The following code fragment shows the custom output filter for the custom security assertion. The custom filter provides three methods:
CustomSecurityClientOutputFilterHok class constructor – Creates token and message signature objects for the SOAP message.
SecureMessage – An override method for the .NET method SendSecurityFilter.SecureMessage. The override method adds the SAML token and message signature to the .NET Security element.
CreateKeyInfoSignatureElement – Creates an XML document that specifies the SAML token type and ID.
Example: Output Filter for the Custom SecurityPolicyAssertion
 
internal class CustomSecurityClientOutputFilterHok : SendSecurityFilter
{
IssuedToken issuedToken = null;
string samlAssertionId = null;
MessageSignature messageSignature = null;
 
/// Create a custom SOAP request filter.
/// (Save the token and certificate.)
public CustomSecurityClientOutputFilterHok(CustomSecurityAssertionHok parentAssertion)
: base(parentAssertion.ServiceActor, true)
{
issuedToken = new IssuedToken(parentAssertion.BinaryToken, parentAssertion.TokenType);
samlAssertionId = parentAssertion.BinaryToken.Attributes.GetNamedItem("ID").Value;
messageSignature = new MessageSignature(parentAssertion.SecurityToken);
}
 
/// Secure the SOAP message before its sent to the server.
public override void SecureMessage(SoapEnvelope envelope, Security security)
{
//create KeyInfo XML element
messageSignature.KeyInfo = new KeyInfo();
messageSignature.KeyInfo.LoadXml(CreateKeyInfoSignatureElement());
 
security.Tokens.Add(issuedToken);
security.Elements.Add(messageSignature);
}
 
/// Helper method to create a custom key info signature element.
/// Returns Key info XML element.
private XmlElement CreateKeyInfoSignatureElement()
{
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(@"<root><SecurityTokenReference
xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""
xmlns:wsse=""http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd""
wsse:TokenType=""http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"">
<KeyIdentifier xmlns=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""
ValueType=""http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID"">" + samlAssertionId +
@"</KeyIdentifier></SecurityTokenReference></root>");
return xmlDocument.DocumentElement;
}
}
Connection and Login
The following code fragment performs the following actions:
Calls the LoginByTokenSample class method GetSecurityPolicyAssertionForHokToken (see Security Policies) and adds the security policy to the VimService object.
The VimService object contains the following data:
Calls the RetrieveServiceContent method. The method establishes the connection with the vCenter Server and it returns a session cookie. The session cookie is stored in the cookie container in the VimService object.
Calls the LoginByToken method. The .NET framework uses the security policy assertion and the session cookie to construct the login request.
Calls the LoginByTokenSample class method resetService to create a new VimService object.
Example: Connection and Login
 
// Construct the security policy assertion
SecurityPolicyAssertion securityPolicyAssertion = null;
securityPolicyAssertion = GetSecurityPolicyAssertionForHokToken(xmlToken);
 
// Setting up the security policy for the request
Policy policySAML = new Policy();
policySAML.Assertions.Add(securityPolicyAssertion);
 
// Setting policy of the service
_service.SetPolicy(policySAML);
 
_sic = _service.RetrieveServiceContent(_svcRef);
if (_sic.sessionManager != null)
{
_service.LoginByToken(_sic.sessionManager, null);
}
resetService();
 
The following code fragment shows the resetService method. The method creates a new VimService object and a new cookie container. The method also obtains a new instance of the session cookie.
Example: The resetService method
 
/// Resetting the VimService without the security policies
/// as we need the policy only for the LoginByToken method
/// and not the other method calls. resetService also maintains the
/// authenticated session cookie post LoginByToken.
///
/// This method needs to be called only after successful
/// login
private void resetService()
{
var _cookie = getCookie();
_service = new VimService();
_service.Url = _serverUrl;
_service.CookieContainer = new CookieContainer();
if (_cookie != null)
{
_service.CookieContainer.Add(_cookie);
}
}
 
 
/// Method to save the session cookie
private Cookie getCookie()
{
if (_service != null)
{
var container = _service.CookieContainer;
if (container != null)
{
var _cookies = container.GetCookies(new Uri(_service.Url));
if (_cookies.Count > 0)
{
return _cookies[0];
}
}
}
return null;
}
LoginByToken (Java Example)
The following example is based on the LoginByTokenSample.java file contained in the vCenter Single Sign On SDK. The SDK contains Java code that supports HTTP and SOAP header manipulation.
Client Support for a vCenter Single Sign On Session with a vCenter Server
After you obtain a SAML token from the vCenter Single Sign On Server, you can use the vSphere API method LoginByToken to establish a vCenter Single Sign On session with a vCenter Server. At the beginning of the session, your client is responsible for the following tasks:
Maintain the vCenter session cookie. During the initial connection, the Server produces an HTTP session cookie to support the persistent connection. Operations during the login sequence will reset the request context so your client must save this cookie and re-introduce it at the appropriate times.
The example program uses these general steps:
1
Call the RetrieveServiceContent method to establish an HTTP connection with the vCenter Server and save the HTTP session cookie. The client uses an HTTP header handler method to extract the cookie from the vCenter Server response.
2
Call the LoginByToken method to authenticate the vCenter session. To send the token to the vCenter Server, the client uses a handler to embed the token and a time stamp in the SOAP header for the message. To identify the session started with the RetrieveServiceContent method, the client uses a handler to embed the session cookie in the HTTP header.
3
HTTP and SOAP Header Handlers
To use a vCenter Single Sign On token to login to a vCenter Server, the example uses header handlers to manipulates the HTTP and SOAP header elements of the login request. After establishing a handler, subsequent requests automatically invoke the handler.
An extraction handler obtains the HTTP session cookie provided by the vCenter Server. After setting up the handler, a call to the RetrieveServiceContent method will invoke the handler to extract the cookie from the Server response.
The following figure shows the use of handlers to manipulate header elements when establishing a vCenter Single Sign On session with a vCenter Server.
Starting a vCenter Session
 
Important Every call to the vCenter Server will invoke any message handlers that have been established. The overhead involved in using the SOAP and HTTP message handlers is not necessary after the session has been established. The example saves the default message handler before setting up the SOAP and HTTP handlers. After establishing the session, the example will reset the handler chain and restore the default handler.
The example code also uses multiple calls to the VimPortType.getVimPort method to manage the request context. The getVimPort method clears the HTTP request context. After each call to the getVimPort method, the client resets the request context endpoint address to the vCenter Server URL. After the client has obtained the session cookie, it will restore the cookie in subsequent requests.
Sample Code
The code examples in the following sections show how to use the LoginByToken method with a holder-of-key security token. The code examples are based on the sample code contained in the vCenter Single Sign On SDK. The files are located in the Java samples directory (SDK/ssoclient/java/JAXWS/samples):
samples/com/vmware/vsphere/samples/LoginByTokenSample.java
samples/com/vmware/vsphere/soaphandlers/HeaderCookieHandler.java
samples/com/vmware/vsphere/soaphandlers/HeaderCookieExtractionHandler.java
SOAP header handlers. These are the same handlers that are used in the vCenter Single Sign On example in vCenter Single Sign On Programming Guide. The SOAP handler files are contained in the vCenter Single Sign On SDK and are located in the sso client soaphandlers directory:
SDK/ssoclient/java/JAXWS/samples/com/vmware/sso/client/soaphandlers
Saving the vCenter Server Session Cookie
The code fragment in this section establishes an HTTP session with the vCenter Server and saves the HTTP session cookie.
The following sequence describes these steps and shows the corresponding objects and methods.
1
Use the getHandlerResolver method to save the default message handler. To use the HTTP and SOAP message handlers, you must first save the default message handler so that you can restore it after login. The HTTP and SOAP message handlers impose overhead that is unneccessary after login.
2
Set the cookie handler. The HeaderCookieExtractionHandler method retrieves the HTTP cookie.
3
Get the VIM port. The VIM port provides access to the vSphere API methods, including the LoginByToken method.
5
Retrieve the ServiceContent. This method establishes the HTTP connection and sets the session cookie.
The following example shows Java code that saves the session cookie.
Example: Saving the vCenter Server Session Cookie
/*
* The example uses a SAML token (obtained from a vCenter Single Sign On Server)
* and the vCenter Server URL.
* The following declarations indicate the datatypes; the token datatype (Element) corresponds
* to the token datatype returned by the vCenter Single Sign On Server.
*
* Element token; -- from vCenter Single Sign On Server
* String vcServerUrl; -- identifies vCenter Server
*
* First, save the default message handler.
*/
 
HandlerResolver defaultHandler = vimService.getHandlerResolver();
 
/*
* Create a VIM service object.
*/
vimService = new VimService();
 
/*
* Construct a managed object reference for the ServiceInstance.
*/
ManagedObjectReference SVC_INST_REF = new ManagedObjectReference();
SVC_INST_REF.setType("ServiceInstance");
SVC_INST_REF.setValue("ServiceInstance");
 
/*
* Create a handler resolver.
* Create a cookie extraction handler and add it to the handler resolver.
* Set the VIM service handler resolver.
*/
HeaderCookieExtractionHandler cookieExtractor = new HeaderCookieExtractionHandler();
HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver();
handlerResolver.addHandler(cookieExtractor);
vimService.setHandlerResolver(handlerResolver);
 
/*
* Get the VIM port for access to vSphere API methods. This call clears the request context.
*/
vimPort = vimService.getVimPort();
 
/*
* Get the request context and set the connection endpoint.
*/
Map<String, Object> ctxt = ((BindingProvider) vimPort) .getRequestContext();
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, vcServerUrl);
ctxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
 
/*
* Retrieve the ServiceContent. This call establishes the HTTP connection.
*/
serviceContent = vimPort.retrieveServiceContent(SVC_INST_REF);
 
/*
* Save the HTTP cookie.
*/
String cookie = cookieExtractor.getCookie();
Using LoginByToken
The code fragment in this section sets up the message handlers and calls the LoginByToken method. The following sequence describes the steps and shows the corresponding objects and methods.
1
Create a new HeaderHandlerResolver. Then set the message security handlers for cookie insertion and for inserting the SAML token and credentials in the SOAP header.
4
Call the LoginByToken method. The method invocation executes the handlers to insert the elements into the message headers. The method authenticates the session referenced by the session cookie.
 
 
 
The following examples shows Java code that calls the LoginByToken method.
Example: Using LoginByToken
/*
* Create a handler resolver and add the handlers.
*/
HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver();
handlerResolver.addHandler(new TimeStampHandler());
handlerResolver.addHandler(new SamlTokenHandler(token));
handlerResolver.addHandler(new HeaderCookieHandler(cookie));
handlerResolver.addHandler(new WsSecuritySignatureAssertionHandler(
userCert.getPrivateKey(),
userCert.getUserCert(),
Utils .getNodeProperty(token, "ID")));
vimService.setHandlerResolver(handlerResolver);
 
/*
* Get the Vim port; this call clears the request context.
*/
vimPort = vimService.getVimPort();
 
/*
* Retrieve the request context and set the server URL.
*/
Map<String, Object> ctxt = ((BindingProvider) vimPort) .getRequestContext();
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, vcServerUrl);
ctxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
 
/*
* Call LoginByToken.
*/
UserSession us = vimPort.loginByToken( serviceContent.getSessionManager(), null);
Restoring the vCenter Server Session Cookie
After you log in, you must restore the standard vCenter session context. The code fragment in this section restores the default message handler and the session cookie. As the cookie handler has been replaced by the default handler, the client resets the session cookie by calling request context methods to access the context fields directly. The following sequence describes these steps and shows the corresponding objects and methods.
 
1
Restore the default message handler. The handlers used for LoginByToken are not used in subsequent calls to the vSphere API.
The following example shows Java code that restores the vCenter session. This code requires the vCenter URL and the cookie and default handler that were retrieved before login. See Sample Code.
Example: Restoring the vCenter Server Session
 
/*
* Reset the default handler. This overwrites the existing handlers, effectively removing them.
*/
vimService.setHandlerResolver(defaultHandler);
vimPort = vimService.getVimPort();
 
/*
* Restore the connection endpoint in the request context.
*/
// Set the validated session cookie and set it in the header for once,
// JAXWS will maintain that cookie for all the subsequent requests
Map<String, Object> ctxt = ((BindingProvider) vimPort) .getRequestContext();
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, vcServerUrl);
ctxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
 
/*
* Reset the cookie in the request context.
*/
Map<String, List<String>> headers = (Map<String, List<String>>) ctxt.get(MessageContext.HTTP_REQUEST_HEADERS);
if (headers == null) {
headers = new HashMap<String, List<String>>();
}
headers.put("Cookie", Arrays.asList(cookie));
ctxt.put(MessageContext.HTTP_REQUEST_HEADERS, headers);