Skip to content

Commit

Permalink
AuthorizedUser.RefreshAsyncuser.jsonが変更されている場合、自動で再読み込みするようにした (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
yuto-trd authored Jan 19, 2024
1 parent eb1d96e commit 552b02d
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 39 deletions.
82 changes: 49 additions & 33 deletions src/Beutl.Api/BeutlApiApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,16 @@ private void Register<T>(Func<T> factory)
_services.Add(typeof(T), new Lazy<object>(() => factory()));
}

public void SignOut()
public void SignOut(bool deleteFile = true)
{
_authorizedUser.Value = null;
string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
if (deleteFile)
{
File.Delete(fileName);
string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
{
File.Delete(fileName);
}
}
}

Expand Down Expand Up @@ -155,7 +158,7 @@ private async Task<AuthorizedUser> SignInExternalAsync(string provider, Cancella
ProfileResponse profileResponse = await Users.Get2Async(cancellationToken);
var profile = new Profile(profileResponse, this);

_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient);
_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient, DateTime.UtcNow);
SaveUser();
activity?.AddEvent(new("Saved_User"));
return _authorizedUser.Value;
Expand Down Expand Up @@ -196,7 +199,7 @@ public async Task<AuthorizedUser> SignInAsync(CancellationToken cancellationToke
ProfileResponse profileResponse = await Users.Get2Async(cancellationToken);
var profile = new Profile(profileResponse, this);

_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient);
_authorizedUser.Value = new AuthorizedUser(profile, authResponse, this, _httpClient, DateTime.UtcNow);
SaveUser();
activity?.AddEvent(new("Saved_User"));
return _authorizedUser.Value;
Expand Down Expand Up @@ -231,6 +234,8 @@ public void SaveUser()
using var writer = new Utf8JsonWriter(stream);
obj.WriteTo(writer);
}

user._writeTime = File.GetLastWriteTimeUtc(fileName);
}
}

Expand All @@ -240,39 +245,50 @@ public async Task RestoreUserAsync(Activity? activity)
{
activity?.AddEvent(new("Entered_AsyncLock"));

string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
AuthorizedUser? user = await ReadUserAsync();
if (user != null)
{
JsonNode? node;
using (StreamReader reader = File.OpenText(fileName))
{
string json = await reader.ReadToEndAsync();
node = JsonNode.Parse(json);
}
await user.RefreshAsync();

if (node != null)
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
await user.Profile.RefreshAsync();
_authorizedUser.Value = user;
SaveUser();
}
}
}

public async ValueTask<AuthorizedUser?> ReadUserAsync()
{
string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
{
JsonNode? node = JsonNode.Parse(await File.ReadAllTextAsync(fileName));
DateTime lastWriteTime = File.GetLastWriteTimeUtc(fileName);

if (node != null)
{
ProfileResponse? profile = JsonSerializer.Deserialize<ProfileResponse>(node["profile"]);
string? token = (string?)node["token"];
string? refreshToken = (string?)node["refresh_token"];
var expiration = (DateTimeOffset?)node["expiration"];

if (profile != null
&& token != null
&& refreshToken != null
&& expiration.HasValue)
{
ProfileResponse? profile = JsonSerializer.Deserialize<ProfileResponse>(node["profile"]);
string? token = (string?)node["token"];
string? refreshToken = (string?)node["refresh_token"];
var expiration = (DateTimeOffset?)node["expiration"];

if (profile != null
&& token != null
&& refreshToken != null
&& expiration.HasValue)
{
var user = new AuthorizedUser(new Profile(profile, this), new AuthResponse(expiration.Value, refreshToken, token), this, _httpClient);
await user.RefreshAsync();

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", user.Token);
await user.Profile.RefreshAsync();
_authorizedUser.Value = user;
SaveUser();
}
return new AuthorizedUser(
new Profile(profile, this),
new AuthResponse(expiration.Value, refreshToken, token),
this,
_httpClient,
lastWriteTime);
}
}
}

return null;
}

private static int GetRandomUnusedPort()
Expand Down
47 changes: 42 additions & 5 deletions src/Beutl.Api/Objects/AuthorizedUser.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
using System.Diagnostics;
using System.Net.Http.Headers;

using Beutl.Api.Services;

namespace Beutl.Api.Objects;

public class AuthorizedUser(Profile profile, AuthResponse response, BeutlApiApplication clients, HttpClient httpClient)
public class AuthorizedUser(
Profile profile, AuthResponse response,
BeutlApiApplication clients, HttpClient httpClient, DateTime writeTime)
{
private AuthResponse _response = response;
// user.jsonに書き込まれた時間
internal DateTime _writeTime = writeTime;

public Profile Profile { get; } = profile;

public string Token => response.Token;
public string Token => _response.Token;

public string RefreshToken => response.Refresh_token;
public string RefreshToken => _response.Refresh_token;

public DateTimeOffset Expiration => response.Expiration;
public DateTimeOffset Expiration => _response.Expiration;

public bool IsExpired => Expiration < DateTimeOffset.UtcNow;

Expand All @@ -21,12 +29,41 @@ public async ValueTask RefreshAsync(bool force = false)
{
using Activity? activity = clients.ActivitySource.StartActivity("AuthorizedUser.Refresh", ActivityKind.Client);

string fileName = Path.Combine(Helper.AppRoot, "user.json");
if (File.Exists(fileName))
{
DateTime lastWriteTime = File.GetLastWriteTimeUtc(fileName);
if (_writeTime < lastWriteTime)
{
AuthorizedUser? fileUser = await clients.ReadUserAsync();
if (fileUser?.Profile?.Id == Profile.Id)
{
_response = fileUser._response;
_writeTime = lastWriteTime;
}
else if (fileUser != null)
{
clients.SignOut(false);
throw new BeutlApiException<ApiErrorResponse>(
message: "The user may have been changed in another process.",
statusCode: 401,
response: "",
headers: new Dictionary<string, IEnumerable<string>>(),
result: new ApiErrorResponse(
documentation_url: "",
error_code: ApiErrorCode.Unknown,
message: "The user may have been changed in another process."),
innerException: null);
}
}
}

activity?.SetTag("force", force);
activity?.SetTag("is_expired", IsExpired);

if (force || IsExpired)
{
response = await clients.Account.RefreshAsync(new RefeshTokenRequest(RefreshToken, Token))
_response = await clients.Account.RefreshAsync(new RefeshTokenRequest(RefreshToken, Token))
.ConfigureAwait(false);
activity?.AddEvent(new("Refreshed"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public AccountSettingsPageViewModel(BeutlApiApplication clients)
.DisposeWith(_disposables);

SignOut = new ReactiveCommand(SignedIn);
SignOut.Subscribe(_clients.SignOut).DisposeWith(_disposables);
SignOut.Subscribe(() => _clients.SignOut()).DisposeWith(_disposables);

OpenAccountSettings = new();
OpenAccountSettings.Subscribe(BeutlApiApplication.OpenAccountSettings);
Expand Down

0 comments on commit 552b02d

Please sign in to comment.