Ich versuche, mithilfe von ASPNET Core eine Aktualisierungs-/Zugriffstoken-Authentifizierungssicherheit für eine einfache Web-API zu implementieren. Bisher läuft es gut. Es funktioniert! Nur nicht wie erwartet.
Nach meinem Verständnis ist bei dieser Art der Einrichtung das erste, was die App überprüfen soll, der JwtBearer innerhalb der Authentifizierung. In diesem Fall das Zugriffstoken, das im Header gesendet wird.
Code: Select all
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["JWT:Issuer"], // Ensure this matches your token's Issuer
ValidateAudience = false, // Assuming you're not validating audience, change if necessary
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]!)),
ValidateLifetime = true, // Ensure lifetime validation is enabled
ClockSkew = TimeSpan.Zero // Optional: set this to zero to remove any time drift
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Check if the "auth_token" cookie is present and extract the token
if (context.Request.Cookies.ContainsKey("auth_token"))
{
context.Token = context.Request.Cookies["auth_token"];
}
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policy => policy.RequireRole("Admin"));
options.AddPolicy("UserPolicy", policy => policy.RequireRole("User"));
});
Jede Anfrage wird auf den Header (Zugriffstoken) überprüft wird eine kurze Lebensdauer haben. Alle paar Minuten gibt es auf der Client-Seite (React) einen useEffect, der versucht, das Zugriffstoken mit der Verwendung des nächsten Endpunkts erneut abzurufen:
Code: Select all
[HttpGet]
public async Task RefreshAccessToken()
{
// Access HttpContext from the ControllerBase class (no need to pass it as a parameter)
if (!HttpContext.Request.Cookies.ContainsKey("auth_token"))
return StatusCode(401, "Unauthorized");
var token = HttpContext.Request.Cookies["auth_token"];
// Decode the JWT to extract claims
var decodedClaims = _refreshTokenServices.DecodeJWT(token!);
if (decodedClaims == null)
return StatusCode(401, "Unauthorized");
// Retrieve the username claim from the decoded JWT
var usernameClaim = decodedClaims?.FindFirst(JwtRegisteredClaimNames.Sub);
// Check if the username claim exists
if (usernameClaim == null)
return StatusCode(401, "Unauthorized");
var username = usernameClaim.Value; // Use the 'Value' to get the actual username
// Find the user by username
var foundUser = await _userManager.FindByNameAsync(username);
// If user is not found, return unauthorized
if (foundUser == null)
return StatusCode(401, "Unauthorized");
// Create a new access token for the user
var accessToken = _accessTokenServices.CreateToken(foundUser);
// Return the new access token
return Ok(new { AccessToken = accessToken.Result });
}
Jetzt habe ich diese Seite der Logik herausgefunden. Aber die Sache ist aus irgendeinem Grund. Diese Struktur ignoriert das Bearer-Token vollständig.
Bei einigen Tests ist mir aufgefallen, dass nur nach HttpOnly gesucht wird, was das Refresh-Token bedeutet.
Seitdem besagtes Refresh-Token Enthält nur Benutzernamen- und ID-Informationen.
Es ist für die rollenbasierte Zugriffskontrollseite der Anwendung nutzlos. Was ich seltsam finde, ist, dass ich, wenn ich das Refresh-Token vollständig lösche, was bedeutet, dass es kein einziges HttpOnly-Cookie gibt, dann die Mühe machen muss, nach dem Bearer-Token zu suchen. Jetzt weiß ich, dass dies wahrscheinlich auf einen Missbrauch oder eine Fehlkonfiguration meinerseits zurückzuführen ist Ich habe wirklich keine Ahnung, wie ich vorgehen soll.
Ich versuche, Attribute zu verwenden, um meine geschützten Routen wie folgt zu verwalten:
Code: Select all
[HttpGet]
[Route("admin-test")]
[Authorize(Roles = "Admin")]
public IActionResult Admin()
{
return Ok("Admin");
}
Irgendwelche Anleitungen Für Ratschläge zur Implementierung dieser Funktionalität wären wir sehr dankbar!
BEARBEITEN: Bei Bedarf sende ich die Cookies auf folgende Weise zurück:
Code: Select all
[HttpPost]
[Route("login")]
public async Task Login([FromBody] LoginRequestDto loginModel)
{
if (!ModelState.IsValid) return BadRequest("Invalid Input Data");
var user = await _userManager.Users.FirstOrDefaultAsync(u => u.UserName == loginModel.Username);
if (user == null) return BadRequest("Wrong Username or Password");
var passwordMatch = await _userManager.CheckPasswordAsync(user, loginModel.Password);
if (!passwordMatch) return BadRequest("Wrong Username or Password");
var refreshToken = _refreshToken.CreateToken(user);
Response.Cookies.Append("auth_token", refreshToken, new CookieOptions()
{
HttpOnly = true,
SameSite = SameSiteMode.None,
Secure = false, //For dev purposes
Expires = DateTime.Now.AddDays(7)
});
var accessToken = _accessToken.CreateToken(user);
return Ok(new { AcessToken = accessToken.Result });
}
}
Code: Select all
public async Task CreateToken(Usuario usuario)
{
var userRoles = await _userManager.GetRolesAsync(usuario);
var authClaims = new List()
{
new Claim(JwtRegisteredClaimNames.Sub, usuario.UserName!),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
authClaims.AddRange(userRoles.Select(role => new Claim(ClaimTypes.Role, role)));
// Ensure the expiration time is UTC to avoid any timezone issues
var expiresAt = DateTime.UtcNow.AddMinutes(15);
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(authClaims),
Expires = expiresAt, // Set expiration time
IssuedAt = DateTime.UtcNow, // Set the issue time
NotBefore = DateTime.UtcNow.AddSeconds(5), // Set the 'NotBefore' to a few seconds after issue time
SigningCredentials = creds,
Issuer = _config["JWT:Issuer"]
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}