In the current authentication framework (upto IS 5.1.0), we had endpoints for each type of request. As an example we had samlsso endpoint (https://localhost:9443/samlsso) to handle samlsso requests, oauth endpoint for oauth (https://localhost:9443/oauth) etc. So we had servlets in the endpoint hosted, waiting to receive those requests. But at each servlet we did few common things. The current SAMLSSOProviderServlet can be found at [1]. There, all the logic is written through the handleRequest() method. From the work done here we can identify two kind of necessary work in the flow.
- Unique workload needed in SAML flow
- SAML request validation
- DTO, DO and DAO preparation
- SAML response building
- Common workload for every endpoint.
- Building authentication request
- Sending request to frameowrk for login, logout etc
- Handling response form framework after login and logout
It is obvious, implementing common-things at every endpoint is unnecessary. There fore the IS team has come up with a new framework which will give the facility to develop custom "endpoints" (processors) easier than before. Actually there is no more endpoints for different requests. These servlets will be replace by "processors" which is equivalent to old servlets.
Ok lets see what is new in the authentication framework. In new Identity Server there is no multiple endpoints. Only endpoint is "identity" endpoint, https://localhost:9443/identity. Lets see how we are going to replace the "samlsso" endpoint with the new processor (with "identity endpoint"). The code for new SAML endpoint can be found at [2].
We will use travelocity.com [3] for example. The flow in the current implementation using SAML endpoint is as follows. The same should happen in the new implementation also.
We will use travelocity.com [3] for example. The flow in the current implementation using SAML endpoint is as follows. The same should happen in the new implementation also.
Everything above was handled starting from a dedicated servlet called 'SAMLSSOProviderServlet'. In the travelocity.properties "SAML2.IdPURL" is set to value "https://localhost:9443/samlsso." Now it should be set to "https://localhost:9443/identity". So the IdentityServlet will receive the request. But it doesn't know how to handle this request. Starting from there everything above should be implemented by us. For that we need to understand the flow in current Identity Servlet.
Now we can easily look at how we are going to replace the SAMLSSOProviderServlet with IdentityServlet, using the Factory and Builder pattern introduced into new framework in IS 5.3.0.
We should build an SAMLIdentityRequest object which includes the necessary parameters and functionalities using the HTTPServletRequest. For that we need an SAMLIdentityRequest.SAMLIdentityRequestBuilder. For that we need an SAMLIdentityRequestFactory. [4] Following methods should be implemented in order to cooperate with the framework.
As you can see in the sequence diagram, all the builders are inner classes in the respective entity. I.E IdentityRequestBuilder is an inner class of IdentityRequest. Hence our SAMLIdentityRequestBuilder is also an inner class of SAMLIdentityRequest. Both are extended respectively from IdentityRequest.IdentityRequestBuilder and IdentityRequest.
The HTTPIdentityRequestFactory, HTTPResponseFactory, IdentityProcessor should be registered in the OSGI context. All these three entities has a method "canhandle", which will decide whether the respective entity can handle the given scenario. In Identity Framework all of these will be stored in a respective list and this list will be iterated in the order of priority and the method "canhandle" will be called.
We should build an SAMLIdentityRequest object which includes the necessary parameters and functionalities using the HTTPServletRequest. For that we need an SAMLIdentityRequest.SAMLIdentityRequestBuilder. For that we need an SAMLIdentityRequestFactory. [4] Following methods should be implemented in order to cooperate with the framework.
Methods to be implemented in SAMLIdentityRequestFactory
canhandle
Check whether the HttpServletRequest is a SAML request. Here simply we have checked whether the request has a query parameter "SAMLRequest"
getPriority
The priority of the RequestFactory decides the index in the HTTPIdentityRequestFactory list, which is added to OSGI context throughout all the components. We have given a high priority to ake it sure that other RequestFactory is not picked before our SAMLIdentityRequestFactory. If a wrong factory is picked up it will end upwith a runtime exception.
create
This should create the SAMLIdentityRequestBuilder with the needed properties assigned.
handleException
Decides how the exception should be handled and send the HTTPResponseBuilder. Here we can decide whether ti be redirected to another location etc. things.
canhandle
Check whether the HttpServletRequest is a SAML request. Here simply we have checked whether the request has a query parameter "SAMLRequest"
getPriority
The priority of the RequestFactory decides the index in the HTTPIdentityRequestFactory list, which is added to OSGI context throughout all the components. We have given a high priority to ake it sure that other RequestFactory is not picked before our SAMLIdentityRequestFactory. If a wrong factory is picked up it will end upwith a runtime exception.
create
This should create the SAMLIdentityRequestBuilder with the needed properties assigned.
handleException
Decides how the exception should be handled and send the HTTPResponseBuilder. Here we can decide whether ti be redirected to another location etc. things.
As you can see in the sequence diagram, all the builders are inner classes in the respective entity. I.E IdentityRequestBuilder is an inner class of IdentityRequest. Hence our SAMLIdentityRequestBuilder is also an inner class of SAMLIdentityRequest. Both are extended respectively from IdentityRequest.IdentityRequestBuilder and IdentityRequest.
Both have the same properties while SAMLIdentityRequest has getters and SAMLIdentityRequestBuilder has setters. So all the properties should be initialized in the time of creating IdentityRequestBuilder.
IdentityRequest object keeps the necessary parameters in the first HTTPServletRequest. This is stored in the cache context.
Does this cause data redundancy and spend space twice? No. Only the reference is used in the SAMLIdentityRequest constructor.
IdentityRequest object keeps the necessary parameters in the first HTTPServletRequest. This is stored in the cache context.
Does this cause data redundancy and spend space twice? No. Only the reference is used in the SAMLIdentityRequest constructor.
Now we have built the IdentityRequest. Now we should process the IdentityRequest. For that we need an processor extended from IdentityProcessor. Now this proccessor will validate the SAML Request and if successfully validated, will return the FrameworkLoginResponse.FrameworkLoginResponseBuilder which will the sp to identify whether user is already authenticated or not. If not it will be redirected to the authentication endpoint via the commanauth endpoint.
Everything is handled in abstract IdentityProcessor "buildResponseForFrameworkLogin". We just need to call that method with an IdentityContext which includes the IdentityRequest we built.
References
Everything is handled in abstract IdentityProcessor "buildResponseForFrameworkLogin". We just need to call that method with an IdentityContext which includes the IdentityRequest we built.
Methods to be implemented in SPInitSSOAuthnRequestProcessor
canHandle
Just as in the RequestFactory, here we should decide whether the given IdentityReuest can be processed in this processor. As we have a custom IdentityRequest we can just check whether the IdentityRequest is an "instanceof" SAMLIdentityRequest.
getRelyingPartyId
This is used in the "buildResponseForFrameworkLogin" and "buildResponseForFrameworkLogout" methods. Here we have assigned the relyingParty sent in the request, if there is one.
getName
This will be also used in above methods as the "type". Simply assigned the class name for the name.
getPriority
The priority of the IdentityProcessor decides the index in the IdentityProcessor list, which is added to OSGI context throughout all the components. We have given a high priority to ake it sure that other IdentityProcessor is not picked before our SPInitSSOAuthnRequestProcessor. If a wrong factory is picked up it will end upwith a runtime exception.
getCallbackPath
Callbackpath is the url, which the SP should be redirected upon completion of authentication at commonauth endpoint.
process
The processing functionality. In this processor basically we have validated the SAML request. All the process methods should return a IdentityResponse.IdenstityResponse builder. Here we will return an default FarameworkLoginResponseBuilder which is returned from the "buildResponseForFrameworkLogin". For this to call we need an IdentityMessageContext. This object includes the IdentityRequest we built. This context will be cached in the framework. Here we have created a custom SAMLMessageContext. In this object you can include other fields which you need access later. The only thing is they should be serializable, as this is going to be cached.
canHandle
Just as in the RequestFactory, here we should decide whether the given IdentityReuest can be processed in this processor. As we have a custom IdentityRequest we can just check whether the IdentityRequest is an "instanceof" SAMLIdentityRequest.
getRelyingPartyId
This is used in the "buildResponseForFrameworkLogin" and "buildResponseForFrameworkLogout" methods. Here we have assigned the relyingParty sent in the request, if there is one.
getName
This will be also used in above methods as the "type". Simply assigned the class name for the name.
getPriority
The priority of the IdentityProcessor decides the index in the IdentityProcessor list, which is added to OSGI context throughout all the components. We have given a high priority to ake it sure that other IdentityProcessor is not picked before our SPInitSSOAuthnRequestProcessor. If a wrong factory is picked up it will end upwith a runtime exception.
getCallbackPath
Callbackpath is the url, which the SP should be redirected upon completion of authentication at commonauth endpoint.
process
The processing functionality. In this processor basically we have validated the SAML request. All the process methods should return a IdentityResponse.IdenstityResponse builder. Here we will return an default FarameworkLoginResponseBuilder which is returned from the "buildResponseForFrameworkLogin". For this to call we need an IdentityMessageContext. This object includes the IdentityRequest we built. This context will be cached in the framework. Here we have created a custom SAMLMessageContext. In this object you can include other fields which you need access later. The only thing is they should be serializable, as this is going to be cached.
After this processor is executed the user will be redirected to the authentication endpoint if he is not already authenticated, or to the IdentitySevlet if the user is authenticated, with the "sessionDataKey". To handle this we have implemented SSOLoginProcessor.
References
[1]https://github.com/ChamaraPhilipsuom/identity-inbound-auth-saml/blob/samlnew/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java
[2]https://github.com/ChamaraPhilipsuom/identity-inbound-auth-saml/tree/samlnew/components/org.wso2.carbon.identity.sso.samlnew
[3] travelocity.com link
[4]https://github.com/ChamaraPhilipsuom/identity-inbound-auth-saml/blob/samlnew/components/org.wso2.carbon.identity.sso.samlnew/src/main/java/org/wso2/carbon/identity/sso/samlnew/bean/message/request/SAMLIdentityRequestFactory.java
[2]https://github.com/ChamaraPhilipsuom/identity-inbound-auth-saml/tree/samlnew/components/org.wso2.carbon.identity.sso.samlnew
[3] travelocity.com link
[4]https://github.com/ChamaraPhilipsuom/identity-inbound-auth-saml/blob/samlnew/components/org.wso2.carbon.identity.sso.samlnew/src/main/java/org/wso2/carbon/identity/sso/samlnew/bean/message/request/SAMLIdentityRequestFactory.java