5

We are starting a new web application that will be hosted in our customers infrastructure. Since this is a solution that will be here for a while and serve as a base for a lot of future products, we wanted to have a future proof security, that would be future poff (SSO / MFA) but this is something for like in 3 years. It's important for our customer that we rely on some standards, so I thought about using OpenId.

The solution will be based on ASP.NET Core + Angular. So I found out there was ASP.NET Core Identity, already compatible with OpenID Connect, but then I saw here that Microsoft recommends Duende Identity Server (IdentityServer4).

The problem is that we are a small team, building a small application, but in a big enterprise, so will have to go for the licensed version. The other problem is that since its our customers that deploy themself the application, we do not control how many servers will be deployed, therefore we would have to opt for an "enterprise" subscription, which is totally out of our budget.

Despite this, we were hoping that we could still use ASP.NET Core Identity to connect to different sources of users, manage permissions for our app, use the attributes on our controllers.

So, how to use ASP.NET Core Identity, without using IdentityServer?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
J4N
  • 19,480
  • 39
  • 187
  • 340
  • Use PHP instead . However I think OpenIddict is still open-source with the Apache license, so a viable option – Pieterjan Feb 28 '22 at 19:24
  • Would using local accounts be an option? This way you store the user data in your own database – MaartenDev Feb 28 '22 at 22:16
  • Accounts local to what? The solution will be deployed inside of docker components, and we have to be able to backup them. So if you're talking about a local database, yes, how? – J4N Mar 01 '22 at 05:48
  • By the way, most enterprises do not use anything related to IdentityServer, but opt for [Azure ActiveDirectory](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/add-application-portal-setup-sso) instead. As far as I understand, it's way more flexible to configure – Pieterjan Mar 01 '22 at 07:54
  • @Pieterjan I wish I could, but most of our clients are running this on their private network without internet access, I'm not sure how Azure Active Directory would work then? This is used to monitor critical infrastructure(power plant). – J4N Mar 01 '22 at 10:46
  • can i ask you , what solution did you choose ? – CSharp-n Jul 05 '22 at 15:08
  • @CSharp-n Currently none, but we will probably go for the Duende option when required – J4N Jul 06 '22 at 13:26

2 Answers2

3

According to the MSFT docs

ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps.
To secure web APIs and SPAs, use one of the following:

  • Azure Active Directory Azure
  • Active Directory B2C (Azure AD B2C)
  • IdentityServer4

So they first offer their cloud solutions.
Identityserver4 free version is still supported though till the .Net Core 3.1 EOL.
As a free-free option without any predefined EOL, you can try this OpenIddict sample as a start point for your solution, however it has a bit more gaps to be filled in yourself.
And here is an explanation why MSFT don't offer it in their docs (spoiler: see above)

d_f
  • 4,599
  • 2
  • 23
  • 34
  • Thanks for the answer. This is a solution for critical infrastructure monitoring, so as of today, most of our customer doesn't want to have those network connected to internet. I will read about OpenIddict. Do you have anything in mind when you speak about "gap to be filled" ? – J4N Mar 01 '22 at 06:06
  • possibly I was not exact. the "gaps" i mentioned were not lack of common features, but a general requirement to set up everything manually (starting from the protocol endpoints), while Idenityserver provides most of the implementations together with common settings out of the box, requiring you to override only the places you really need to customize – d_f Mar 01 '22 at 19:28
1

You can use pure ASP.NET Core without IdentityServer. It's quite easy if you're using the same backend for authentication and API.

Example (copied from source):

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/security/getMessage", () => "Hello World!").RequireAuthorization();
app.MapPost("/security/createToken",
[AllowAnonymous] (User user) =>
{
    if (user.UserName == "joydip" && user.Password == "joydip123")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes
        (builder.Configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim("Id", Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti,
                Guid.NewGuid().ToString())
             }),
            Expires = DateTime.UtcNow.AddMinutes(5),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials
            (new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha512Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return Results.Ok(stringToken);
    }
    return Results.Unauthorized();
});
app.UseAuthentication();
app.UseAuthorization();
app.Run();

See also:

Mr Patience
  • 1,564
  • 16
  • 30