.Net Core Identity Server 4 Authentication VS Identity Authentication

.NET Core Identity Server 4 Authentication VS Identity Authentication

TL;DR

IdentityServer = token encryption and validation services via OAuth 2.0/OpenId-Connect

ASP.NET Identity = current Identity Management strategy in ASP.NET

How can I authenticate similar to the way done in previous version's of .Net does the old way still work or is there a newer version.

I see no reason why you couldn't achieve the old way in ASP.NET Core, but in general, that strategy was replaced with ASP.NET Identity, and ASP.NET Identity is alive and well in ASP.NET Core.

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity

ASP.NET Identity uses a backing store like SQL Server to hold user information like username, password (hashed), email, phone and easily be extended to hold FirstName, LastName or whatever else. So, there really no reason to encrypt user information into a cookie and pass it back and forth from client to server. It supports notions like user claims, user tokens, user roles, and external logins. Here are the entities in ASP.NET Identity:

  • AspNetUsers
  • AspNetUserRoles
  • AspNetUserClaims
  • AspNetUserLogins (for linking external identity providers, like Google, AAD)
  • AspNetUserTokens (for storing things like access_tokens and refresh_tokens amassed by the user)

What are the pros and cons of using your own token server verses creating your own custom principle?

A token server would be a system that generates a simple data structure containing Authorization and/or Authentication information. Authorization usually takes the for of a token named access_token. This would be the "keys to the house", so to speak, letting you through the doorway and into the residence of a protected resource, usually a web api. For Authentication, the id_token contains a unique identifier for a user/person. While it is common to put such an identifier in the access_token, there is now a dedicated protocol for doing that: OpenID-Connect.

The reason to have your own Security Token Service (STS), would to be to safeguard your information assets, via cryptography, and control which clients (applications) can access those resources. Furthermore, the standards for identity controls now exist in OpenID-Connect specifications. IdentityServer is an example of a OAuth 2.0 Authorization Server combined with an OpenID-Connect Authentication server.

But none of this is necessary if you just want a user table in your application. You don't need a token server- just use ASP.NET Identity. ASP.NET Identity maps your User to a ClaimsIdentity object on the server- no need for a custom IPrincipal class.

When using a cloud based solution or a separate Token server how would you Integrate that with your current application, would I would still need a users table in my application how would you associate the two?

See these tutorials for integrating separate identity solutions with an application:
https://identityserver4.readthedocs.io/en/latest/quickstarts/0_overview.html
https://auth0.com/docs/quickstart/webapp/aspnet-core

At a minimum you would need a two column table mapping the username to the external provider's user identifier. This is what the AspNetUserLogins table does in ASP.NET Identity. The rows in that table however are dependent on the being a User record in AspNetUsers.

ASP.NET Identity supports external providers like Google, Microsoft, Facebook, any OpenID-Connect provider, Azure AD are already there. (Google and Microsoft have already implemented the OpenID-Connect protocol so you don't need their custom integration packages either, like this one, for example). Also, ADFS is not yet available on ASP.NET Core Identity.

See this doc to get started with external providers in ASP.NET Identity:

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/

Being that there are so many different solutions how can I create an enterprise application, to allow Login through Gmail/Facebook while still being able to expand to other SSO's

As explained above, ASP.NET Identity already does this. It's fairly easy to create an "External Providers" table and data drive your external login process. So when a new "SSO" comes along, just add a new row with the properties like the provider's url, the client id and secret they give you. ASP.NET Identity already has the UI built in there Visual Studio templates, but see Social Login for cooler buttons.

Summary

If you just need a users table with password sign in capabilities and a user profile, then ASP.NET Identity is perfect. No need to involve external authorities. But, if have many applications needing to access many apis, then an independent authority to secure and validate identity and access tokens makes sense. IdentityServer is a good fit, or see openiddict-core, or Auth0 for a cloud solution.

My apologies is this isn't hitting the mark or if it is too introductory. Please feel free to interact to get to the bulls-eye you are looking for.

Addendum: Cookie Authentication

To do bare bones authentication with cookies, follow these steps. But, to my knowledge a custom claims principal is not supported. To achieve the same effect, utilize the Claims list of the ClaimPrincipal object.

Create a new ASP.NET Core 1.1 Web Application in Visual Studio 2015/2017 choosing "No Authentication" in the dialog. Then add package:

Microsoft.AspNetCore.Authentication.Cookies

Under the Configure method in Startup.cs place this (before app.UseMvc):

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
LoginPath = new PathString("/Controller/Login/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});

Then build a login ui and post the html Form to an Action Method like this:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(String username, String password, String returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// check user's password hash in database
// retrieve user info

var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username),
new Claim("FirstName", "Alice"),
new Claim("LastName", "Smith")
};

var identity = new ClaimsIdentity(claims, "Password");

var principal = new ClaimsPrincipal(identity);

await HttpContext.Authentication.SignInAsync("MyCookieMiddlewareInstance", principal);

return RedirectToLocal(returnUrl);
}

