Skip to content

Commit

Permalink
improved session cookie handling, abstracting more away to specific h…
Browse files Browse the repository at this point in the history
…andlers
  • Loading branch information
pingu2k4 committed Jul 14, 2024
1 parent 33b5fe1 commit f6f54eb
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 60 deletions.
38 changes: 1 addition & 37 deletions src/PinguApps.Appwrite.Client/Clients/AccountClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using PinguApps.Appwrite.Client.Clients;
Expand All @@ -11,25 +8,20 @@
using PinguApps.Appwrite.Shared;
using PinguApps.Appwrite.Shared.Requests;
using PinguApps.Appwrite.Shared.Responses;
using Refit;

namespace PinguApps.Appwrite.Client;

public class AccountClient : IAccountClient, ISessionAware
{
private readonly IAccountApi _accountApi;
private readonly bool _saveSession;

public AccountClient(IServiceProvider services, bool saveSession)
public AccountClient(IServiceProvider services)
{
_accountApi = services.GetRequiredService<IAccountApi>();
_saveSession = saveSession;
}

string? ISessionAware.Session { get; set; }

public event EventHandler<string?>? SessionChanged;

ISessionAware? _sessionAware;
public string? Session => GetSession();
private string? GetSession()
Expand All @@ -42,32 +34,6 @@ public AccountClient(IServiceProvider services, bool saveSession)
return _sessionAware.Session;
}

private void SaveSession<T>(IApiResponse<T> response)
{
if (_saveSession && response.IsSuccessStatusCode && SessionChanged is not null)
{
if (response.Headers.TryGetValues("Set-Cookie", out var values))
{
var sessionCookie = values.FirstOrDefault(x => x.StartsWith("a_session", StringComparison.OrdinalIgnoreCase) && !x.Contains("legacy", StringComparison.OrdinalIgnoreCase));

if (sessionCookie is null)
return;

var base64 = sessionCookie.Split('=')[1].Split(';')[0];

var decodedBytes = Convert.FromBase64String(base64);
var decoded = Encoding.UTF8.GetString(decodedBytes);

var sessionData = JsonSerializer.Deserialize<CookieSessionData>(decoded);

if (sessionData is null)
return;

SessionChanged.Invoke(this, sessionData.Secret);
}
}
}

/// <inheritdoc/>
public async Task<AppwriteResult<User>> Get()
{
Expand Down Expand Up @@ -226,8 +192,6 @@ public async Task<AppwriteResult<Session>> CreateSession(CreateSessionRequest re

var result = await _accountApi.CreateSession(request);

SaveSession(result);

return result.GetApiResponse();
}
catch (Exception e)
Expand Down
14 changes: 1 addition & 13 deletions src/PinguApps.Appwrite.Client/Clients/AppwriteClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using PinguApps.Appwrite.Client.Clients;
using PinguApps.Appwrite.Client.Clients;

namespace PinguApps.Appwrite.Client;
public class AppwriteClient : IAppwriteClient, ISessionAware
Expand All @@ -10,16 +9,10 @@ public class AppwriteClient : IAppwriteClient, ISessionAware
public AppwriteClient(IAccountClient accountClient)
{
Account = accountClient;
if (Account is ISessionAware sessionAwareAccount)
{
sessionAwareAccount.SessionChanged += OnSessionChanged;
}
}

string? ISessionAware.Session { get; set; }

public event EventHandler<string?>? SessionChanged;

ISessionAware? _sessionAware;
/// <inheritdoc/>
public string? Session => GetSession();
Expand All @@ -39,9 +32,4 @@ public void SetSession(string? session)
(this as ISessionAware).UpdateSession(session);
(Account as ISessionAware)!.UpdateSession(session);
}

private void OnSessionChanged(object? sender, string? newSession)
{
SetSession(newSession);
}
}
6 changes: 1 addition & 5 deletions src/PinguApps.Appwrite.Client/Clients/ISessionAware.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System;

namespace PinguApps.Appwrite.Client.Clients;
namespace PinguApps.Appwrite.Client.Clients;

internal interface ISessionAware
{
public string? Session { get; protected set; }

public void UpdateSession(string? session) => Session = session;

event EventHandler<string?>? SessionChanged;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using PinguApps.Appwrite.Client.Internals;

namespace PinguApps.Appwrite.Client.Handlers;
internal class ClientCookieSessionHandler : DelegatingHandler
{
private readonly Lazy<IAppwriteClient> _appwriteClient;

public ClientCookieSessionHandler(Lazy<IAppwriteClient> appwriteClient)
{
_appwriteClient = appwriteClient;
}

private IAppwriteClient AppwriteClient => _appwriteClient.Value;

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var result = await base.SendAsync(request, cancellationToken);

SaveSession(result);

return result;
}

private void SaveSession(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
if (response.Headers.TryGetValues("Set-Cookie", out var values))
{
var sessionCookie = values.FirstOrDefault(x => x.StartsWith("a_session", StringComparison.OrdinalIgnoreCase) && !x.Contains("legacy", StringComparison.OrdinalIgnoreCase));

if (sessionCookie is null)
return;

var base64 = sessionCookie.Split('=')[1].Split(';')[0];

var decodedBytes = Convert.FromBase64String(base64);
var decoded = Encoding.UTF8.GetString(decodedBytes);

var sessionData = JsonSerializer.Deserialize<CookieSessionData>(decoded);

if (sessionData is null)
return;

AppwriteClient.SetSession(sessionData.Secret);
}
}
}
}
11 changes: 7 additions & 4 deletions src/PinguApps.Appwrite.Client/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ public static class ServiceCollectionExtensions
/// <returns>The service collection, enabling chaining</returns>
public static IServiceCollection AddAppwriteClient(this IServiceCollection services, string projectId, string endpoint = "https://cloud.appwrite.io/v1", RefitSettings? refitSettings = null)
{
services.AddSingleton(sp => new HeaderHandler(projectId));
services.AddSingleton(x => new HeaderHandler(projectId));
services.AddSingleton<ClientCookieSessionHandler>();

services.AddRefitClient<IAccountApi>(refitSettings)
.ConfigureHttpClient(x => x.BaseAddress = new Uri(endpoint))
.AddHttpMessageHandler<HeaderHandler>();
.AddHttpMessageHandler<HeaderHandler>()
.AddHttpMessageHandler<ClientCookieSessionHandler>();

services.AddSingleton<IAccountClient>(x => new AccountClient(x, true));
services.AddSingleton<IAccountClient, AccountClient>();
services.AddSingleton<IAppwriteClient, AppwriteClient>();
services.AddSingleton(x => new Lazy<IAppwriteClient>(() => x.GetRequiredService<IAppwriteClient>()));

return services;
}
Expand All @@ -54,7 +57,7 @@ public static IServiceCollection AddAppwriteClientForServer(this IServiceCollect
UseCookies = false
});

services.AddSingleton<IAccountClient>(x => new AccountClient(x, false));
services.AddSingleton<IAccountClient, AccountClient>();
services.AddSingleton<IAppwriteClient, AppwriteClient>();

return services;
Expand Down
2 changes: 1 addition & 1 deletion src/PinguApps.Appwrite.Playground/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task Run(string[] args)
var request = new CreateSessionRequest
{
UserId = "664aac1a00113f82e620",
Secret = "814123"
Secret = "834938"
};

Console.WriteLine($"Session: {_client.Session}");
Expand Down

0 comments on commit f6f54eb

Please sign in to comment.