Supporting .NET Standard 2.0 and .NET Framework 4.6.1+. Refer to Microsoft's documentation for compatibility information.
Castle analyzes device, location, and interaction patterns in your web and mobile apps and lets you stop account takeover attacks in real-time.
Install the Castle.Sdk
nuget.
nuget install Castle.Sdk
install-package Castle.Sdk
- Go to Tools -> Package Manager -> Manage NuGet Packages for Solution...
- Click the Browse tab and search for
Castle.Sdk
- Click the
Castle.Sdk
package in the search results, select version and what projects to apply it to on the right side, and click Install
Go to the settings page of your Castle account and find your API Secret. Use it to create a new instance of the CastleClient
class.
var client = new CastleClient("YOUR SECRET");
It's a good idea to set up your CastleClient
instance using an IoC container.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton(new CastleClient(new CastleConfiguration("YOUR SECRET")));
}
The CastleConfiguration
object has a number of properties that control the SDK.
Property | Default | Description |
---|---|---|
ApiSecret | Secret used to authenticate with the Castle Api. Required | |
FailoverStrategy | Allow | The response action to return in case of a failover in an Authenticate request. |
Timeout | 1000 | Timeout for requests, in milliseconds. |
BaseUrl | https://api.castle.io | Base Castle Api url. |
LogLevel | Error | The log level applied by the injected ICastleLogger implementation. |
AllowList | List of headers that should be passed intact to the API. A list of recommended .headers can be retrieved from the static property Castle.Headers.AllowList in the SDK |
|
DenyList | List of header that should not be passed intact to the API. | |
DoNotTrack | false | If true, no requests are actually sent to the Caste Api, and Authenticate returns a failover response. |
Logger | Your own logger implementation. | |
IpHeaders | IP Headers to look for a client IP address | |
TrustedProxies | Trusted public proxies list | |
TrustedProxyDepth | 0 | Number of trusted proxies used in the chain |
TrustProxyChain | false | Is trusting all of the proxy IPs in X-Forwarded-For enabled |
The SDK allows customized logging by way of implementing the ICastleLogger
interface and passing in an instance as part of the CastleConfiguration
. Exactly what gets logged can be controlled by setting the LogLevel
property of CastleConfiguration
.
var client = new CastleClient(new CastleConfiguration("secret") {
Logger = new MyLogger()
});
public class DebugLogger : ICastleLogger
{
public void Info(string message)
{
Debug.WriteLine($"INFO: {message}");
}
public void Warn(string message)
{
Debug.WriteLine($"WARNING: {message}");
}
public void Error(string message)
{
Debug.WriteLine($"ERROR: {message}");
}
}
The CastleClient
instance has the action methods Track and Authenticate. In order to provide the information required for both these methods, you'll need access to the logged in user information (if that is available at that stage in the user flow), as well as request information. In asp.net core, both Razor Pages pages and MVC controllers expose the information you need in User
and Request
members.
For events where you don't require a response.
castleClient.Track(new ActionRequest()
{
Event = Castle.Events.LogoutSucceeded,
UserId = user.Id,
UserTraits = new Dictionary<string, string>()
{
["email"] = user.Email,
["registered_at"] = user.RegisteredAt
},
Context = new RequestContext()
{
Ip = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
ClientId = Request.Cookies["__cid"],
Headers = Request.Headers.ToDictionary(x => x.Key, y => y.Value.FirstOrDefault());
}
});
For events where you require a response. It is used in the same way as Track
, except that you have the option of waiting for a response.
var verdict = await castleClient.Authenticate(new ActionRequest()
{
Event = Castle.Events.LogoutSucceeded,
UserId = user.Id,
UserTraits = new Dictionary<string, string>()
{
["email"] = user.Email,
["registered_at"] = user.RegisteredAt
},
Context = new RequestContext()
{
Ip = Request.HttpContext.Connection.RemoteIpAddress.ToString(),
ClientId = Request.Cookies["__cid"],
Headers = Request.Headers.ToDictionary(x => x.Key, y => y.Value.FirstOrDefault());
}
});
Property | Description |
---|---|
Action | The recommended action for the given event. |
UserId | The UserId of the end user that was provided in the request. |
DeviceToken | The Castle token for the device that generated the event. |
Failover | A boolean indicating if the request failed and the response is a failover. |
FailoverReason | A message indicating why the request failed in case of failover. |
When a request to the /v1/authenticate
endpoint of the Castle API fails, the SDK will generate a failover response based on the FailoverStrategy
set in the CastleConfiguration
object.
If no failover strategy is set (i.e. None
), a Castle.Infrastructure.Exceptions.CastleExternalException
will be thrown.
Option | Description |
---|---|
Event | The event generated by the user. It can be either an event from the SDK constants in Castle.Events or a custom one. |
UserId | Your internal ID for the end user. |
UserTraits | An optional, recommended, dictionary of user information, such as email and registered_at . |
Properties | An optional dictionary of custom information. |
Timestamp | An optional datetime indicating when the event occurred, in cases where this might be different from the time when the request is made. |
DeviceToken | The optional device token, used for mitigating or escalating. |
Context | The request context information. See information below. |
Option | Description |
---|---|
Ip | The IP address of the request. Note that this needs to be the original request IP, not the IP of an internal proxy, such as nginx. |
ClientId | The client ID, generated by the c.js integration on the front end. Commonly found in the __cid cookie in Request.Cookies , or in some cases the X-CASTLE-CLIENT-ID header. |
Headers | Headers mapped from the the original request (most likely Request.Headers ). |
You can call Castle.Context.FromHttpRequest(request)
to get a ready-made RequestContext
instance from your current request.
public class HomeController : Controller
{
public ActionResult Index()
{
var actionRequest = new ActionRequest()
{
Context = Castle.Context.FromHttpRequest(Request)
...
public class IndexModel : PageModel
{
public void OnGet()
{
var actionRequest = new ActionRequest()
{
Context = Castle.Context.FromHttpRequest(Request)
...
See the documentation on securing requests in order to learn more.
Use the Castle.Signature.Compute(string key, string message)
method, with your API Secret as key, to create a signature to use in the frontend and to validate Webhooks.
You target .NET Framework and get an exception on startup.
System.IO.FileNotFoundException: 'Could not load file or assembly 'System.Runtime.InteropServices.RuntimeInformation, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
The Castle SDK has a dependency on Sentry.PlatformAbstractions, which in turn uses System.Runtime.InteropServices.RuntimeInformation
, version 4.3.0.
Find the binding redirect for System.Runtime.InteropServices.RuntimeInformation
in web.config
and either remove the entire dependentAssembly
element, or update newVersion
to 4.3.0.
There is a sample application using ASP.NET Core Razor Pages and this SDK here.