DEV Community

Christos Matskas for The 425 Show

Posted on

Modernize your legacy API authentication with Microsoft.Identity.Web and Azure AD

In a previous blog post, we looked at how to migrate the authentication code of a front-end solution written with ASP.NET Core to using the latest Microsoft.Identity.Web library for .NET. That was part 1 of our short series. For part 2, we will look at how to modernize the auth code in our back-end API using Microsoft.Identity.Web again.

Alt Text

Updating the Web API

I was quite excited upgrading the API to .NET 5 because not only I could get rid of unnecessary code but I was also able to harden the security of the API at the same time. The majority of the redundant code was the identity-related extension classes

  • AzureAdAuthenticationBuilderExtension.cs
  • AzureAdOptions

The code in these classes is part of the Microsoft.Identity.Web now :)

Alt Text

Once these files are removed, we can update the *.csproj file and change it from this:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
  </ItemGroup>

</Project>
Enter fullscreen mode Exit fullscreen mode

to this

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Identity.Web" Version="1.3.0"/>
  </ItemGroup>

</Project>
Enter fullscreen mode Exit fullscreen mode

The only dependency on the updated project is Microsoft.Identity.Web. We can now proceed with the code updates. Open Startup.cs and update the ConfigureServices() method from this

public void ConfigureServices(IServiceCollection services)
{
     services.AddAuthentication(sharedOptions =>
     {
         sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
     })
     .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

     services.AddMvc();
}
Enter fullscreen mode Exit fullscreen mode

to this

public void ConfigureServices(IServiceCollection services)
{
   services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
     services.AddControllers();
}
Enter fullscreen mode Exit fullscreen mode

Finally, we need to update the ToDoListController.cs to take advantage of the newer, simplified way to authorize and validate incoming tokens. The old code

[Authorize]
[Route("api/[controller]")]
public class TodoListController : Controller
{
    static ConcurrentBag<TodoItem> todoStore = new ConcurrentBag<TodoItem>();

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value;
        return todoStore.Where(t => t.Owner == owner).ToList();
    }

    // POST api/values
    [HttpPost]
    public void Post([FromBody]TodoItem Todo)
    {
        string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value;
        todoStore.Add(new TodoItem { Owner = owner, Title = Todo.Title });
    }
}
Enter fullscreen mode Exit fullscreen mode

Can be updated to this:

[Authorize]
[Route("[controller]")]
public class TodoListController : Controller
{
    static ConcurrentBag<TodoItem> todoStore = new ConcurrentBag<TodoItem>();
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
            HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value;
        return todoStore.Where(t => t.Owner == owner).ToList();
     }

     // POST api/values
     [HttpPost]
     public void Post([FromBody]TodoItem Todo)
     {
        string owner = (User.FindFirst(ClaimTypes.NameIdentifier))?.Value;
        todoStore.Add(new TodoItem { Owner = owner, Title = Todo.Title });
     }
}
Enter fullscreen mode Exit fullscreen mode

The important change here is the new addition of HttpContext.VerifyUserHasAnyAcceptedScope() which ensures that the incoming access token has the right scopes.

Source Code

There is a fully working solution showing how the code works end to end on GitHub.

Before the migration
After the migration

Conclusion

Microsoft.Identity.Web has made developers' lives much easier when it comes to authenticating users and acquiring or managing tokens. Although not groundbreaking, the new code is much simpler and more secure by leveraging the Microsoft.Identity.Web library.

Top comments (0)