Table of Contents

Getting Started

To use the SCIM protocol in your application, you have to register methods in the startup of the application.

Setup

In the entry of your application, add the following code to configure the SCIM package:

var builder = WebApplication.CreateBuilder(args);

// Your code ...

// Add the SCIM services, define the event classes
builder.Services.AddScim<ScimUserEvents, ScimGroupEvents>();

// Your code ...

var app = builder.Build();

// Your code...

app.UseScim(new()
{
	// configure here identity options ...
});

Events

To handle SCIM operations, create a class that implements the IScimUserEvents and IScimGroupEvents interfaces. The generic parameter type in ICreate defines your ScimUser type. This can also be a type that is inherited from ScimUser. All added class references will be treated as an SCIM extension.


public class ScimUserEvents : IScimUserEvents, ICreate<ScimUser> // other operation interfaces ...
{
	public async Task<OperationResult<ScimUser>> OnCreateAsync(ScimUser user)
	{
		// Your code ...
		
		return OperationResult.Ok(user);
	}
	
	// Other methods ...
}

Extensions

To add an extension to the SCIM user, create a class that extends the ScimUser class and add the extension class with the ScimExtensionInfo attribute.


public class ExtendedScimUser : ScimUser 
{
	[ScimRequired(false)]
	public Creativity CreativityExtension { get; set; } = new();
}

[ScimExtensionInfo("urn:ietf:params:scim:schemas:extend:2.0:Creativity", nameof(Creativity), "Creativity")]
public class Creativity
{
	[ScimProperty(Description = "The user's favorite artist.")]
	public string? FavoriteArtist { get; set; }
}

To handle the extended SCIM user, make sure your event class implements the ICreate<ExtendedScimUser> interface.


public class ScimUserEvents : IScimUserEvents, ICreate<ExtendedScimUser> // other operation interfaces ...
{
	public async Task<OperationResult<ExtendedScimUser>> OnCreateAsync(ExtendedScimUser user)
	{
		// Your code ...
		
		return OperationResult.Ok(user);
	}
	
	// Other methods ...
}

Authentication

By default, the SCIM package uses the Bearer authentication scheme. All paths that begins with the /scim/v2 (or the configured base path) prefix, are protected by this scheme.

⚠️ It is not possible to change this scheme.

Configuration

⚠️ JWT settings are required to start up your application. When the JWT settings are invalid, an exception will be thrown to let the developer know.

JWT settings can be configured using the appsettings file, or by setting the JwtSettings property in the ScimOptions object.

The ServiceProviderSettings contains all the SCIM service provider settings. For example:

  • DocumentUri: The URI of the SCIM service provider.
  • PatchSupported: A boolean that indicates if the service provider supports the PATCH operation.
  • BulkSupported: A boolean that indicates if the service provider supports the BULK operation.
  • FilterSupported: A boolean that indicates if the service provider supports the FILTER operation.
  • FilterMaxResults: The maximum number of results that can be returned by the service provider.
  • SortingSupported: A boolean that indicates if the service provider supports the SORT operation.
  • ETagSupported: A boolean that indicates if the service provider supports the ETag operation.

This is an example of the appsettings configuration:

{
	"Scim": {
		"BasePath": "...",
		"ServiceProviderSettings": {
			"DocumentUri": "http://example.com"
		},
		"JwtSettings": {
			"Audience": "Your audience",
			"Issuer": "Your issuer",
			"SecurityKey": "Your minimal 128-bit security key",
			"Lifetime": "00:30:00"
		}
	}
}

When using the code configuration, add the following code to the startup class:

builder.Services.AddScim<ScimUserEvents, ScimGroupEvents>(
	scimOptions =>
	{
		scimOptions.JwtSettings = new()
		{
			Audience = "Your audience",
			Issuer = "Your issuer",
			SecurityKey = "Your minimal 128-bit security key",
			// Default is 5 minutes if not set
			Lifetime = TimeSpan.FromMinutes(30)
		};
	});

Token generation

The same JWT settings will be used to generate the token.

To generate a token, use the Create method from the ScimJwtTokenGenerator class. The ScimJwtTokenGenerator class is registered in the service collection, so you can inject it into your classes. The create method allows you to pass claims. These claims can be used for authorization purposes in the SCIM event classes. Retrieve these claims through the IHttpContextAccessor service. This service has a HttpContext instance, with a User property that contains the claims of an authorized request.

This is an example how to generate a token with an available service provider:

var generator = ServiceProvider.GetRequiredService<ScimJwtTokenGenerator>();
var token = generator.Create(new Claim(ClaimTypes.Name, "John Doe"));

// Return the 'token' variable as response ...

Object mapper

To make dynamic field configuration of SCIM properties available, the ScimObjectMapper class can be used. This class contains a Fields property, whereof the Key is the *SCIM path and the value a **JSON path. This class also contains a Actions property, whereof the Key is the ***SCIM filter and the value a string that defines the name of the action that should be executed if the filter evaluated true.

*SCIM path: The path of the SCIM property. This value should contain valid SCIM path syntax.

**JSON path: The path of the JSON property. This path will always start from a root object. That's why the path does not have to start with a $ sign. These are valid JSON path examples:

  • user.name
  • user.phoneNumbers[]
  • user.phoneNumbers[].value

It is possible to use the [] syntax to indicate an array. No index should be provided in the path.

***SCIM filter: An SCIM expression that should contain valid SCIM filter syntax. The expression should be able to return true of false.

This class can be instantiated like this:

var mapper = new ScimObjectMapper();

No service provider is needed to instantiate this class. It is also possible to load your configuration from a JSON file, or request.

var mapper = ScimObjectMapper.FromJson("{...}");

It is also possible to add configuration to the ScimObjectMapper instance like this:

var mapper = new ScimObjectMapper();

mapper.Fields["urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber"] = "user.employeeNumber";
mapper.Actions["emails[value eq \"hello@world.dev\"] and name.givenName eq \"hello\""] = ["SomeAction"];

To evaluate and get a converted object, use the EvaluateAsync method from the ScimObjectMapper class.

var user = new ScimUser
{
	Id = "123",
	UserName = "user.name"
};
var mapper = new ScimObjectMapper();

// Configure the SCIM path to JSON path mapping
var jsonObject = await mapper.EvaluateAsync(user);

// Send the jsonObject to the client ...

Evaluation of an object can also trigger an action if the SCIM filter evaluates true. Register the action like this:

var user = new ScimUser
{
	Id = "123",
	UserName = "user.name"
};
var mapper = new ScimObjectMapper();
// Configure `SomeAction`, when the expression evaluates true, the parameter well be called ...
mapper.OnExecute("SomeAction", () => Task.CompletedTask);

An action can also be used to modify the object before you send it to the client.

var user = new ScimUser
{
	Id = "123",
	UserName = "user.name"
};
var mapper = new ScimObjectMapper();
// Configure `SomeAction` in the `Actions` property ...
// Multiple actions can be registered for the same action name.
var trueWhenAnyIsTrue = mapper.OnExecuteAny("SomeAction", user);
var trueWhenAllIsTrue = mapper.OnExecuteAll("SomeAction", user);

Minimal Configuration

The SCIM package provides a AddScimCore method to add some services that allow to interface with SCIM.