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
offalse
.
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.