Skip to content

Commit

Permalink
Add refresh token
Browse files Browse the repository at this point in the history
  • Loading branch information
oveldman committed Apr 24, 2024
1 parent 89b105e commit 74ceec8
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 13 deletions.
3 changes: 2 additions & 1 deletion sources/ClientSdk.Grpc/Protos/authentication.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ message LoginRequest {
message LoginResponse {
bool isSuccess = 1;
string accessToken = 2;
google.protobuf.Timestamp expiration = 3;
google.protobuf.Timestamp accessTokenExpiration = 3;
string refreshToken = 4;
google.protobuf.Timestamp refreshTokenExpiration = 5;
}

message LogoutResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public class LoginProxyResponse
{
public bool IsSuccess { get; set; }
public string AccessToken { get; set; } = string.Empty;
public DateTime Expiration { get; set; }
public DateTime AccessTokenExpiration { get; set; }
public string RefreshToken { get; set; } = string.Empty;
public DateTime RefreshTokenExpiration { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MadWorldNL.Clients.Identity.Api.Contracts.Authentications;

public class RefreshTokenProxyRequest
{
public string AccessToken { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ private static void AddAuthenticationEndpoints(this WebApplication app)
authenticationService.LogoutAsync())
.RequireAuthorization()
.WithName("Logout");

authenticationEndpoints.MapPost("/RefreshToken",
([FromBody] RefreshTokenProxyRequest request, [FromServices] AuthenticationService authenticationService) =>
authenticationService.RefreshTokenAsync(request))
.WithName("RefreshToken");
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Google.Protobuf.WellKnownTypes;
using MadWorldNL.Clients.Identity.Api.Contracts.Authentications;
using MadWorldNL.Clients.Identity.Api.Shared.Settings;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.Extensions.Options;
using Server.Presentation.Grpc.Authentication.V1;
using LoginRequest = Server.Presentation.Grpc.Authentication.V1.LoginRequest;

namespace MadWorldNL.Clients.Identity.Api.Shared.Services;

Expand All @@ -27,8 +29,9 @@ public async Task<LoginProxyResponse> AuthenticateAsync(LoginProxyRequest proxyR
{
IsSuccess = response.IsSuccess,
AccessToken = response.AccessToken,
Expiration = response.Expiration.ToDateTime(),
RefreshToken = response.RefreshToken
AccessTokenExpiration = response.AccessTokenExpiration.ToDateTime(),
RefreshToken = response.RefreshToken,
RefreshTokenExpiration = response.RefreshTokenExpiration.ToDateTime()
};
}

Expand All @@ -41,4 +44,24 @@ public async Task<LogoutProxyResponse> LogoutAsync()
IsSuccess = response.IsSuccess
};
}

public async Task<LoginProxyResponse> RefreshTokenAsync(RefreshTokenProxyRequest proxyRequest)
{
var request = new TokenRefreshRequest()
{
Audience = _identitySettings.Audience,
RefreshToken = proxyRequest.AccessToken
};

var response = await client.TokenRefreshAsync(request);

return new LoginProxyResponse()
{
IsSuccess = response.IsSuccess,
AccessToken = response.AccessToken,
AccessTokenExpiration = response.AccessTokenExpiration.ToDateTime(),
RefreshToken = response.RefreshToken,
RefreshTokenExpiration = response.RefreshTokenExpiration.ToDateTime()
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ public async Task<LoginProxyResponse> LoginAsync(LoginProxyRequest request)
return response;
}

public async Task<LoginProxyResponse> RefreshAsync(string accessToken)
{
var request = new RefreshTokenProxyRequest()
{
AccessToken = accessToken
};

var response = await _authenticationService.RefreshTokenAsync(request);
await _authenticationStorage.SetAccessTokenAsync(response);
await _authenticationStateProvider.GetAuthenticationStateAsync();

return response;
}

public async Task<LoginProxyResponse> GetActiveTokenFromSession()
{
return await _authenticationStorage.GetAccessTokenAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ public async Task<LoginProxyResponse> LoginAsync(LoginProxyRequest request)
return await response.Content.ReadFromJsonAsync<LoginProxyResponse>()
?? new LoginProxyResponse();
}

public async Task<LoginProxyResponse> RefreshTokenAsync(RefreshTokenProxyRequest request)
{
var response = await _httpClient.PostAsJsonAsync($"{Endpoint}/RefreshToken", request);
return await response.Content.ReadFromJsonAsync<LoginProxyResponse>()
?? new LoginProxyResponse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications;
public interface IAuthenticationManager
{
Task<LoginProxyResponse> LoginAsync(LoginProxyRequest request);
Task<LoginProxyResponse> RefreshAsync(string accessToken);
Task<LoginProxyResponse> GetActiveTokenFromSession();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications;
public interface IAuthenticationService
{
Task<LoginProxyResponse> LoginAsync(LoginProxyRequest request);
Task<LoginProxyResponse> RefreshTokenAsync(RefreshTokenProxyRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net;
using System.Net.Http.Headers;
using MadWorldNL.Clients.Identity.Api.Contracts.Authentications;

namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications;

Expand Down Expand Up @@ -46,9 +47,19 @@ private async Task AddAuthorizationHeader(HttpRequestMessage request)
{
throw new RefreshTokenInvalidException();
}

if (accessToken.AccessTokenExpiration.AddMinutes(-5) < DateTimeOffset.UtcNow)
{
accessToken = await RefreshAccessToken(accessToken);
}

AddBearerToken(request, accessToken.AccessToken);
}

private async Task<LoginProxyResponse> RefreshAccessToken(LoginProxyResponse accessToken)
{
return await _authenticationManager.RefreshAsync(accessToken.RefreshToken);
}

private static void AddBearerToken(HttpRequestMessage request, string token)
{
Expand Down
8 changes: 5 additions & 3 deletions sources/Server.Application/Users/LoginUseCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ public async Task<TokenResponse> Login(Login request)
var jwt = _jwtGenerator.GenerateToken(user!, request.Audience, roles);
var token = GenerateRefreshToken();

var refreshToken = new RefreshToken(token, request.Audience, expires: DateTime.UtcNow.AddDays(7), user!.Id);
var refreshTokenExpires = DateTime.UtcNow.AddDays(7);
var refreshToken = new RefreshToken(token, request.Audience, refreshTokenExpires, user!.Id);
await _userRepository.AddRefreshToken(refreshToken);

return new TokenResponse()
{
IsSuccess = true,
Jwt = jwt.Token,
Expires = jwt.Expires,
RefreshToken = token
JwtExpires = jwt.Expires,
RefreshToken = token,
RefreshTokenExpires = refreshTokenExpires
};
}

Expand Down
5 changes: 3 additions & 2 deletions sources/Server.Application/Users/RefreshTokenUseCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ public async Task<TokenResponse> RefreshToken(Token request)
{
IsSuccess = true,
Jwt = jwt.Token,
Expires = jwt.Expires,
RefreshToken = request.RefreshToken
JwtExpires = jwt.Expires,
RefreshToken = request.RefreshToken,
RefreshTokenExpires = refreshToken.Expires
};
}
}
5 changes: 3 additions & 2 deletions sources/Server.Domain/Users/Login/TokenResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ public class TokenResponse
{
public bool IsSuccess { get; init; }
public string Jwt { get; init; } = string.Empty;
public DateTimeOffset Expires { get; init; }
public DateTimeOffset JwtExpires { get; init; }
public string RefreshToken { get; init; } = string.Empty;
public DateTimeOffset RefreshTokenExpires { get; init; }

public string Message { get; set; } = string.Empty;

Expand All @@ -14,7 +15,7 @@ public static TokenResponse AccessDenied()
return new TokenResponse()
{
Message = "Access denied",
Expires = DateTimeOffset.MinValue,
JwtExpires = DateTimeOffset.MinValue,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ public static LoginResponse ToLoginResponse(this TokenResponse response)
{
AccessToken = response.Jwt,
RefreshToken = response.RefreshToken,
Expiration = response.Expires.ToTimestamp(),
IsSuccess = response.IsSuccess
AccessTokenExpiration = response.JwtExpires.ToTimestamp(),
IsSuccess = response.IsSuccess,
RefreshTokenExpiration = response.RefreshTokenExpires.ToTimestamp()
};
}
}

0 comments on commit 74ceec8

Please sign in to comment.