ModelState.AddModelError(String.Empty, "Invalid login attempt.");

return View();
}

The HttpContext.User object should have your custom claims and are easily retrievable the List collection of the ClaimPrincipal.

I hope this suffices, as a full Solution/Project seems a bit much for a StackOverflow post.

.NET Core Identity vs IdentityServer4

It depends.

IdentityServer will provide you with OAuth 2.0 and OpenID Connect implementation, and it will handle all details for you (providing you endpoints, token management, scopes, grants and so on). It runs independently so you can use it for multiple clients (SPA, mobile, web apps) and it is nicely isolated from rest of your app. If you wish so, you can use it together with ASP.NET Core Identity.

If you don't use IdentityServer, you will have to write some of these things yourself because ASP.NET Core Identity is a membership system and it does not provide any ready to use endpoints and neither token management or support for different ways how to authorize.

You need to evaluate whether it is better for you to write these things yourself but have a more straightforward setup because you probably don't need everything IdentityServer provides although it might limit you in future.

You can also have a look at OpenIddict that is less complicated than IdentityServer.

ASP.NET Identity vs ASP.NET Identity Core: Differences and Usage with Identity Server, etc.?

  1. Is there any difference between ASP.NET Identity vs ASP.NET Identity Core? Or can we integrate ASP.NET Identity Core to an ASP.NET
    Core app as the same manner like ASP.NET Identity in ASP.NET MVC, etc?

It seems no breaking changes from ASP.NET Identity to ASP.NET Core Identity,you could check the document:

https://learn.microsoft.com/en-us/aspnet/core/migration/identity?view=aspnetcore-5.0

We could integrate Identity to ASP.NET Core project,you could refer to the document below:

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-5.0&tabs=visual-studio


  1. Another point I am wondering is if we use Identity Server, can we still use ASP.NET Identity Core? Or is it unnecessary to use it and is
    Identity Server enough for performing all the stuff e.g. user and role
    management like ASP.NET Identity Core can do?

Yes,you wan use Identity together with Identity Server.Furthermore,Identity Server can use ASP.NET Core Identity for user management, allowing the same OAuth/OpenID Connect functionality Microsoft Identity Platform provides, backed by ASP.NET Core Identity.


  1. What about using only Angular side without any implementation on backen (ASP.NET Core) side? Is it possible or good idea to use it with
    Identity Server, OpenID Connect, etc.? Or should we stick on ASP.NET
    Core side for authentication, authorization and role management?

You can use Angular with Identity Server and OpenID Connect.As for sticking on Identity,it depends on you.

IdentityServer will provide you with OAuth 2.0 and OpenID Connect implementation, and it will handle all details for you (providing you endpoints, token management, scopes, grants and so on). It runs independently so you can use it for multiple clients (SPA, mobile, web apps) and it is nicely isolated from rest of your app. If you wish so, you can use it together with ASP.NET Core Identity.

