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();