SSO w/ External App

We are starting to look into Single Sign On (SSO). Seems to work fine with the Epicor client. Where we are running into issues is when we want our external (custom) apps to connect to Epicor 10 w/ SSO. We have it working with the normal Username/Password login where you create a new session object and go from there. We are unsure how to create a new session with SSO turned on. Any help would be greatly appreciated.

What method of connectino are you using? WCF Services? DLL’s? REST?

DLLs. We bring in Ice.Core.Session.dll currently to create a new session.

Try this, using the DLL’s

var wcfBinding = NetTcp.UsernameWindowsChannel();
	var appServer = new Uri("net.tcp://localhost/epicor10/erp/bo/part.svc");
	using (var partClient = new PartImpl(wcfBinding, appServer))
	{
		partClient.ClientCredentials.UserName.UserName = "Manager";
		partClient.ClientCredentials.UserName.Password = "Epicor123";
		bool morePages;
		var myPartDataset = partClient.GetList("", 10, 1, out morePages);
		foreach (var partRec in myPartDataset.PartList.Rows.Cast<PartListDataSet.PartListRow>())
		{
			Console.WriteLine(partRec.PartNum);
		}
		partClient.Close();
	}

You can change the binding from UserNameWindowsChannel() to just WindowsChannel to use Windows Auth

1 Like

Oh ok. So I dont technically create a Session?

Right, you shouldn’t need to make a session object.

Session Objects are optional but handy in certain circumstances. The system is designed to walk straight up to with credentials and the server will stand up a ‘temporary session’ - meaning it is never cached between calls. This causes a little overhead but makes it possible to do some ‘saga’ scenarios as you iterate companies or plants and workstations and… you get the idea.
For the most part you should be able to walk up with the company and plant in a header and get what you want done.

2 Likes

We have set up the SSO and can create, for example, a job. But that job is only created in the plant we are logged into. We need to be able to change the plants that they are working in because they can work in multiple plants and must be able to switch plants. Bart_Elia said it creates a ‘temporary session’ which you can iterate companies or plants. Are these temporary sessions available with SSO? How do we get access to that temporary session and/or change plants? We also noticed we can pull jobs from plants we don’t have access to so it doesn’t seem to have the restriction as within Epicor of pulling just the records in the plant the user has access to.

We got this figured out. We can change the plant/company using the UserFile dll.

1 Like

Also take a look at Ice.Lib.SessionMod service to manage the state of the Session variables such as company and plant, etc.

It’s the legacy pattern of having a session act as a saga over the life of many calls where a client is conversing with a server. We are moving to a more declarative approach in REST where you can specify these values in headers.This makes life easier on those trying to integrate from devices. It’s all about IoT these days after all :wink:

1 Like

Can you maybe give us an example of how to use the SessionMod with SSO? I am unsure how to “Login” and get a Session created with that library.

Login is a bit of a misname. Think of it as ‘Start Caching SessionID’ instead and it may make more sense.

The use of SSO versus anything else makes no difference to the handling of a session. SSO is for Identity of the user. You can run ‘sessionless’ with SSO without an issue.

Sessions are just used to build up what is the current state of the client interaction with the server instead of having to declare every session variable on every call (And we don’t expose every session variable in headers yet so session still required in some scenarios).

For example, with a session you can login as company ‘A’, set the plant, the workstation, reserve a license, etc as you make dozens of calls over hours.

Without a session you have to declare the company and plant explicitly in the header and hope you get a license (or fall into license delay penalties).

Both are good, just depends on your needs. We try to give you options though that can be more difficult to learn. It’s the classic power/ease of use trade off :confused:

If you have specific needs, first start by turning on the client trace and watching which methods fly by. You might see a call to 'SessionMod.SetCompany or the like in the trace and you’ll know what you need to call in the integration you are doing.

I have a love/hate with the tracing tool. I love the power and complete transparency to assist everyone. Then a little embarrassment at our 30 year old API showing some quirks from time to time. I’ll live with groaning over something missed in a code review 20 years ago and left to support backwards compat though to let folks do their jobs.

Oh ok. That helps some. I believe we were doing the “sessionless” SSO. We are looking for a way to connect to Epicor then if we need to, change the plant/company.

Maybe where I am stuck is this – Let’s say I use the SessionMod and call .Login(). That gives me a guid which is the session id. What if I already have the session object (or the session id) and I want to change the plant/company. How do I get the SessionMod from an existing Session. I don’t want to create a new session just to change the plant.