IdentityServer4 and ASP.NET Core5.0 Identity - Role based Authorization

Statup.cs in API Client

public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api1");
});
});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization("ApiScope");
});
}
}

Startup.cs in MVC Client

public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.Scope.Add("email");
options.Scope.Add("roles");
options.ClaimActions.DeleteClaim("sid");
options.ClaimActions.DeleteClaim("idp");
options.ClaimActions.DeleteClaim("s_hash");
options.ClaimActions.DeleteClaim("auth_time");
options.ClaimActions.MapJsonKey("role", "role");
options.Scope.Add("api1");
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
services.AddTransient<AuthenticationDelegatingHandler>();
services.AddHttpClient("ApplicationAPI", client =>
{
client.BaseAddress = new Uri("https://localhost:5002/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
}).AddHttpMessageHandler<AuthenticationDelegatingHandler>();

services.AddHttpClient("ApplicationIdentityServer", client =>
{
client.BaseAddress = new Uri("https://localhost:5001/");
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
});
services.AddHttpContextAccessor();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{area=Admin}/{controller=Home}/{action=Index}/{id?}");
});
}
}

AuthenticationDelegatingHandler in MVC Application

To prevent getting token again.

public class AuthenticationDelegatingHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;

public AuthenticationDelegatingHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);

if (!string.IsNullOrWhiteSpace(accessToken))
{
request.SetBearerToken(accessToken);
}

return await base.SendAsync(request, cancellationToken);
}
}

Config.cs in IdentityServer

public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource("roles", "Your role(s)", new List<string>() { "role" })
};
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};

public static IEnumerable<Client> Clients =>
// we can remove API client here because we call API from MVC
// and pass the token to API Application.
// Only we get token from IdentityServer with MVC Application
new List<Client>
{
new Client
{
ClientId = "client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = { "api1" }
},
new Client
{
ClientId = "mvc",
ClientName = "Application Web",
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets = { new Secret("secret".Sha256()) },
RequirePkce = false,
AllowRememberConsent = false,
RedirectUris = { "https://localhost:5003/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc" },

AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1",
"roles"
}
}
};
}

I added ASP.NET Core Identity in the IdentityServer project

Startup.cs in IdentityServer

public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();

var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.EmitStaticAudienceClaim = true;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Logout";
options.Authentication = new AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromHours(10),
CookieSlidingExpiration = true
};
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddAspNetIdentity<ApplicationUser>();

if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
services.AddAuthentication()
.AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "copy client ID from Google here";
options.ClientSecret = "copy client secret from Google here";
});

services.AddTransient<IEmailSender, EmailSender>();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}

Nuget Packages I used in IdentityServer project:

    <PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />

<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="5.0.12" />

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.12" />

<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />

<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.12" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.12">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.12" />

source code repo

How to add AzureAd authentication to .net core web app using identity framework

To achieve the above requirements, You can use Azure AD authentication & external login in your asp.net application by implementing the code in your appsettings and controller as mentioned here .

As you wanted to implement the application ,

Consider other options before storing users of your Web applications in a local database. Instead of managing users in a local database, it's best practice to store and manage user information outside of the application, such as with Azure Active Directory or Azure Active Directory B2C. Consider Identity Server if the authentication service must run on-premises. Identity Server is a member of the.NET Foundation and is OpenID certified.

To implement from scratch you can refer this blog.

Please refer the below links for the similar discussion & Documentation to get started with :-

  • MICROSOFT DOCUMENTATION|Secure a hosted ASP.NET Core Blazor WebAssembly app with Identity Server

  • SO THREAD|Implement both Individual User Accounts and Azure AD Authentication & .NET Core Identity Server 4 Authentication VS Identity Authentication

  • BLOG| Integrating with External identity Providers



Related Topics



Leave a reply



Submit