Identify and remediate risks by using identity protection APIs
Article
Microsoft Entra ID Protection provides organizations insight into identity-based risk and different ways to investigate and automatically remediate risk. The Identity Protection APIs used in this tutorial can help you identify risk and configure a workflow to confirm compromise or enable remediation.
In this tutorial, you learn how to use identity protection APIs to:
Generate a risky sign-in.
Allow users with risky sign-ins to remediate the risk status with a conditional access policy that requires multi-factor authentication (MFA).
Block a user from signing in using a conditional access policy.
Dismiss a user risk.
Prerequisites
To complete this tutorial, you need the following resources and privileges:
A working Microsoft Entra tenant with a Microsoft Entra ID P1 or P2 license.
Sign in to an API client such as Graph Explorer with an account that has at least the Conditional Access Administrator role.
Grant yourself the following delegated permissions: IdentityRiskEvent.Read.All, IdentityRiskyUser.ReadWrite.All, Policy.Read.All, Policy.ReadWrite.ConditionalAccess, and User.ReadWrite.All.
A test user account that you use to sign in later to an anonymous session to trigger a risk detection. You can use a private browsing session or the Tor browser. In this tutorial, the test user mail nickname is MyTestUser1.
Step 1: Trigger a risk detection
In the anonymous browser session, sign in as MyTestUser1 to entra.microsoft.com.
Step 2: List risk detections
When MyTestUser1 signed in to the Microsoft Entra admin center using the anonymous browser, an anonymizedIPAddress risk event was detected. You can use the $filter query parameter to get only the risk detections that are associated with the MyTestUser1 user account. It might take a few minutes for the event to be returned.
GET https://graph.microsoft.com/v1.0/identityProtection/riskDetections?$filter=userDisplayName eq 'MyTestUser1'
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.IdentityProtection.RiskDetections.GetAsync((requestConfiguration) =>
{
requestConfiguration.QueryParameters.Filter = "userDisplayName eq 'MyTestUser1'";
});
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
graphidentityprotection "github.com/microsoftgraph/msgraph-sdk-go/identityprotection"
//other-imports
)
requestFilter := "userDisplayName eq 'MyTestUser1'"
requestParameters := &graphidentityprotection.IdentityProtectionRiskDetectionsRequestBuilderGetQueryParameters{
Filter: &requestFilter,
}
configuration := &graphidentityprotection.IdentityProtectionRiskDetectionsRequestBuilderGetRequestConfiguration{
QueryParameters: requestParameters,
}
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
riskDetections, err := graphClient.IdentityProtection().RiskDetections().Get(context.Background(), configuration)
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
RiskDetectionCollectionResponse result = graphClient.identityProtection().riskDetections().get(requestConfiguration -> {
requestConfiguration.queryParameters.filter = "userDisplayName eq 'MyTestUser1'";
});
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
from msgraph.generated.identity_protection.risk_detections.risk_detections_request_builder import RiskDetectionsRequestBuilder
from kiota_abstractions.base_request_configuration import RequestConfiguration
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
query_params = RiskDetectionsRequestBuilder.RiskDetectionsRequestBuilderGetQueryParameters(
filter = "userDisplayName eq 'MyTestUser1'",
)
request_configuration = RequestConfiguration(
query_parameters = query_params,
)
result = await graph_client.identity_protection.risk_detections.get(request_configuration = request_configuration)
You can use conditional access policies in your organization to allow users to self-remediate when risk is detected. Self-remediation enables your users to unblock themselves to access their resources securely after completing the policy prompt. In this step, you create a conditional access policy that requires the user to sign in using MFA if a medium or high risk detection occurs.
Set up multifactor authentication
When setting up an account for MFA, you can choose from several methods for authenticating the user. Choose the best method for your situation to complete this tutorial.
Complete the MFA setup procedure using the appropriate method for your situation, such as using the Microsoft Authenticator app.
Create the conditional access policy
The conditional access policy allows you to set the conditions of the policy to identify sign-in risk levels. Risk levels can be low, medium, high, none. The following example shows how to require MFA for sign ins with medium and high risk levels.
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.Models;
var requestBody = new ConditionalAccessPolicy
{
DisplayName = "Policy for risky sign-in",
State = ConditionalAccessPolicyState.Enabled,
Conditions = new ConditionalAccessConditionSet
{
SignInRiskLevels = new List<RiskLevel?>
{
RiskLevel.High,
RiskLevel.Medium,
},
Applications = new ConditionalAccessApplications
{
IncludeApplications = new List<string>
{
"All",
},
},
Users = new ConditionalAccessUsers
{
IncludeUsers = new List<string>
{
"4628e7df-dff3-407c-a08f-75f08c0806dc",
},
},
},
GrantControls = new ConditionalAccessGrantControls
{
Operator = "OR",
BuiltInControls = new List<ConditionalAccessGrantControl?>
{
ConditionalAccessGrantControl.Mfa,
},
},
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Identity.ConditionalAccess.Policies.PostAsync(requestBody);
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
ConditionalAccessPolicy conditionalAccessPolicy = new ConditionalAccessPolicy();
conditionalAccessPolicy.setDisplayName("Policy for risky sign-in");
conditionalAccessPolicy.setState(ConditionalAccessPolicyState.Enabled);
ConditionalAccessConditionSet conditions = new ConditionalAccessConditionSet();
LinkedList<RiskLevel> signInRiskLevels = new LinkedList<RiskLevel>();
signInRiskLevels.add(RiskLevel.High);
signInRiskLevels.add(RiskLevel.Medium);
conditions.setSignInRiskLevels(signInRiskLevels);
ConditionalAccessApplications applications = new ConditionalAccessApplications();
LinkedList<String> includeApplications = new LinkedList<String>();
includeApplications.add("All");
applications.setIncludeApplications(includeApplications);
conditions.setApplications(applications);
ConditionalAccessUsers users = new ConditionalAccessUsers();
LinkedList<String> includeUsers = new LinkedList<String>();
includeUsers.add("4628e7df-dff3-407c-a08f-75f08c0806dc");
users.setIncludeUsers(includeUsers);
conditions.setUsers(users);
conditionalAccessPolicy.setConditions(conditions);
ConditionalAccessGrantControls grantControls = new ConditionalAccessGrantControls();
grantControls.setOperator("OR");
LinkedList<ConditionalAccessGrantControl> builtInControls = new LinkedList<ConditionalAccessGrantControl>();
builtInControls.add(ConditionalAccessGrantControl.Mfa);
grantControls.setBuiltInControls(builtInControls);
conditionalAccessPolicy.setGrantControls(grantControls);
ConditionalAccessPolicy result = graphClient.identity().conditionalAccess().policies().post(conditionalAccessPolicy);
<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\Models\ConditionalAccessPolicy;
use Microsoft\Graph\Generated\Models\ConditionalAccessPolicyState;
use Microsoft\Graph\Generated\Models\ConditionalAccessConditionSet;
use Microsoft\Graph\Generated\Models\RiskLevel;
use Microsoft\Graph\Generated\Models\ConditionalAccessApplications;
use Microsoft\Graph\Generated\Models\ConditionalAccessUsers;
use Microsoft\Graph\Generated\Models\ConditionalAccessGrantControls;
use Microsoft\Graph\Generated\Models\ConditionalAccessGrantControl;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new ConditionalAccessPolicy();
$requestBody->setDisplayName('Policy for risky sign-in');
$requestBody->setState(new ConditionalAccessPolicyState('enabled'));
$conditions = new ConditionalAccessConditionSet();
$conditions->setSignInRiskLevels([new RiskLevel('high'),new RiskLevel('medium'), ]);
$conditionsApplications = new ConditionalAccessApplications();
$conditionsApplications->setIncludeApplications(['All', ]);
$conditions->setApplications($conditionsApplications);
$conditionsUsers = new ConditionalAccessUsers();
$conditionsUsers->setIncludeUsers(['4628e7df-dff3-407c-a08f-75f08c0806dc', ]);
$conditions->setUsers($conditionsUsers);
$requestBody->setConditions($conditions);
$grantControls = new ConditionalAccessGrantControls();
$grantControls->setOperator('OR');
$grantControls->setBuiltInControls([new ConditionalAccessGrantControl('mfa'), ]);
$requestBody->setGrantControls($grantControls);
$result = $graphServiceClient->identity()->conditionalAccess()->policies()->post($requestBody)->wait();
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
from msgraph.generated.models.conditional_access_policy import ConditionalAccessPolicy
from msgraph.generated.models.conditional_access_policy_state import ConditionalAccessPolicyState
from msgraph.generated.models.conditional_access_condition_set import ConditionalAccessConditionSet
from msgraph.generated.models.risk_level import RiskLevel
from msgraph.generated.models.conditional_access_applications import ConditionalAccessApplications
from msgraph.generated.models.conditional_access_users import ConditionalAccessUsers
from msgraph.generated.models.conditional_access_grant_controls import ConditionalAccessGrantControls
from msgraph.generated.models.conditional_access_grant_control import ConditionalAccessGrantControl
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
request_body = ConditionalAccessPolicy(
display_name = "Policy for risky sign-in",
state = ConditionalAccessPolicyState.Enabled,
conditions = ConditionalAccessConditionSet(
sign_in_risk_levels = [
RiskLevel.High,
RiskLevel.Medium,
],
applications = ConditionalAccessApplications(
include_applications = [
"All",
],
),
users = ConditionalAccessUsers(
include_users = [
"4628e7df-dff3-407c-a08f-75f08c0806dc",
],
),
),
grant_controls = ConditionalAccessGrantControls(
operator = "OR",
built_in_controls = [
ConditionalAccessGrantControl.Mfa,
],
),
)
result = await graph_client.identity.conditional_access.policies.post(request_body)
Step 4: Trigger another risky sign in but complete multifactor authentication
By signing in to the anonymous browser, a risk was detected, but you remediated it by completing MFA.
Sign in to entra.microsoft.com using the MyTestUser1 account and complete the MFA process.
Step 5: List risk detections
Rerun the request in Step 2 to get the latest risk detection for the MyTestUser1 user account. Because MFA was completed in Step 4, the riskState for this latest sign in event is now remediated.
[Optional] Block the user from signing in
Instead of providing the opportunity for the user to self-remediate, you can block the user who is associated with a risky sign in from signing in. In this step, you create a new conditional access policy that blocks the user from signing in if a medium or high risk detection occurs. The difference in between this policy and the preview policy in Step 3 is that the builtInControls is now set to block.
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.Models;
var requestBody = new ConditionalAccessPolicy
{
DisplayName = "Policy for risky sign-in block access",
State = ConditionalAccessPolicyState.Enabled,
Conditions = new ConditionalAccessConditionSet
{
SignInRiskLevels = new List<RiskLevel?>
{
RiskLevel.High,
RiskLevel.Medium,
},
Applications = new ConditionalAccessApplications
{
IncludeApplications = new List<string>
{
"All",
},
},
Users = new ConditionalAccessUsers
{
IncludeUsers = new List<string>
{
"4628e7df-dff3-407c-a08f-75f08c0806dc",
},
},
},
GrantControls = new ConditionalAccessGrantControls
{
Operator = "OR",
BuiltInControls = new List<ConditionalAccessGrantControl?>
{
ConditionalAccessGrantControl.Block,
},
},
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
var result = await graphClient.Identity.ConditionalAccess.Policies.PostAsync(requestBody);
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
ConditionalAccessPolicy conditionalAccessPolicy = new ConditionalAccessPolicy();
conditionalAccessPolicy.setDisplayName("Policy for risky sign-in block access");
conditionalAccessPolicy.setState(ConditionalAccessPolicyState.Enabled);
ConditionalAccessConditionSet conditions = new ConditionalAccessConditionSet();
LinkedList<RiskLevel> signInRiskLevels = new LinkedList<RiskLevel>();
signInRiskLevels.add(RiskLevel.High);
signInRiskLevels.add(RiskLevel.Medium);
conditions.setSignInRiskLevels(signInRiskLevels);
ConditionalAccessApplications applications = new ConditionalAccessApplications();
LinkedList<String> includeApplications = new LinkedList<String>();
includeApplications.add("All");
applications.setIncludeApplications(includeApplications);
conditions.setApplications(applications);
ConditionalAccessUsers users = new ConditionalAccessUsers();
LinkedList<String> includeUsers = new LinkedList<String>();
includeUsers.add("4628e7df-dff3-407c-a08f-75f08c0806dc");
users.setIncludeUsers(includeUsers);
conditions.setUsers(users);
conditionalAccessPolicy.setConditions(conditions);
ConditionalAccessGrantControls grantControls = new ConditionalAccessGrantControls();
grantControls.setOperator("OR");
LinkedList<ConditionalAccessGrantControl> builtInControls = new LinkedList<ConditionalAccessGrantControl>();
builtInControls.add(ConditionalAccessGrantControl.Block);
grantControls.setBuiltInControls(builtInControls);
conditionalAccessPolicy.setGrantControls(grantControls);
ConditionalAccessPolicy result = graphClient.identity().conditionalAccess().policies().post(conditionalAccessPolicy);
<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\Models\ConditionalAccessPolicy;
use Microsoft\Graph\Generated\Models\ConditionalAccessPolicyState;
use Microsoft\Graph\Generated\Models\ConditionalAccessConditionSet;
use Microsoft\Graph\Generated\Models\RiskLevel;
use Microsoft\Graph\Generated\Models\ConditionalAccessApplications;
use Microsoft\Graph\Generated\Models\ConditionalAccessUsers;
use Microsoft\Graph\Generated\Models\ConditionalAccessGrantControls;
use Microsoft\Graph\Generated\Models\ConditionalAccessGrantControl;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new ConditionalAccessPolicy();
$requestBody->setDisplayName('Policy for risky sign-in block access');
$requestBody->setState(new ConditionalAccessPolicyState('enabled'));
$conditions = new ConditionalAccessConditionSet();
$conditions->setSignInRiskLevels([new RiskLevel('high'),new RiskLevel('medium'), ]);
$conditionsApplications = new ConditionalAccessApplications();
$conditionsApplications->setIncludeApplications(['All', ]);
$conditions->setApplications($conditionsApplications);
$conditionsUsers = new ConditionalAccessUsers();
$conditionsUsers->setIncludeUsers(['4628e7df-dff3-407c-a08f-75f08c0806dc', ]);
$conditions->setUsers($conditionsUsers);
$requestBody->setConditions($conditions);
$grantControls = new ConditionalAccessGrantControls();
$grantControls->setOperator('OR');
$grantControls->setBuiltInControls([new ConditionalAccessGrantControl('block'), ]);
$requestBody->setGrantControls($grantControls);
$result = $graphServiceClient->identity()->conditionalAccess()->policies()->post($requestBody)->wait();
With this conditional access policy in place, MyTestUser1 account is now blocked from signing in because the sign-in risk level is medium or high.
Step 6: Dismiss risky users
If you believe the user isn't at risk, and you don't want to enforce a conditional access policy, you can manually dismiss the risky user. The request returns a 204 No Content response.
POST https://graph.microsoft.com/v1.0/identityProtection/riskyUsers/dismiss
Content-Type: application/json
{
"userIds": [
"4628e7df-dff3-407c-a08f-75f08c0806dc"
]
}
// Code snippets are only available for the latest version. Current version is 5.x
// Dependencies
using Microsoft.Graph.IdentityProtection.RiskyUsers.Dismiss;
var requestBody = new DismissPostRequestBody
{
UserIds = new List<string>
{
"4628e7df-dff3-407c-a08f-75f08c0806dc",
},
};
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
await graphClient.IdentityProtection.RiskyUsers.Dismiss.PostAsync(requestBody);
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
graphidentityprotection "github.com/microsoftgraph/msgraph-sdk-go/identityprotection"
//other-imports
)
requestBody := graphidentityprotection.NewDismissPostRequestBody()
userIds := []string {
"4628e7df-dff3-407c-a08f-75f08c0806dc",
}
requestBody.SetUserIds(userIds)
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
graphClient.IdentityProtection().RiskyUsers().Dismiss().Post(context.Background(), requestBody, nil)
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
com.microsoft.graph.identityprotection.riskyusers.dismiss.DismissPostRequestBody dismissPostRequestBody = new com.microsoft.graph.identityprotection.riskyusers.dismiss.DismissPostRequestBody();
LinkedList<String> userIds = new LinkedList<String>();
userIds.add("4628e7df-dff3-407c-a08f-75f08c0806dc");
dismissPostRequestBody.setUserIds(userIds);
graphClient.identityProtection().riskyUsers().dismiss().post(dismissPostRequestBody);
<?php
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Graph\Generated\IdentityProtection\RiskyUsers\Dismiss\DismissPostRequestBody;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$requestBody = new DismissPostRequestBody();
$requestBody->setUserIds(['4628e7df-dff3-407c-a08f-75f08c0806dc', ]);
$graphServiceClient->identityProtection()->riskyUsers()->dismiss()->post($requestBody)->wait();
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
from msgraph.generated.identityprotection.riskyusers.dismiss.dismiss_post_request_body import DismissPostRequestBody
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
request_body = DismissPostRequestBody(
user_ids = [
"4628e7df-dff3-407c-a08f-75f08c0806dc",
],
)
await graph_client.identity_protection.risky_users.dismiss.post(request_body)
After dismissing the risk user, you can rerun the request in Step 2 and will notice that the MyTestUser1 user account now has a risk level of none and a riskState of dismissed.
Step 7: Clean up resources
In this step, you delete the two conditional access policies that you created. The request returns a 204 No Content response.
// Code snippets are only available for the latest version. Current version is 5.x
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=csharp
await graphClient.Identity.ConditionalAccess.Policies["{conditionalAccessPolicy-id}"].DeleteAsync();
// Code snippets are only available for the latest major version. Current major version is $v1.*
// Dependencies
import (
"context"
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
//other-imports
)
// To initialize your graphClient, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=go
graphClient.Identity().ConditionalAccess().Policies().ByConditionalAccessPolicyId("conditionalAccessPolicy-id").Delete(context.Background(), nil)
// Code snippets are only available for the latest version. Current version is 6.x
GraphServiceClient graphClient = new GraphServiceClient(requestAdapter);
graphClient.identity().conditionalAccess().policies().byConditionalAccessPolicyId("{conditionalAccessPolicy-id}").delete();
<?php
use Microsoft\Graph\GraphServiceClient;
$graphServiceClient = new GraphServiceClient($tokenRequestContext, $scopes);
$graphServiceClient->identity()->conditionalAccess()->policies()->byConditionalAccessPolicyId('conditionalAccessPolicy-id')->delete()->wait();
# Code snippets are only available for the latest version. Current version is 1.x
from msgraph import GraphServiceClient
# To initialize your graph_client, see https://learn.microsoft.com/en-us/graph/sdks/create-client?from=snippets&tabs=python
await graph_client.identity.conditional_access.policies.by_conditional_access_policy_id('conditionalAccessPolicy-id').delete()