Protected Systems are applications, services, or infrastructure that you want to protect with SGNL. In this guide, we’ll describe how to achieve fine-grained access control at sign-in time with Azure AD and SGNL, enabling the right amount of access to applications and infrastructure connected to Azure AD.
With this integration, Azure AD need not know about the policies, systems of record, or any of the data in SGNL - it simply needs to pass to SGNL:
Authentication ensures that only authorized systems can make requests into SGNL, as well as verifying the identity of an integration in order to effectively evaluate Policies - to access Authentication settings, open your AWS protected system and select the Authentication tab
Click Generate Token
Give your token a descriptive name so that you know how it’s being used in the future and click to Generate Token
On the next screen, copy the token - this will be used by Azure AD to make access requests to SGNL using the SGNL Access Service API
Note: The value of this token is not available again after this screen, so ensure you securely store it for steps later in this guide
Integrating SGNL with Azure AD, for the purposes of securing SSO, relies on Azure AD Custom Authentication Extensions. Azure AD Custom Authentication Extensions enable Azure AD to make an outbound request that can be routed to SGNL, in order to determine whether a user should have access to a specific application at that point in time, or what roles, claims, or tags to include in their token.
The steps to configure SGNL with Azure AD SSO are fairly straightforward. Azure includes a great guide for configuring Custom Authentication Extensions. You’ll need a custom claim provider API endpoint to get started, the good news is that this can be easily deployed with our out of box connector, or code that you create inside of Azure Functions or Container Apps - a sample is included below:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Text;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
// Extract the relevant query fields from the request body
string principalId = data?.data.authenticationContext.user.userPrincipalName;
string ipAddress = data?.data.authenticationContext.client.ip;
string assetId = data?.data.authenticationContext.clientServicePrincipal.appDisplayName;
// Read the correlation ID from the Azure AD request
string correlationId = data?.data.authenticationContext.correlationId;
// Optional claims to return to Azure AD
ResponseContent r = new ResponseContent();
r.data.actions[0].claims.CorrelationId = correlationId;
r.data.actions[0].claims.ApiVersion = "1.0.0";
r.data.actions[0].claims.DateOfBirth = "05/08/1984";
r.data.actions[0].claims.CustomRoles.Add("Reader");
r.data.actions[0].claims.CustomRoles.Add("Admin");
// Create a request for the SGNL Access API
WebRequest request = WebRequest.Create("https://access.sgnlapis.cloud/access/v1/evaluations");
request.Method = "POST";
// TODO: Do this elegantly…
string postData = "{\"principal\":{\"id\":\"" + principalId + "\",\"ipAddress\":\"" + ipAddress + "\"},\"queries\":[{\"action\":\"access\",\"assetId\":\"" + assetId + "\"}]}"
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest.
request.ContentType = "application/json";
request.Headers["Authorization"] = "Bearer eyJk..5MzkwIn0=";
// Get the request stream
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
// Get the response
WebResponse response = request.GetResponse();
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
// Read the content
string responseFromServer = reader.ReadToEnd();
dynamic responseData = JsonConvert.DeserializeObject(responseFromServer);
string decision = responseData?.decisions[0].decision;
reader.Close();
dataStream.Close();
response.Close();
// Return a decision to the app
if (decision == "Allow") {
return new OkObjectResult(r);
}
else {
return new BadRequestObjectResult("Access was Denied :-(");
}
}
public class ResponseContent{
[JsonProperty("data")]
public Data data { get; set; }
public ResponseContent()
{
data = new Data();
}
}
public class Data{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public List<Action> actions { get; set; }
public Data()
{
odatatype = "microsoft.graph.onTokenIssuanceStartResponseData";
actions = new List<Action>();
actions.Add(new Action());
}
}
public class Action{
[JsonProperty("@odata.type")]
public string odatatype { get; set; }
public Claims claims { get; set; }
public Action()
{
odatatype = "microsoft.graph.tokenIssuanceStart.provideClaimsForToken";
claims = new Claims();
}
}
public class Claims{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string CorrelationId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string DateOfBirth { get; set; }
public string ApiVersion { get; set; }
public List<string> CustomRoles { get; set; }
public Claims()
{
CustomRoles = new List<string>();
}
}
Once you’ve got a Custom Claim Extension endpoint deployed, you’ll need to register the Custom Extension in the Azure Portal.
The final step, you should now assign the Custom Extension to one or more of your Enterprise Applications in the Azure Portal, from the Sign-On tab and grant Admin Consent, and that’s it.
At this point, it’s likely that all decisions will either be Allow or Deny, based on the Default Decision you’ve selected for the Azure AD Integration - if that’s the case, you’re ready to start assigning policies.
Once the Azure AD integration is created, you can start assigning versions of Policies - to get started, select Policies from the tabs in your newly created integration
Select ‘Assign Policies’
Select:
Click Next once you have the Policies and Versions configured as is appropriate
Select the Enforcement mode for the Policies you chose in the previous step
Simulated: Policy Versions that are being simulated will only log their access decision in the SGNL logs and will not impact the access decision that SGNL hands back to an integration. Simulated policies are useful for performing what-if analysis of new policy versions as well as debugging policy changes.
Note: It’s considered best practice to start with policies in Simulated mode, to verify that policies have been created an applied as expected
Enforced: Policy Versions that are being enforced will impact the access decisions that SGNL hands back to an integration. Enforced Policies will determine access for an integration
Select your desired Enforcement mode and select Assign
Versions of Policies will now be Assigned to your integration