External claims transformation
Although the different transformation rules that exist in Safewhere Identify will cover a lot of different situations, there will still be many scenarios where standard rules do not suffice. It is possible to create your own transformation rule and plug in to Safewhere Identify. Briefly speaking, an external claims transformation module enables third parties to write claim transformation rules that are independent from the Safewhere Identify build. Some of the important features are:
- It is possible to change an external DLL by simply copying the new DLL to the Safewhere Identify server and recycling the application pool.
- There is no dependency between the external DLL and a specific build of Safewhere Identify. The only dependency between the two is the interface that is implemented by the external DLL.
- The interface is defined in a strongly named assembly, but its version is fixed as long as there are no breaking changes.
- A new build of Safewhere Identify does not require that the external DLL be recompiled.
Before we set up the external claims transformation rule in Safewhere Identify, we must first prepare the plug-in.
The processing flow for external claims transformation
The processing flow for how a claims principal is exposed to the claim transformation pipeline:
- The principal is then passed through the configurable claims transformation rules that are configured to run after the rule that loads claims from the local store. External claims transformations can also be among the rules that are run after claims are loaded from the local store.
- The principal then loads claims from the local store.
- The principal is then passed through the configurable claims transformation rules that are configured to run before the rule that loads claims from the local store. External claims transformations can be among the rules that run before claims are loaded from the local store.
- The principal is passed through Safewhere Identify's built-in, hard-coded transformation rules.
- The user authenticates.
- A claims principal is created.
- The principal is finally passed through Safewhere Identify's other built-in, hard-coded transformation rules.
Steps to implement a new external claims transformation
There are three steps:
- Reference the Safewhere.External assembly.
- Implement the IExternalClaimsTransformation interface.
- Set up the external claims transformation to be used in the normal way from Safewhere Identify's Administrator pages.
An external custom claims transformation must implement the IExternalClaimsTransformation interface:
public interface IExternalClaimsTransformation
{
IClaimsPrincipal Transform(IClaimsPrincipal principal , IDictionary<string, string> inputs, IExternalClaimTransformationPipelineContext pipelineContext);
}
- By design, the claims principal object may have more identities.
- Each identity provider may add its own identity to the claims principal object at the time the principal object is passed to this transformation rule. The number of identities it contains depends on what previous transformation rules it has passed through.
- Typically what happens is:
- After Safewhere Identify receives a token from an upstream identity provider or from the UserNamePassword login, it creates the principal with one identity.
- There is a special, built-in transformation that loads claims from the local store into another identity and adds it to the principal (two identities so far).
- There is a setting called "Execute before loading claims from local store" in the claims transformation configuration page, which controls if a transformation should be executed before or after the second identity is added.
Note 1: It is safe to add a claim to multiple identities because all but one will be filtered out before returning them to the service provider.
Note 2: Other transformations may add more identities to the claims principal.
Exception handling and logging
The interface assembly will provide an interface for logging:
public interface IIdentifyLogWriter
{
void WriteError(object message);
void WriteError(intmessageId, object message);
void WriteInformation(object message);
void WriteInformation(intmessageId, object message);
void WriteWarning(object message);
void WriteWarning(intmessageId, object message);
}
External assemblies can use this interface to log exceptions as well as other information to event logs at their will.
For unhandled exceptions from an external custom transformation, Safewhere Identify will log them to the event log. Safewhere Identify will then either halt the processing pipeline or progress to the next transformation based on the relevant settings.
In order to enable logging to the Safewhere event log, simply add a dependency to the IIdentifyLogWriter interface. For example:
public class AddDummyClaimTransformation : IExternalClaimsTransformation
{
private readonly IIdentifyLogWriter identifyLogWriter;
public AddDummyClaimTransformation(IIdentifyLogWriter identifyLogWriter)
{
if (identifyLogWriter == null)
{
throw new ArgumentNullException("identifyLogWriter");
}
this.identifyLogWriter = identifyLogWriter;
}
public IClaimsPrincipal Transform(IClaimsPrincipal principal, IDictionary<string, string> inputs, IExternalClaimTransformationPipelineContext pipelineContext)
{
if (principal == null)
{
throw new ArgumentNullException("principal");
}
try
{
var type = GetType();
foreach (var identity in principal.Identities)
{
identity.Claims.Add(new Claim(type.FullName, type.AssemblyQualifiedName));
identifyLogWriter.WriteInformation(string.Format(CultureInfo.InvariantCulture,
"A new claim is added: [{0},{1}]", type.FullName, type.AssemblyQualifiedName));
}
}
catch (InvalidOperationException ex) // this exception type is caught for illustration purpose only
{
// Ids from 4830 to 4899 are reserved for external components. Pick one that doesn't conflict with other external events in the same set up
identifyLogWriter.WriteError(4830, ex);
}
return principal;
}
}
Localization
The external plug-ins will need their own text resources. These text resources will also be used for the custom views.
Safewhere Identify's text resource framework is built in a way that allows customized default language. In addition, it is also possible to override default texts with some company-specific resources. But because external DLLs are used for a specific installation, where you know what the default language is, you can make the plug-in use a simpler text resource model:
- Add text resource files to the project.
- Text resources files' properties must be set correctly:
- Build Action: None
- Copy to output: Do not copy
- Custom tool: GlobalResourceProxyGenerator
- Upon deploying the DLL, also copy the resource files to appropriate folders.
It is recommended that when logging messages to the event log, the external DLL should hard-code messages in the language of its choice instead of reading from text resources. Otherwise, messages may be logged in the user's preferred language.
How to use the plugin
Drop your DLL into the "\Runtime\bin" folder then reset the IIS.
Go to Safewhere Admin and create a new external claims transformation.
Transformation type name: Select the assembly qualified name of the transformation into the Transformation type name drop-down box. The options in this drop-down list will be updated immediately after the DLL files are put into the Bin folder.
If you do not want the pipeline to break down in case the external Plug-in failed, enable the Continue on error check box.
Additional settings: Give some predefined settings, which will be used on transform on the selected external claims transformation above.
Questions and Answers on using external claims transformations
Question: Do I need to wire up my transformation in some Windsor file?
Answer: No, Safewhere Identify can do the registration job for you at runtime.
Question: What if my transformation has dependency to another service? For example, what if it has a dependency to IMyCompanyEmailSender service?
Answer: Then unfortunately, you will need to wire your transformation up the IMyCompanyEmailSender in some Windsor file. You still don't need to wire up the transformation itself, though.
Question: How many identities does a claims principal have?
Answer: A claims principal, by the time it is passed to an external claims transformation, should have two identities: a name claim identity and a local user identity, which contains all the claims that are issued by the local store. The second identity is for claim issuance purpose only. An external claims transformation can add new claims to either of the two or add to both. Duplicate claims will be filtered out later.
Event IDs
Event IDs used by Safewhere Identify for this plug-in are:
- 140: External claims transformation error.