.NET library to allowing a client application get new access tokens
Jwt Refresh Token is .Net library to provide a importante authentication aspects, and using Jwt Token (learn more jwt.io web site) and Refresh Token.
Refresh Token basically require a unique token identifier to obtain additional access tokens. Access token arent'n valid for an long period for security and Refresh Token strategy can help to re-authentication a user without login credential 🤔 (some scratches risks here)
This project is based in Onion Architecture created by Jeffrey Palermo in 2008.
- Jwt.Refresh.Token.Infrastructure
- Jwt.Refresh.Token.Application
- Jwt.Refresh.Token.Domain
- Jwt.Refresh.Token.Tests
Goal of this project is support at last 3 main Azure Databases:
- Azure Cosmos DB for NoSQL preview
- Azure Cosmos DB for PostgreSQL not started
- Azure Sql Database not started
To install Jwt.Refresh.Token.Cosmos (include prereleases), run the following command in the .NET CLI
dotnet add package Jwt.Refresh.Token.Cosmos --prerelease
- ✯ Cosmos setup
The first step to do is provision your cosmos db, or if you already have it, create the token container id (choose name you want) with partitionKey '/userId'.
Implement IUserRepository for get user by id and password. UserId
- ✯ Configure settings app:
"JwtRefreshTokenDescriptor": {
"AlgorithmKey": "YOUR_ALGORITHM_KEY",
"Issuer": "https://your-resource.com",
"Audience": "https://your-resource.com"
},
"JwtRefreshTokenExpires": {
"CreateMilliseconds": 60000,
"RefreshMilliseconds": 900000
},
"JwtRefreshTokenCosmos": {
"ConnectionString": "YOUR_COSMOS_CONNECTIONSTRING",
"DatabaseId": "YOUR_DATABASEID",
"TokenContainerId": "YOUR_TOKEN_CONTAINERID"
}
- ✯ Configure startup app:
Install ASP.NET Core authentication middleware
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
and configuring it in your application’s startup class file:
// [required] Add jwt domain services
builder.Services.AddJwtRefreshTokenServices(builder.Configuration);
// [required (cosmos)] Add jwt cosmos repositories
builder.Services.AddJwtRefreshTokenCosmosServices(builder.Configuration);
// [optional] Bind util token expires config
builder.Services.BindJwtRefreshTokenExpiresOptions(builder.Configuration);
// [required] AspNetCore Authentication config
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// choose your bearer config
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = true;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
.GetBytes(builder.Configuration.GetValue<string>("JwtRefreshTokenDescriptor:AlgorithmKey"))),
ValidateIssuer = true,
ValidateAudience = true
};
});
// [required] AspNetCore Authentication config
builder.Services
.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
- ✯ Create token controller
Creating token controler to management token:
[ApiController]
[Route("[controller]")]
public class TokenController : ControllerBase
{
private readonly ILogger<TokenController> _logger;
private readonly ITokenAppService _tokenAppService;
private readonly IOptionsSnapshot<JwtRefreshTokenExpiresOptions> _jwtRefreshTokenExpiresOptions;
public TokenController(ILogger<TokenController> logger,
ITokenAppService tokenAppService,
IOptionsSnapshot<JwtRefreshTokenExpiresOptions> jwtRefreshTokenExpiresOptions)
{
_logger = logger;
_tokenAppService = tokenAppService;
_jwtRefreshTokenExpiresOptions = jwtRefreshTokenExpiresOptions;
}
private string GetRemoteIpAddress()
{
return this.Request?.HttpContext?.Connection?
.RemoteIpAddress?.ToString();
}
[HttpPost("")]
public async Task<IActionResult> PostAsync([FromForm] string userId,
[FromForm] string password, CancellationToken cancellationToken)
{
var token = await _tokenAppService.CreateAsync(userId,
password, _jwtRefreshTokenExpiresOptions.Value.CreateMilliseconds,
GetRemoteIpAddress(), cancellationToken);
return new TokenResult(token);
}
[Authorize("Bearer")]
[HttpPatch("")]
public async Task<IActionResult> RefreshAsync([FromForm] string tokenId,
[FromForm] string userId, CancellationToken cancellationToken)
{
var token = await _tokenAppService.RefreshAsync(tokenId,
userId, _jwtRefreshTokenExpiresOptions.Value.RefreshMilliseconds,
GetRemoteIpAddress(), cancellationToken);
return new TokenResult(token);
}
[Authorize("Bearer")]
[HttpPatch("/revoke")]
public async Task<IActionResult> RevokeAsync([FromForm] string tokenId,
[FromForm] string userId, CancellationToken cancellationToken)
{
var updated = await _tokenAppService.TryRevokeAsync(tokenId,
userId, GetRemoteIpAddress(), cancellationToken);
return Ok(new { updated = updated });
}
}
See integration test api here
- Incrise unit test coverage
- Create pipeline
- Implement PostgreeSql infrastructure
- Implement Sql Databse infrastructure