Until now, this blog was entirely a read-only site. Content is stored in a database, and the app reads from that DB, but there was no code for writing to the DB. In order to update content, I''d manually write SQL queries. I needed to be able to secure some features of the site so I could get away from that practice.
Here were my goals for this:
I worked off this helpdoc at docs.microsoft.com. It links to a sample project I could use as a reference, which was useful while troubleshooting.
Step 1 was to install Nuget package Microsoft.AspNet.Core.Authentication.AzureAD.UI.
To use Azure AD auth, you need to create an App Registration. I did mine point-and-click in the Azure Portal.
More info on how to do this at docs.microsoft.com. By the end of it, I had a TenantId
and a ClientId
. I also had to make sure my redirect urls were correct - I needed danschnau.com/oidc-login
for prod and localhost:8888/oidc-login
for local development.
In my ASP.NET Core 3.1 app, I plugged in a few settings into my appsettings.json
. These were copied from the sample project.
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
"TenantId": "TENANT_ID_GOES_HERE",
"ClientId": "CLIENT_ID_GOES_HERE",
"CallbackPath": "/signin-oidc"
},
Startup.cs
Configure
ChangesIn Configure
, I added app.UseAuthorization()
and app.UseAuthentication()
.
// This method gets called by the runtime. Use this method to configure
// the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// snip
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { /* snip */ });
}
ConfigureServices
ChangesIn ConfigureServices
, I added a few things.
I changed services.ConfigureMvc()
to instead to a bit of Authorization configuration:
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
I also had to call services.AddAuthentication()
:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
Then I had to call services.AddAuthorization
, specifying that the only way to be authorized is to have a claim of my email address.
services.AddAuthorization(options =>
{
options.AddPolicy("adminpolicy", o =>
{
o.RequireAssertion(authorizationHandlerContext =>
authorizationHandlerContext.User
.HasClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "dsschnau@gmail.com"));
});
});
It took a bit of trial and error to get that working.
[AllowAnonymous]
To Public ControllersThis blog is a pretty vanilla MVC app. All the Controllers that service public-facing pages got the [AllowAnonmyous]
tag put on their clases.
[AllowAnonymous]
public class HomeController : Controller
Then I could force a Controller to lock down access to only me:
[Authorize("adminpolicy")]
public class AdminController : Controller
That was it! All done.
This all worked well, taking 2-3 hours to implement this. Having examples, well-written documentation, and helpful error messages, I was never stuck troubleshooting for too long.
There''s more I'd like to do around Authentication/Authorization on Dan Schnau Dot Com in the future - adding the ability to leave comments comes to mind.
If you have feedback, questions or advice - email me at dsschnau@gmail.com.