.NET Runtime Client

The runtime client library communicates with the runtime API to turn a user’s identity into application roles and permissions.

The library is provided as a NuGet package with the name PolicyServer.Runtime.Client.

Creating and configuring the Runtime Client

In the easiest case, you configure the name of the base policy, URL of the token server, and credentials in code:

var options = new PolicyServerRuntimeClientOptions
{
    BasePolicy = "EmergencyRoom",

    TokenClient =
    {
        Authority = "<insert here>",
        ClientId = "runtime.client",
        ClientSecret = "<insert here>"
    }
};

You can then either create the client via a DI container, e.g.:

var services = new ServiceCollection();
services.AddPolicyServerRuntimeClientCore(options =>
{
    options.BasePolicy = "EmergencyRoom";

    options.TokenClient.Authority = "<insert here>";
    options.TokenClient.ClientId = "runtime.client";
    options.TokenClient.ClientSecret = "<insert here>";
});

var provider = services.BuildServiceProvider();
var client = provider.GetService<IPolicyServerRuntimeClient>();

…or by calling the Create method (if no DI is available):

var client = PolicyServerRuntimeClient.Create(options);

A third option is to use the .NET Core configuration API, e.g. with the following configuration file:

{
    "PolicyServerRuntimeClient": {
        "BasePolicy": "EmergencyRoom",

        "TokenClient": {
            "Authority": "<insert authority here>",
            "ClientId": "runtime.client",
            "ClientSecret": "<insert runtime API client secret here>"
        }
    }
}

…and then initialize the client like this:

services.AddPolicyServerRuntimeClientCore(Configuration.GetSection("PolicyServerRuntimeClient"));

Using an external Token Service

By default, PolicyServer contains an embedded token service to issue tokens for the runtime API. If you already have an OAuth 2.0 authorization server, you can use your server to issue tokens instead. In this case, you specify different addresses for PolicyServer and your token service in configuration, e.g.:

{
    "PolicyServerRuntimeClient": {
        "PolicyServerUrl": "<insert address of policyserver>",
        "BasePolicy": "EmergencyRoom",

        "TokenClient": {
            "Authority": "<insert authority here>",
            "ClientId": "runtime.client",
            "ClientSecret": "<insert runtime API client secret here>"
        }
    }
}

Creating Claims for Policy Evaluation

Either you create claims to represent the current user, or you forward the claims of the currently logged on user (e.g. via the User property in ASP.NET).

An easy way to create a principal is using the Principal helper class:

var user = Principal.Create("ps",
    new Claim("sub", "1"),
    new Claim("role", "Surgeon"),
    new Claim("tenant", "5"));

The claims sub, role and tenant have special meanings in PolicyServer. sub specifies the unique identifier for the user, role an identity role and tenant is a tenant identifier (see Claims Transformation).

Calling the Runtime API

You can then use the client library to send the claims to the configured policy to retrieve application roles and permissions:

var result = await client.EvaluateAsync(user);
if (result.IsError) throw new Exception(result.Error);

result.Roles.ToList().ForEach(i => Console.WriteLine(i));
result.Permissions.ToList().ForEach(i => Console.WriteLine(i));

EvaluateSync will return all application roles and permissions based on the incoming claims. You can also call IsInRoleAsync or HasPermissionAsync as shortcuts if you want to check for a specific role or permission.

Caching

By default, every call will result in a network round trip to the runtime API. You can also specify CacheDuration on the PolicyServerRuntimeClientOptions (in seconds), which means that the result will be cached locally for the specified amount of time:

var options = new PolicyServerRuntimeClientOptions
{
    BasePolicy = "EmergencyRoom",
    CacheDuration = 300,

    TokenClient =
    {
        Authority = "<insert here>",
        ClientId = "runtime.client",
        ClientSecret = "<insert here>"
    }
};

Claims Transformation

The PolicyServer runtime API expects some claims to have certain claim types:

  • sub - a unique user identifier

  • role - an identity role

  • tenant - a tenant identifier

You might use different claim types in your system to express the same data. If you create the ClaimsPrincipal yourself - you need to transform your claim types to the expected claim types.

If you want to forward an existing identity (maybe created by some application plumbing or coming from your identity system), the client library can also do automatic claims transformation for you. You can configure that on the PolicyServerRuntimeClientOptions, e.g.:

var options = new PolicyServerRuntimeClientOptions
{
    BasePolicy = "EmergencyRoom",

    TokenClient =
    {
        Authority = "<insert here>",
        ClientId = "runtime.client",
        ClientSecret = "<insert here>"
    },

    ClaimTypes =
    {
        Subject = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
        Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
        Tenant = "http://myorg/claims/tenantid"
    }
};

This would automatically translate a claim of type http://myorg/claims/tenantid to our expected tenant.

Runtime Client Flags

The PolicyServer runtime API support some flags that helps extract more data the claims evaluations. These flags are:

  1. IncludePolicyDiagnostics

  2. EvaluateChildPolicies

  3. IncludeChildrenWithDescendantAssignments

  4. IncludeTenantRoles

IncludePolicyDiagnostics

When evaluating a policy that resides within a hierarchy, roles and permissions can be assigned at any level in the hierarchy. If you wish to debug which policy in the hierarchy has made specific assignments, the IncludePolicyDiagnostics flag can be used: e.g.:

var client = PolicyServerRuntimeClient.Create(options);
var user = Principal.Create("test", new Claim("sub", "1111"));
var result = client.EvaluateAsync(new PolicyEvaluationRequest {
    User = user,
    IncludePolicyDiagnostics = true
}).Result;

EvaluateChildPolicies

When evaluating a policy, you can also request that the child policies are also evaluated and returned in the response (which includes the child policies’ roles and permissions) with the EvaluateChildPolicies parameter.

This only evaluates the immediate child policies, not the entire descendant hierarchy. If no roles or permissions are matched in the child policy, then the child policy is not included in the response. This indicates that the user has no access to the policy. e.g.:

var client = PolicyServerRuntimeClient.Create(options);
var user = Principal.Create("test", new Claim("sub", "1111"));
var result = client.EvaluateAsync(new PolicyEvaluationRequest {
    User = user,
    EvaluateChildPolicies = true
}).Result;

IncludeChildrenWithDescendantAssignments

When evaluating child policies (as described above), children where no role or permission is issued is not emitted in the response. There might be descendant policies beneath the child that do have role or permission assignments, but when simply evaluating child policies those are not taken into consideration.

If, when evaluating child policies, you do want the policies where some assignment is granted somewhere in the descendant policy hierarchy, then include the IncludeChildrenWithDescendantAssignments in the request. e.g.:

var client = PolicyServerRuntimeClient.Create(options);
var user = Principal.Create("test", new Claim("sub", "1111"));
var result = client.EvaluateAsync(new PolicyEvaluationRequest {
    User = user,
    IncludeChildrenWithDescendantAssignments = true
}).Result;

IncludeTenantRoles

Normally the runtime API evaluation return the subject roles in all policies, but does not include the tenant roles even if the tenant claim was sent in the evaluation request.

The IncludeTenantRoles parameter in a policy evaluation request allows the runtime API to include the roles for the tenant claim sent in the request. e.g.:

var client = PolicyServerRuntimeClient.Create(options);
var user = Principal.Create("test", new Claim("sub", "1111"));
var result = client.EvaluateAsync(new PolicyEvaluationRequest {
    User = user,
    IncludeTenantRoles = true
}).Result;