# 2 · Require roles and confirmation

> Tutorial step 2: restrict actions to roles and ask the user to confirm before the agent changes data, then try both cases.

- URL: https://docs.mentoragent.net/get-started/tutorial/roles-and-confirmation
- Verified with: MentorAgent 1.0.0
- Last updated: 2026-10-13
- Duration: 15 min
- Packages: Novior.MentorAgent

Right now anyone who reaches the widget can add a note. In this step you decide who may change a case. The app gets two roles for its support staff: `Agents` may add notes, and `Supervisors` may also close cases. Here `Agents` is a role of people, not an AI agent. Every change still asks for confirmation.

**Before you start:** finish [1 · Turn service methods into actions](https://docs.mentoragent.net/get-started/tutorial/actions).

## Put rules on the actions that change data

Replace the content of `CaseService.cs`. The reads stay as they are; `AddNote` gets roles, and a new action closes a case:

```csharp title="CaseService.cs"
using System.ComponentModel;
using Novior.MentorAgent.Attributes;

public class CaseService(CaseStore store)
{
    [MentorAction(
        Description = "Gets a support case by its number, with its status, priority and notes",
        ReadOnly = true)]
    public SupportCase? GetCase(
        [Description("The case number, for example C-1")] string caseNumber)
        => store.Find(caseNumber);

    [MentorAction(
        Description = "Lists support cases, optionally only those with a given status",
        ReadOnly = true)]
    public IReadOnlyList<SupportCase> ListCases(
        [Description("Open, Waiting for customer or Closed. Leave empty for every case")] string? status = null)
        => store.List(status);

    [MentorAction(
        Description = "Adds a note to a support case",
        RequiresConfirmation = true,
        RequiredRoles = ["Agents", "Supervisors"])]
    public string AddNote(string caseNumber, string text)
    {
        store.AddNote(caseNumber, text);
        return $"Note added to case {caseNumber}.";
    }

    [MentorAction(
        Description = "Closes a support case with a resolution",
        RequiresConfirmation = true,
        RequiredRoles = ["Supervisors"])]
    public string CloseCase(string caseNumber, string resolution)
    {
        store.Close(caseNumber, resolution);
        return $"Case {caseNumber} closed.";
    }
}
```

- `RequiredRoles` lists the roles that may run the action. Holding any one of them is enough: `AddNote` is for agents and supervisors, `CloseCase` only for supervisors.
- `RequiresConfirmation = true` keeps the confirmation box on both. The role decides who may act. The confirmation asks the user, every time, whether to act. A `[MentorAction]` that is not read-only asks even without it. Writing `RequiresConfirmation = true` puts the rule in your code.
- `CloseCase` becomes the tool `close_case`.
- The reads have no rules, so anyone who reaches the widget can call them. Protect reads the same way when the data is not for everyone.

## Sign users in

MentorAgent checks roles with `ClaimsPrincipal.IsInRole` (the `ClaimTypes.Role` claims, by default) on the signed-in user. It gets that user from the `AuthenticationStateProvider` that Blazor already has. The Cases app has no sign-in yet. In a real app, users sign in with ASP.NET Core Identity, Microsoft Entra ID or another scheme. For this tutorial, a cookie and a test endpoint are enough.

1. At the top of `Program.cs`, add the `using` directives:

   ```csharp title="Program.cs"
   using System.Security.Claims;
   using Microsoft.AspNetCore.Authentication;
   using Microsoft.AspNetCore.Authentication.Cookies;
   ```

2. Before `builder.Build()`, register cookie authentication:

   ```csharp title="Program.cs"
   builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
       .AddCookie();
   ```

3. After `var app = builder.Build();`, add the test endpoints:

   ```csharp title="Program.cs"
   if (app.Environment.IsDevelopment())
   {
       // For this tutorial only: sign in as a test user, with or without a role
       app.MapGet("/dev/sign-in", async (HttpContext http, string name, string? role) =>
       {
           List<Claim> claims = [new(ClaimTypes.Name, name)];
           if (!string.IsNullOrEmpty(role))
               claims.Add(new(ClaimTypes.Role, role));

           await http.SignInAsync(new ClaimsPrincipal(
               new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)));
           return Results.Redirect("/");
       });

       app.MapGet("/dev/sign-out", async (HttpContext http) =>
       {
           await http.SignOutAsync();
           return Results.Redirect("/");
       });
   }
   ```

- `/dev/sign-in?name=Alex&role=Agents` signs you in as Alex, with the role `Agents`. Leave out `role` to sign in with no role, and open `/dev/sign-out` to sign out.
- ASP.NET Core adds the authentication middleware by itself when authentication is registered, so the rest of the pipeline stays as it is.
- Each endpoint redirects to the home page. The page loads again, and the widget works for the new user.

> **Caution:**
> The test endpoints sign in anyone, with any role written in the address. Keep them inside `IsDevelopment()`, as above, and replace them with your real sign-in before the app leaves your machine.

## Try it

Run the app and try the same requests as three different users. To sign in as a user, add a path such as `/dev/sign-in?name=Alex&role=Agents` to the address of the app in the browser.

### Without signing in

1. Type "Close case C-2, it is a duplicate of C-1". No confirmation box appears. The agent tells you that you need to sign in, and the case stays open.

### With the Agents role

1. Open `/dev/sign-in?name=Alex&role=Agents`.
2. Type "Add a note to C-2: same issue as C-1". The confirmation box appears. Click **Confirm**, and the note is added.
3. Type "Close case C-2, it is a duplicate of C-1". No box appears: the agent tells you that closing a case needs the role Supervisors.

### With the Supervisors role

1. Open `/dev/sign-in?name=Sam&role=Supervisors`.
2. Ask again to close C-2. The confirmation box shows the case number and the resolution.
3. Click **Cancel**. The method does not run, and the agent tells you in one sentence that it did not proceed.
4. Ask once more and click **Confirm**. Case C-2 is closed.

## What the model receives

- **The same tools for everyone.** `close_case` is in the tool list even when Alex is signed in. The check runs in your app when the tool is called, not in the model.
- **Who is asking.** For a signed-in user, the model also receives the name and the roles. For a user who is not signed in, it receives nothing about the user.
- **A refusal instead of a result.** When the role is missing, the method does not run and no box appears. The model receives an access-denied error instead. The error names the required roles, or says that the user must sign in, and the model passes it on to the user.
- **A cancellation.** When the user clicks Cancel, the method does not run. The model is told that the user declined and that nothing was checked or changed. It is asked not to try again, not to give another reason, and not to describe the state of any record.

The order never changes: first the role, then the confirmation, then your method. MentorAgent reads the roles once more after the user confirms, so a user who lost the role while the box was open is refused.

> **Note:**
> Roles decide who may run an action, not which records a user may see. Filtering the data that a method returns stays in your method.

## Next step

The coordinator, the main agent that answers you, does everything itself. In [3 · Hand off to a specialist agent](https://docs.mentoragent.net/get-started/tutorial/specialist-agent) you move the case actions to a specialist, and the rules move with them.

[Two checks, in this order](https://docs.mentoragent.net/concepts/governance#two-checks-in-this-order) explains both checks, and [Where nobody can answer](https://docs.mentoragent.net/concepts/governance#where-nobody-can-answer) what changes on the other channels.

---

- Previous: [1 · Turn service methods into actions](https://docs.mentoragent.net/get-started/tutorial/actions.md)
- Next: [3 · Hand off to a specialist agent](https://docs.mentoragent.net/get-started/tutorial/specialist-agent.md)
