As I started my exploration of Identity Server I listed a few alternatives that provided the same type of functionality, but as a Software as a Service. This series of posts will be covering one of the options I mentioned, Auth0.
The big selling points for Auth0, and other services like it, are that it removes you from having to worry about Auth/User Management and get to the part of your applications that bring value to your customers. As with Identity Server, Auth0 can use OpenID Connect (as well as a lot of other protocols), single sign-on and API Access Control.
Sign-up
The first step in getting started is to sign up for a new account. Here is a link to the sign-up page (not an affiliate link). You can use a username and password or a social login. I’m going the social route using GitHub.
After user creation then there are a couple of setup steps. The first is to choose a tenant domain and region.
Click next and on the second step, there are questions about what the account is going to be used for. In my case, it is a personal, developer, who is just playing around.
Click Create Account to finish the creation of your account which will then land you on the account dashboard page.
Auth0 Setup
Client
From the Auth0 Dashboard click the New Client button. Give the client a name, TestMvc in my case, and select Regular Web Applications as the type.
In a follow-up post I will be covering the Single Page Web Application, but for this post, we are going to be using MVC since it tends to be simpler. The next page defaults to a framework selection which seems to be a guide to getting going for the framework you select. We are going to skip that and click on the Settings tab.
On the settings page, we need to fill in a value for Allowed Callback URLs. The sample client should use http://localhost:50774/signin-auth0. Click the Save Changes button.
API
While we are doing some setup on the Auth0 site we are going to go ahead and set up our API as well. Click the APIs menu option on the left menu.
Then click the Create API button. In the dialog enter a Name and Identifier and click the Create button.
Sample Solution Structure and Setup
The sample solution for this post has two projects.
- ApiApp – Backend application and is a resource that is will require authorization to access. The API is an ASP.NET Core Web API.
- ClientApp – Frontend application that will be requesting authorization. This is an ASP.NET Core application that is hosting an Angular (4) application. Note for this post we will be using MVC and not Angular. A future post will deal with the Angular side.
The sample solution with the two projects already added can be found here. If you are using the sample solution feel free to skip the next two sub-sections as they are going over how the projects were created.
To start, add a directory to contain the solution.
API Application
Inside the solution directory, create an ApiApp directory. From the command line in the ApiApp directory run the following command to create a new ASP.NET Core application using the Web API template.
dotnet new webapi
Client Application
Inside the solution directory, create a ClientApp directory. From the command line in the ClientApp directory run the following command to create a new ASP.NET Core application using the Angular template which as of this writing outputs an Angular 4 application.
dotnet new angular
After generation is done run the following to make the all the NPM packages that are required get installed.
npm install
Solution
Inside the solution directory, let’s create a solution file for use with Visual Studio. Run the following command to create a solution file named AspNetCoreAngularAuth0.sln.
dotnet new sln --name AspNetCoreAngularAuth0
Next, run the following two commands to add the API and Client projects to the solution.
dotnet sln add ApiApp/ApiApp.csproj
dotnet sln add ClientApp/ClientApp.csproj
Securing the API Application
Open the appsettings.json file and add a section for Auth0. We are going to need to store the Auth0 domain (tenant domain from sign up) and API Identifier (from the creation of the API at Auth0). The following is the full file from the API project with the new Auth0 section.
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"Auth0": {
"Domain": "yourTenantDomain.auth0.com",
"ApiIdentifier": "http://localhost:50467/"
}
}
Next, in the ConfigureServices function of the Startup class add the following to add authentication using the JWT Bearer scheme to the DI system.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = $"https://{Configuration["Auth0:Domain"]}/";
options.Audience = Configuration["Auth0:ApiIdentifier"];
});
In the Configure function add the following line before app.UseMvc() to add authentication to the HTTP pipeline for the API application.
app.UseAuthentication();
The last step in the API for this post is to add a controller that will require authentication. The following is the full code for the AuthTestController that was added to the Controllers directory.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace ApiApp.Controllers
{
[Route("api/[controller]")]
[Authorize]
public class AuthTestController : Controller
{
[HttpGet]
public string Get()
{
return "Congratulations you are authenticated";
}
}
}
Client Application
In the Client Application open the appsettings.json and add the following setting related to Auth0. This is the full file so the logging section was existing.
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"Auth0": {
"Domain": "yourTenantDomain.auth0.com",
"ClientId": "yourClientId",
"ClientSecret": "yourClientSecret",
"CallbackUrl": "http://localhost:50774/signin-auth0",
"ApiIdentifier": "yourApiIdentifier"
}
}
If you are going to be checking in your code into a publically accessible source control I recommend you use user secrets instead of appsettings.json. You can read more about user secrets here.
Next, in the ConfigureServices function of the Startup class add the following. I’m not going to go over this code line by line the gist is it is setting up the application to using cookies and Open ID Connect for authentication.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("Auth0", options =>
{
options.Authority = $"https://{Configuration["Auth0:Domain"]}";
options.ClientId = Configuration["Auth0:ClientId"];
options.ClientSecret = Configuration["Auth0:ClientSecret"];
options.ResponseType = "code";
options.Scope.Clear();
options.Scope.Add("openid");
options.CallbackPath = new PathString("/signin-auth0");
options.ClaimsIssuer = "Auth0";
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProviderForSignOut = (context) =>
{
var logoutUri = $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";
var postLogoutUri = context.Properties.RedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
{
if (postLogoutUri.StartsWith("/"))
{
// transform to absolute
var request = context.Request;
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
}
logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
}
context.Response.Redirect(logoutUri);
context.HandleResponse();
return Task.CompletedTask;
},
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("audience", Configuration["Auth0:ApiIdentifier"]);
return Task.FromResult(0);
}
};
});
Note that the OnRedirectToIdentityProvider bit is related to getting access to the API.
In the Configure function add the following line before app.UseMvc to add authentication to the HTTP pipeline.
app.UseAuthentication();
Testing Setup
In order to provide a way to test login, logout, and API access without using the Angular portion of the client app, remember that will be a future post, I add an AuthTestController to the Controllers directory with the following.
public class AuthTestController : Controller
{
public async Task<IActionResult> Index()
{
ViewBag.ApiResults = "Not Called";
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", await HttpContext.GetTokenAsync("access_token"));
ViewBag.ApiResults = await (await client.GetAsync("http://localhost:50467/api/authtest")) .Content.ReadAsStringAsync();
return View();
}
public async Task Login(string returnUrl = "/")
{
await HttpContext.ChallengeAsync("Auth0", new AuthenticationProperties() { RedirectUri = returnUrl });
}
[Authorize]
public async Task Logout()
{
await HttpContext.SignOutAsync("Auth0", new AuthenticationProperties
{
RedirectUri = Url.Action("Index", "Home")
});
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
Sorry for the formatting, but that sample has long of verbose statements. Nothing crazy going on in this file. There is an Index action that attempts to call the API and then returns a view. The Login and Logout functions do what they say and were pull right from the official docs.
The associated Index.cshtml file was added to the Views/AuthTest directory with the following.
@{
ViewBag.Title = "Auth Test";
}
@if (User.Identity.IsAuthenticated)
{
<a asp-controller="AuthTest" asp-action="Logout">Logout</a>
}
else
{
<a asp-controller="AuthTest" asp-action="Login">Login</a>
}
<p>
@ViewBag.ApiResults
</p>
This view just shows a link to login or logout and shows the results of the API call. It is ugly but is enough to prove the setup is working.
Wrapping Up
Getting up and running was much fast with Auth0 and would be true of any SASS option I’m sure. It also helped that I have more of an idea of what is going on after all the posts I did on Identity Server. Another positive is Auth0 has some great docs. I used the ASP.NET Core and Web API ones a lot to get this sample application up and running.
Next steps are to get this setup running in the Angular client which should be my next post. The finished code for this post can be found here.