Some background context to my weird thinking – We have a DLL we are making that will handle Epicor connection for external (custom) apps. We’re trying to support SSO and non-SSO. Our non-SSO stuff works nice because we can instantiate a new Session object with the Epicor Username/Password. SSO is new to us so we are struggling a little. We have it working by connecting with the Windows account, but we want to add logic to be able to change the plant or company for either SSO and non-SSO.

Session.PlantID on the client Session object will call the servers SessionMod service SetPlant() method. I think that is what you are looking for in a ‘saga’ scenario.

For ‘sessionless’, you need to set PlantID on the CallSettings header. This also works in REST.

Do you have any examples? I tried both and failed:

Setting Session.PlantID to a new value did not update my current plant in the system. Or does it only set it for that session but it doesnt persist it?

The sessionless way didnt work because the CallSettings header was null when I instantiated the SessionMod object. Below are some examples of what I did. My “GetClient” method is just a helper to instantiate an ‘Impl’ object generically. So I can use it for other stuff like JobHeadImpl.

Session Way:

var session = new Session("Username", "Password", "", Session.LicenseType.Default, sysConfigPath, false, initialCompany, initialPlant);
session.PlantID = "SomeOtherPlantId";

Sessionless Way:

var sessionModClient = conn.GetClient();
var guid = sessionModClient.Login();
sessionModClient.SessionID = guid;

sessionModClient.CallSettings.Plant = "SomeOtherPlant";

sessionModClient.Logout();

This is the ‘old way’ of calling through the client Session object and calling services via the client proxies. It’s basically how the client works for the Rich Client and for EWA.

The negative to this approach is keeping the client dlls in sync and having to take references to each server assembly you want to talk to. It has the most power though so … trade offs…

In a csproj, add a reference to
*Epicor.ServiceModel
*Ice.Core.Session
*A Contract of interest (SessionMod in this case but any server one)

class Program
{
static void Main(string[] args)
{
RunViaSession(“manager”, “Epicor123”, “net.tcp://localhost/ICE3”);
}

    private static void RunViaSession(string userID, string password, string appServerUrl)
    {
        using (var session = new Session(userID, password, appServerUrl))
        {
            //Can just set Session Properties via the client framework
            session.PlantID = "foo";

            //Run some server code
            RunViaImpl(session);
        }
    }

    private static void RunViaImpl(Session session)
    {
        //Can stand up any client proxy and call services
        using (var service = WCFServiceSupport.CreateImpl<SessionModImpl>(session, SessionModImpl.UriPath))
        {
            var sessionID = service.Login();
            Console.WriteLine(sessionID);
        }
    }
}

Hey @Bart_Elia quick question.

Let’s say for whatever reason I would like to run the following on AbcCode Entry:

using (var session = new Ice.Core.Session("SA_GLOBAL", "testPw"))
{
	// Declare and Initialize Variables
	string BAQName = "SMI-zGetDataTagByPartRev";
	Ice.BO.QueryExecutionDataSet ds = new Ice.BO.QueryExecutionDataSet();

	// Add Parameter Rows
	ds.ExecutionParameter.AddExecutionParameterRow("PartNum", partNum, "nvarchar", false, Guid.Empty, "A");
	ds.ExecutionParameter.AddExecutionParameterRow("RevisionNum", partRev, "nvarchar", false, Guid.Empty, "A");

	// Use Business Object Directly
	Ice.Proxy.BO.DynamicQueryImpl dynamicQuery = Ice.Lib.Framework.WCFServiceSupport.CreateImpl<Ice.Proxy.BO.DynamicQueryImpl>(session, Epicor.ServiceModel.Channels.ImplBase<Ice.Contracts.DynamicQuerySvcContract>.UriPath);
	results = dynamicQuery.ExecuteByID(BAQName, ds);

}

Once the Dispose is called it Closes my AbcCode Entry Form as well, is there any way to have it leave my Entry Form alone?

I also tried stuff like

ILauncher iLnch = new ILauncher( new Ice.Core.Session("SA_GLOBAL", "testPw!") );
EpiTransaction epit = new EpiTransaction(iLnch);

Also calling .Dispose() manually, it always tends to kill the Entry Form because the OnSessionClosing event somehow has the form targeted.

Sorry, way out of my expertise area. @Rich is your expert here.

@Rich - are there any caveats to getting sessionMod via reflection and calling sessionMod.Logout() to release the license - and then dispose of the session completely in form dispose?

on second thought - i wonder if it will throw an exception on dispose saying the session is not logged in

@Chris_Conn suggested to use reflection to call sessionMod.Logout() it did release the session, but couldnt dispose still
@josecgomez used reflection to remove the Dispose event where Target Contains the Form Name, that allowed me to use using() and Dispose without a problem.

I can also Dispose a global Session object in Form_Closing()