Autenticazione tramite Bearer Token
16 giugno 2025
IIS | .net core 8 Web API MVC | Sql Server | C#

Questo esempio rappresenta una comoda base estendibile per l'implementazione di un micro servizio di autenticazione. Lo schema relazionale del perimetro Policy / Claim รจ volutamente non completamente normalizzato per consentirne un'agevole configurazione ed estrazione delle policy. In un successivo articolo mostreremo il completamento del progetto con l'aggiunta dei servizi di Autorizzazione e di condivisione delle risorse di diversa origine CORS.



Database script

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Policy](
[id] [int] NOT NULL,
[name] [varchar](40) NOT NULL,
[claim_type] [varchar](40) NOT NULL,
[basic] [int] NOT NULL,
CONSTRAINT [PK_Policy] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[ClaimValues](
[policy_id] [int] NOT NULL,
[value] [varchar](40) NOT NULL,
CONSTRAINT [PK_Claim_Values] PRIMARY KEY CLUSTERED
(
[policy_id] ASC,
[value] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[ClaimValues] WITH CHECK ADD CONSTRAINT [FK_ClaimValues_Policy] FOREIGN KEY([policy_id])
REFERENCES [dbo].[Policy] ([id])
GO

ALTER TABLE [dbo].[ClaimValues] CHECK CONSTRAINT [FK_ClaimValues_Policy]
GO

CREATE TABLE [dbo].[Users](
[id] [int] IDENTITY(1,1) NOT NULL,
[email] [varchar](40) NOT NULL,
[name] [varchar](40) NOT NULL,
[surname] [varchar](40) NOT NULL,
[sha256_password] [varchar](44) NOT NULL,
[access_attempt_number] [int] NOT NULL,
[last_attempt_data_access] [datetime] NOT NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Users] ADD CONSTRAINT [DF_access_attempt_number] DEFAULT ((0)) FOR [access_attempt_number]
GO

ALTER TABLE [dbo].[Users] ADD CONSTRAINT [DF_last_attempt_data_access] DEFAULT (getdate()) FOR [last_attempt_data_access]
GO

CREATE TABLE [dbo].[CRS_Users_Policy](
[user_id] [int] NOT NULL,
[policy_id] [int] NOT NULL,
CONSTRAINT [PK_CRS_Customers_Rules] PRIMARY KEY CLUSTERED
(
[user_id] ASC,
[policy_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[CRS_Users_Policy] WITH CHECK ADD CONSTRAINT [FK_CRS_Users_Policy_Policy] FOREIGN KEY([policy_id])
REFERENCES [dbo].[Policy] ([id])
GO

ALTER TABLE [dbo].[CRS_Users_Policy] CHECK CONSTRAINT [FK_CRS_Users_Policy_Policy]
GO

ALTER TABLE [dbo].[CRS_Users_Policy] WITH CHECK ADD CONSTRAINT [FK_CRS_Users_Rules_Users] FOREIGN KEY([user_id])
REFERENCES [dbo].[Users] ([id])
GO

ALTER TABLE [dbo].[CRS_Users_Policy] CHECK CONSTRAINT [FK_CRS_Users_Rules_Users]
GO

 


JwtBearerToken class

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

    public class JwtBearerToken 
    {
        private const int cnst_DefaultExpiresMinutes = 30;
        private const string JwtConfig_Key256 = "JwtConfig:Key256";
        private const string JwtConfig_ExpiresMinutes = "JwtConfig:ExpiresMinutes";

        public const string id = "id";
        public const string hostip = "hostip";
        public static void AddAuthentication(WebApplicationBuilder builder)
        {
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(jwtOption =>
            {
                string? key = builder.Configuration.GetValue<string>(JwtConfig_Key256);
                var keyBytes = Encoding.ASCII.GetBytes(key != null ? key : "");
                jwtOption.SaveToken = true;
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key != null ? key : ""));
                var algorithm = SecurityAlgorithms.HmacSha256;
                var signingCredentials = new SigningCredentials(securityKey, algorithm);
                jwtOption.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingCredentials.Key,
                    ValidateLifetime = true,
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ClockSkew = TimeSpan.Zero,
                };
            });
        }
        public static ClaimsIdentity CustomerClaims(int UserId)
        {
            ClaimsIdentity claims = new ClaimsIdentity();
            using (var db = new UserManagerDbContext())
            {
                var result = (from crs in db.CRS_Users_Policy
                                           join pol in db.Policy
                                           on (crs.policy_id) equals (pol.id)
                                           join clms in db.ClaimValues
                                           on (pol.id) equals (clms.policy_id)
                                           where crs.user_id == UserId
                              select new { type = pol.claim_type,
                                           value = clms.value
                                         }).Distinct().ToList();
                foreach (var claim in result)
                {
                    claims.AddClaim(new Claim(claim.type, claim.value == null ? "" : claim.value));
                }
            }
            return claims;
        }
        public static string Authenticate(IConfiguration configuration, HttpContext HttpContext, int UserId)
        {
            var key = configuration.GetValue<string>(JwtConfig_Key256);
            var expires = configuration.GetValue<string>(JwtConfig_ExpiresMinutes);
            var keyBytes = Encoding.UTF8.GetBytes(key != null? key: "");
            var tokenHandler = new JwtSecurityTokenHandler();
            ClaimsIdentity claims = CustomerClaims(UserId);
            claims.AddClaim(new Claim(id, UserId.ToString()));
            claims.AddClaim(new Claim(hostip, HttpContext.Connection.RemoteIpAddress==null? string.Empty: HttpContext.Connection.RemoteIpAddress.ToString()));
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = claims,
                Expires = DateTime.UtcNow.AddMinutes(expires != null? Convert.ToInt32(expires) : cnst_DefaultExpiresMinutes),
                SigningCredentials = new SigningCredentials(
                                     new SymmetricSecurityKey(keyBytes),
                                     SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor); 
            return JwtBearerDefaults.AuthenticationScheme + " " + tokenHandler.WriteToken(token);
        }
        public static JwtSecurityToken TokenFromHttpHeader(HttpRequest Request)
        {
            var Token = Request.Headers["Authorization"];
            Token = Token.ToString().Split(JwtBearerDefaults.AuthenticationScheme)[1].Trim();
            var handler = new JwtSecurityTokenHandler();
            return (JwtSecurityToken)handler.ReadToken(Token);
        }
        public static string AuthenticationTimeExtend(IConfiguration configuration, HttpRequest Request)
        {
            var key = configuration.GetValue<string>(JwtConfig_Key256);
            var expires = configuration.GetValue<string>(JwtConfig_ExpiresMinutes);
            var keyBytes = Encoding.UTF8.GetBytes(key != null ? key : "");
            var tokenHandler = new JwtSecurityTokenHandler();
            ClaimsIdentity claims = new ClaimsIdentity();
            JwtSecurityToken CurrentToken;
            try 
            { 
                CurrentToken = TokenFromHttpHeader(Request); 
            }
            catch (Exception e)
            {
                throw new Exception(e.Message)
                {
                    Source = "TokenFromHttpHeader",
                    HResult = StatusCodes.Status500InternalServerError,
                };
            }

            try
            {
                claims.AddClaims(CurrentToken.Claims);
            }
            catch (Exception e) 
            {
                throw new Exception(e.Message)
                {
                    Source = "AddClaims",
                    HResult = StatusCodes.Status500InternalServerError,
                };
            }

            if (DateTime.Now > CurrentToken.ValidFrom.AddMinutes((expires != null ? Convert.ToInt32(expires) : cnst_DefaultExpiresMinutes)/2))
            {
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = claims,
                    Expires = DateTime.UtcNow.AddMinutes(expires != null ? Convert.ToInt32(expires) : cnst_DefaultExpiresMinutes),
                    SigningCredentials = new SigningCredentials(
                                         new SymmetricSecurityKey(keyBytes),
                                         SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                string Result;
                try
                {
                    Result = tokenHandler.WriteToken(token);
                }
                catch (Exception e) 
                {
                    throw new Exception(e.Message)
                    {
                        Source = "WriteToken",
                        HResult = StatusCodes.Status500InternalServerError,
                    };
                }
                return JwtBearerDefaults.AuthenticationScheme + " " + Result;
            }
            else return JwtBearerDefaults.AuthenticationScheme + " " + CurrentToken.ToString();
        }
        public static int? UserId(ClaimsPrincipal User)
        {
            if (User.Identity != null)
            {
                var result = User.Claims.FirstOrDefault(C => C.Type == id);
                if (result != null) { return Convert.ToInt32(result.Value); } else { return null; }
            }
            else return null;
        }
    }


appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "default": "User ID=**;Password=**;Data Source=****\\****;initial catalog=*******;Encrypt=false;"
  },
  "JwtConfig": {
    "key256": "********************************",
    "ExpiresMinutes": 180
  },
  "AllowedOrigins": {
    "AllowedOnly": [
      "https://*********"
    ]//,
    //"AllowedAll": []
  },
  "Authentication": {
    "MaxAttempts": 4,
    "PenaltyMinutes": 1
  }
}

Program.cs

var builder = WebApplication.CreateBuilder(args);
.
.
//authentication jwt bearer
JwtBearerToken.AddAuthentication(builder);
.
.
var app = builder.Build();
app.UseAuthentication();

Torna all'inizio