Use base function if not overrided abscract class

96 views Asked by At

I'm using an abstract class from a third party library (FastEndpoints). I want to override some of the functions which I did but the main function to generate tokens should be the same.

Currently my service looks like this, I override the persistTokenAsync, RefreshRequestValidationAsync and SetRenewalPrivilegesAsync.

public class UserTokenService : RefreshTokenService<TokenRequest, AuthTokenResponse>
{

private readonly UserContext _dbContext;

public UserTokenService(IConfiguration config, UserContext context)
{
    _dbContext = context;
    
    Setup(x =>
    {
        x.TokenSigningKey = config["JWTSigningKey"];
        x.AccessTokenValidity = TimeSpan.FromMinutes(1);
        x.RefreshTokenValidity = TimeSpan.FromHours(1);
        x.Endpoint("/user/auth/refresh-token", ep =>
        {
            ep.Summary(s => s.Description = "this is the refresh token endpoint");
        });
    });
}

public override Task PersistTokenAsync(AuthTokenResponse rsp)
{
    return StoreToken(rsp.UserId, rsp.RefreshExpiry, rsp.RefreshToken);
}

public override async Task RefreshRequestValidationAsync(TokenRequest req)
{
    if (!await TokenIsValid(req.UserId, req.RefreshToken))
        AddError("The refresh token is not valid!");
}

public override async Task SetRenewalPrivilegesAsync(TokenRequest request, UserPrivileges privileges)
{
    privileges.Claims.Add(new("UserID", request.UserId));
    privileges.Permissions.AddRange(new UserPermissions().AllCodes());
}

private async Task<bool> TokenIsValid(string userId, string refreshToken)
{
    var result = await _dbContext.Tokens
        .Where(t => t.Token == refreshToken && t.Id == userId)
        .FirstOrDefaultAsync();

    return result is not null;
}

private async Task StoreToken(string userId, DateTime refreshExpiry, string refreshToken)
{
    var token = _dbContext.Tokens.Where(t => t.Id == userId).FirstOrDefaultAsync();
    _dbContext.Remove(token);
    await _dbContext.Tokens.AddAsync(new RefreshToken{ExpiryDate = refreshExpiry, Token = refreshToken, Id = userId});
    
    await _dbContext.SaveChangesAsync();
}

}

I want to use the CreateToken function from the RefreshTokenService provider by FastEndpoints. IN the last part where I want to create a token as response it's telling me

The type 'Authentication.Api.Services.Authentication.UserTokenService' must be convertible to 'FastEndpoints.IRefreshTokenService<Authentication.Api.Contracts.Responses.UserResponse>' in order to use it as parameter 'TService' in the generic method 'Task FastEndpoints.Endpoint<TRequest,TResponse>.CreateTokenWith(string, Action)'

public class LoginEndpoint : Endpoint<LoginRequest, UserResponse>
{
private readonly IAuthService _authService;
private readonly IUserService _userService;
public LoginEndpoint(IAuthService authService, IUserService userService)
{
    _authService = authService;
    _userService = userService;
}

public override async Task HandleAsync(LoginRequest req, CancellationToken ct)
{
    var user = await _userService.GetByEmailAsync(req.Email);

    var hasMatchingPassword = _authService.IsMatchingPassword(user.Salt,req.Password, user.Password);

    if (!hasMatchingPassword)
    {
        await SendNotFoundAsync(ct);
    }

    var result = user.ToUserResponse();
    
    Response = await CreateTokenWith<UserTokenService>("userID", p =>
    {
        p.Claims.Add(new("UserID", "userID"));
    });
}
}

How do I make my implementation of the RefreshTokenService use the base function to create the token?

https://github.com/FastEndpoints/FastEndpoints/blob/main/Src/Security/RefreshTokens/RefreshTokenService.cs

1

There are 1 answers

1
Dĵ ΝιΓΞΗΛψΚ On BEST ANSWER

i believe your problem stems from the fact that the second generic argument (type of the response dto) doesn't match between your endpoint class and the token service class:

UserTokenService : RefreshTokenService<TokenRequest, XXXXXXXXX>
LoginEndpoint : Endpoint<LoginRequest, XXXXXXXXX>

the response dto type must be the same to be able to call CreateTokenWith<TService>() method.

if your initial login response is drastically different from the refresh token response shape, upgrade to the latest beta version v5.13.0.8-beta and create a custom token within your login endpoint like this:

//instantiate token service yourself
var tokenService = new UserTokenService(Config, _userDbContext); 

Response = await tokenService.CreateCustomToken<UserResponse>(userID, p =>
{
    p.Claims.Add(new("UserID", userID));
},
t => new() //mapping from AuthTokenResponse to UserResponse
{
    Access = t.AccessToken,
    Refresh = t.RefreshToken,
    ...
});