Skip to content

Implement User Authentication and Authorization

This guide covers the AZ-204 exam topics for implementing user authentication and authorization:

  • Authenticate and authorize users by using the Microsoft Identity platform
  • Authenticate and authorize users and apps by using Microsoft Entra ID
  • Create and implement shared access signatures
  • Implement solutions that interact with Microsoft Graph

Prerequisites

  • .NET SDK 8.0 (Install via winget: `winget install Microsoft.DotNet.SDK.8))
  • Azure CLI (Install)
  • Azure Functions Core Tools (Install)
  • Azure Subscription (WGU Student Azure account with resource group az204exam)
  • Microsoft 365 Developer Program Subscription (provides Microsoft Entra ID access)

Set Up Entra ID App Registration in Microsoft 365 Developer Tenant

Note: Your WebApp (taskmanagerweb-yourname) is hosted in your WGU Azure tenant, but you'll authenticate users from your Microsoft 365 Developer tenant. We'll configure the app registration as multitenant to enable cross-tenant authentication.

Follow these steps to set up an Entra ID app registration:

  1. Log in to the Microsoft 365 Developer tenant.

  2. Create an app registration.

  3. Verify authentication settings.

  4. Get the tenant domain.

Step 1: Log In to the Microsoft 365 Developer Tenant

Log in to the Azure Portal (https://portal.azure.com) using your personal email address (the one used for the Microsoft 365 Developer Program). Ensure you're in the correct tenant by checking the tenant name in the top-right corner (e.g., yourtenant.onmicrosoft.com). If needed, switch tenants by clicking your profile and selecting the Microsoft 365 Developer tenant.

Step 2: Create App Registration

In the Azure Portal for your Microsoft 365 Developer tenant, go to Microsoft Entra ID > App registrations > New registration. Configure:

  • Name: TaskManagerWebApp.
  • Supported account types: Select Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant) to allow cross-tenant authentication.
  • Redirect URI:
  • Local: https://localhost:5001/signin-oidc (adjust port if different).
  • Azure: https://taskmanagerweb-yourname.azurewebsites.net/signin-oidc (after deployment).
  • Click Register.
  • Note the Application (client) ID and Directory (tenant) ID.

Step 3: Verify Authentication Settings

In the app registration, go to Authentication:

  • Ensure the Web platform is selected with the redirect URI.
  • Enable ID tokens (under Implicit grant and hybrid flows).

Step 4: Get Tenant Domain

In Microsoft Entra ID > Overview, note the Primary domain (e.g., yourtenant.onmicrosoft.com).

Update WebApp for Authentication

Note: We’ll modify our TaskManagerWeb WebApp (hosted in your WGU Azure tenant) to authenticate users from your Microsoft 365 Developer tenant using Microsoft Entra ID.

Follow these steps to update the WebApp for authentication:

  1. Add the Microsoft Identity package.

  2. Configure authentication settings.

  3. Update the Program.cs file.

  4. Create or update the controller.

  5. Update the Razor view with sign-in/out links.

  6. Test locally.

  7. Deploy and test in Azure (optional).

Step 1: Add Microsoft Identity Package

Open a terminal in your TaskManagerWeb project folder and run:

dotnet add package Microsoft.Identity.Web

Step 2: Configure Settings

Update appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "yourtenant.onmicrosoft.com",
    "TenantId": "common", // Use "common" for multitenant apps
    "ClientId": "your-client-id",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Replace yourtenant.onmicrosoft.com with your Microsoft 365 Developer tenant domain and your-client-id with the Application (client) ID from the app registration. Use "TenantId": "common" to allow authentication from any tenant, as the app is multitenant.

Step 3: Update Program.cs

Modify Program.cs to add authentication services:

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

// Add Microsoft Identity authentication
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();

Step 4: Create or Update Controller

Since your WebApp uses a Razor view with @model List<TaskManagerWeb.Models.TaskItem>, we’ll create a controller to handle it.

Create Models/TaskItem.cs (if not already present):

namespace TaskManagerWeb.Models
{
    public class TaskItem
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string DueDate { get; set; }
    }
}

Create Controllers/HomeController.cs:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TaskManagerWeb.Models;
using System.Collections.Generic;

namespace TaskManagerWeb.Controllers
{
    [Authorize] // Require login
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            // Mock task list (replace with Cosmos DB later)
            var tasks = new List<TaskItem>
            {
                new TaskItem { Title = "Sample Task", Description = "Test auth", DueDate = "2025-04-15" }
            };
            return View(tasks);
        }

        [HttpPost]
        public IActionResult Create(string title, string description, string dueDate)
        {
            // Add task logic (e.g., save to Cosmos DB)
            // For now, redirect back to index
            return RedirectToAction("Index");
        }

        public IActionResult SignOut()
        {
            return SignOut("Cookies", OpenIdConnectDefaults.AuthenticationScheme);
        }
    }
}

Step 5: Update Razor View

Ensure your view is at Views/Home/Index.cshtml. Update it to show user info and sign-in/out links:

@model List<TaskManagerWeb.Models.TaskItem>

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Your existing CSS -->
</head>
<body>
    <h1>Task Manager</h1>
    @if (User.Identity.IsAuthenticated)
    {
        <p>Welcome, @User.Identity.Name! <a asp-controller="Home" asp-action="SignOut">Sign Out</a></p>
    }
    else
    {
        <p><a href="/MicrosoftIdentity/Account/SignIn">Sign In</a></p>
    }

    <form asp-action="Create" method="post">
        <div>
            <label>Title:</label>
            <input type="text" name="title" required />
        </div>
        <div>
            <label>Description:</label>
            <textarea name="description"></textarea>
        </div>
        <div>
            <label>Due Date:</label>
            <input type="date" name="dueDate" required />
        </div>
        <button type="submit">Add Task</button>
    </form>

    <h2>Tasks</h2>
    <ul>
        @if (Model != null && Model.Any())
        {
            foreach (var task in Model)
            {
                <li>
                    <div class="task-info">
                        <strong>@task.Title</strong> - @task.Description
                        <br />
                        <small>Due: @(task.DueDate ?? "No due date")</small>
                    </div>
                </li>
            }
        }
        else
        {
            <li class="no-tasks">No tasks yet.</li>
        }
    </ul>
</body>
</html>

Step 6: Test Locally

Run the WebApp locally:

cd TaskManagerWeb
dotnet run

Access https://localhost:5001 (adjust port if different). Expect a redirect to https://login.microsoftonline.com/... for login. Sign in with a user from your Microsoft 365 Developer tenant (e.g., an admin or user account created in your Microsoft 365 Developer sandbox). After login, see the Task Manager with “Welcome, [Your Name]!”.

Verify:

  • Without login, you can’t access the task list.
  • After login, you see tasks and can submit the form.
  • Click Sign Out to end the session.

Step 7: Deploy and Test (Optional)

Deploy to Azure (in your WGU Azure tenant):

az webapp up --name taskmanagerweb-yourname --resource-group az204exam

Update the redirect URI in the app registration (Microsoft 365 Developer tenant) to https://taskmanagerweb-yourname.azurewebsites.net/signin-oidc. Test at https://taskmanagerweb-yourname.azurewebsites.net. Sign in with a Microsoft 365 Developer tenant user.

Create and Implement Shared Access Signatures (SAS)

Shared Access Signatures (SAS) allow you to grant limited access to Azure Blob Storage resources. We'll create a SAS token for a container and use it to upload a file.

Steps

Follow these steps to implement SAS:

  1. Create a Blob Storage account and container.

  2. Generate a SAS token for the container.

  3. Use the SAS token to upload a file.

Step 1: Create a Blob Storage Account and Container

Use the Azure CLI to create a storage account and container (if not already created in previous sections):

az storage account create --name taskmanagerstorage --resource-group az204exam --location westus --sku Standard_LRS
az storage container create --name tasks --account-name taskmanagerstorage

Step 2: Generate a SAS Token

Generate a SAS token with write permissions for the tasks container.

expiry=$(date -u -d "1 hour" '+%Y-%m-%dT%H:%MZ')
sas=$(az storage container generate-sas --account-name taskmanagerstorage --name tasks --permissions rw --expiry $expiry --output tsv)
echo "SAS Token: $sas"

Step 3: Use the SAS Token to Upload a File

Create a test file and upload it using the SAS token.

echo "Test file content" > testfile.txt
az storage blob upload --account-name taskmanagerstorage --container-name tasks --name testfile.txt --file testfile.txt --sas-token "$sas"

Verify the upload in the Azure Portal under your storage account > Containers > tasks.

Implement Solutions that Interact with Microsoft Graph

With Entra ID access through your Microsoft 365 Developer tenant, we can interact with Microsoft Graph to fetch user profile data and display it in the WebApp.

Steps

Follow these steps to interact with Microsoft Graph:

  1. Grant Microsoft Graph permissions to the app registration.

  2. Add the Microsoft Graph SDK to the WebApp.

  3. Update the controller to fetch user profile data.

  4. Update the Razor view to display the Graph data.

  5. Test locally.

Step 1: Grant Microsoft Graph Permissions

In the Azure Portal (Microsoft 365 Developer tenant), go to Microsoft Entra ID > App registrations > TaskManagerWebApp > API permissions:

  • Click Add a permission > Microsoft Graph > Delegated permissions.
  • Select User.Read (to read the signed-in user's profile).
  • Click Add permissions.
  • If required, click Grant admin consent for [your tenant] to apply the permissions.

Step 2: Add Microsoft Graph SDK

In the TaskManagerWeb directory, add the Microsoft Graph SDK:

dotnet add package Microsoft.Graph
dotnet add package Microsoft.Identity.Web.MicrosoftGraph

Step 3: Update the Controller

Update Controllers/HomeController.cs to fetch user profile data using Microsoft Graph:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TaskManagerWeb.Models;
using System.Collections.Generic;
using Microsoft.Graph;

namespace TaskManagerWeb.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private readonly GraphServiceClient _graphClient;

        public HomeController(GraphServiceClient graphClient)
        {
            _graphClient = graphClient;
        }

        public async Task<IActionResult> Index()
        {
            // Fetch user profile from Microsoft Graph
            var user = await _graphClient.Me.Request().GetAsync();
            ViewBag.UserProfile = new { DisplayName = user.DisplayName, Email = user.Mail ?? user.UserPrincipalName };

            // Mock task list (replace with Cosmos DB later)
            var tasks = new List<TaskItem>
            {
                new TaskItem { Title = "Sample Task", Description = "Test auth", DueDate = "2025-04-15" }
            };
            return View(tasks);
        }

        [HttpPost]
        public IActionResult Create(string title, string description, string dueDate)
        {
            // Add task logic (e.g., save to Cosmos DB)
            // For now, redirect back to index
            return RedirectToAction("Index");
        }

        public IActionResult SignOut()
        {
            return SignOut("Cookies", OpenIdConnectDefaults.AuthenticationScheme);
        }
    }
}

Update Program.cs to add the Graph client:

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

// Add Microsoft Identity authentication and Microsoft Graph
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "User.Read" })
    .AddMicrosoftGraph("https://graph.microsoft.com/v1.0")
    .AddInMemoryTokenCaches();

builder.Services.AddAuthorization();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();

Step 4: Update the Razor View

Update Views/Home/Index.cshtml to display the Microsoft Graph user profile data:

@model List<TaskManagerWeb.Models.TaskItem>

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Your existing CSS -->
</head>
<body>
    <h1>Task Manager</h1>
    @if (User.Identity.IsAuthenticated)
    {
        <p>Welcome, @User.Identity.Name! (Graph Profile: @ViewBag.UserProfile.DisplayName, @ViewBag.UserProfile.Email) <a asp-controller="Home" asp-action="SignOut">Sign Out</a></p>
    }
    else
    {
        <p><a href="/MicrosoftIdentity/Account/SignIn">Sign In</a></p>
    }

    <form asp-action="Create" method="post">
        <div>
            <label>Title:</label>
            <input type="text" name="title" required />
        </div>
        <div>
            <label>Description:</label>
            <textarea name="description"></textarea>
        </div>
        <div>
            <label>Due Date:</label>
            <input type="date" name="dueDate" required />
        </div>
        <button type="submit">Add Task</button>
    </form>

    <h2>Tasks</h2>
    <ul>
        @if (Model != null && Model.Any())
        {
            foreach (var task in Model)
            {
                <li>
                    <div class="task-info">
                        <strong>@task.Title</strong> - @task.Description
                        <br />
                        <small>Due: @(task.DueDate ?? "No due date")</small>
                    </div>
                </li>
            }
        }
        else
        {
            <li class="no-tasks">No tasks yet.</li>
        }
    </ul>
</body>
</html>

Step 5: Test Locally

Run the WebApp locally:

cd TaskManagerWeb
dotnet run

Sign in with a Microsoft 365 Developer tenant user at https://localhost:5001. After login, you should see the Graph profile data (e.g., “Graph Profile: [Your Name], [Your Email]”) alongside the welcome message.

Clean Up (Optional)

To avoid costs, delete all resources:

az group delete -n az204exam --no-wait --yes

Next Steps

  • Explore other Microsoft Graph APIs (e.g., fetching calendar events or emails).
  • Integrate with Cosmos DB for persistent task storage (as done in previous sections).