refactoring the login
This commit is contained in:
parent
66c48329cc
commit
2fd799c09f
13 changed files with 129 additions and 196 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -23,6 +23,7 @@ package-lock.json
|
|||
data/
|
||||
appsettings.*.json
|
||||
appsettings-*.*.json
|
||||
*-settings.json
|
||||
|
||||
generated/
|
||||
*.tmp
|
||||
|
|
|
|||
84
.vscode/launch.json
vendored
84
.vscode/launch.json
vendored
|
|
@ -5,97 +5,17 @@
|
|||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET API",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/Api/bin/Debug/net10.0/Api.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/src/Api",
|
||||
"serverReadyAction": {
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"envFile": "${workspaceFolder}/src/Api/.env"
|
||||
},
|
||||
{
|
||||
"name": "C#: API Debug",
|
||||
"name": "API",
|
||||
"type": "dotnet",
|
||||
"request": "launch",
|
||||
"projectPath": "${workspaceFolder}/src/Api/Api.csproj"
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/src/Yavsc.Blogs/bin/Debug/net10.0/Yavsc.Blogs.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/src/Yavsc.Blogs/",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "C#: Yavsc.Org Debug",
|
||||
"name": "Yavsc.Org",
|
||||
"type": "dotnet",
|
||||
"request": "launch",
|
||||
"projectPath": "${workspaceFolder}/src/Yavsc.Org/Yavsc.Org.csproj"
|
||||
},
|
||||
{
|
||||
"name": "Web",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build-web",
|
||||
"program": "${workspaceFolder}/src/Yavsc.Web/bin/Debug/net10.0/Yavsc.Web.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/src/Yavsc.Web",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "cli",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/src/cli/bin/Debug/net10.0/cli.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "Web",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/src/Yavsc.Web/bin/Debug/net10.0/Yavsc.Web.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/src/Yavsc.Web",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/src/Yavsc.Web/Views"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "PostIt",
|
||||
"type": "dotnet",
|
||||
|
|
|
|||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -13,6 +13,7 @@
|
|||
"envsubst",
|
||||
"Newtonsoft",
|
||||
"Npgsql",
|
||||
"postit",
|
||||
"pschneider",
|
||||
"SLNDIR",
|
||||
"validable",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<ProjectReference Include="../../Yavsc.Abstract/Yavsc.Abstract.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="settings.json">
|
||||
<Content Include="postit-settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace PostIt;
|
|||
|
||||
public partial class Settings : ObservableObject
|
||||
{
|
||||
const string SettingsFileName = "settings.json";
|
||||
const string SettingsFileName = "postit-settings.json";
|
||||
IStorageFolder? folder = null;
|
||||
|
||||
[ObservableProperty]
|
||||
|
|
@ -42,8 +42,13 @@ public partial class Settings : ObservableObject
|
|||
|
||||
internal async Task Load()
|
||||
{
|
||||
String configPath =
|
||||
Path.Combine(AppContext.BaseDirectory, SettingsFileName);
|
||||
string configDir = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"PostIt"
|
||||
);
|
||||
Directory.CreateDirectory(configDir);
|
||||
|
||||
string configPath = Path.Combine(configDir, SettingsFileName);
|
||||
|
||||
FileInfo configFileInfo = new FileInfo(configPath);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public class ViewLocator : IDataTemplate
|
|||
{
|
||||
MainPageViewModel => _services.GetRequiredService<MainPage>(),
|
||||
SettingsPageViewModel => _services.GetRequiredService<SettingsPage>(),
|
||||
LoginPageViewModel => _services.GetRequiredService<LoginPage>(),
|
||||
_ => new TextBlock { Text = $"No view for {data.GetType().Name}" }
|
||||
};
|
||||
}
|
||||
|
|
|
|||
67
src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs
Normal file
67
src/PostIt/PostIt/ViewModels/LoginPageViewModel.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using CommunityToolkit.Mvvm.Input;
|
||||
using IdentityModel.OidcClient;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PostIt.ViewModels;
|
||||
|
||||
public partial class LoginPageViewModel : ViewModelBase
|
||||
{
|
||||
public string UserEmail { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
private string _AccessToken;
|
||||
public string AccessToken { get => _AccessToken; private set => this.SetProperty(ref _AccessToken, value); }
|
||||
|
||||
public bool RememberMe { get; set; }
|
||||
public override bool CanNavigateNext { get => false; protected set => throw new System.NotImplementedException(); }
|
||||
public override bool CanNavigatePrevious { get => true; protected set => throw new System.NotImplementedException(); }
|
||||
|
||||
public Settings Settings { get; }
|
||||
|
||||
private string _StatusMessage;
|
||||
public string StatusMessage { get => _StatusMessage; private set => this.SetProperty(ref _StatusMessage, value); }
|
||||
|
||||
private bool _IsBusy;
|
||||
public bool IsBusy { get=> _IsBusy; private set=> this.SetProperty(ref _IsBusy, value); }
|
||||
|
||||
public LoginPageViewModel()
|
||||
{
|
||||
Settings = new Settings();
|
||||
StatusMessage = "Ready";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task LoginAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Settings.Load().Wait();
|
||||
|
||||
var client = new OidcClient(Settings.GetOidcClientOptions());
|
||||
var loginResult = await client.LoginAsync(new LoginRequest());
|
||||
|
||||
if (loginResult.IsError)
|
||||
{
|
||||
StatusMessage = loginResult.Error;
|
||||
return;
|
||||
}
|
||||
|
||||
StatusMessage = "Interactive token acquired.";
|
||||
|
||||
this.IsBusy = false;
|
||||
AccessToken = loginResult.AccessToken;
|
||||
/* TODO save or not user log and password
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Settings.Save().Wait();
|
||||
});*/
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.IsBusy = false;
|
||||
StatusMessage = "Error: "+ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -107,44 +107,6 @@ public partial class MainPageViewModel : ViewModelBase
|
|||
ApplyFilter();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
internal async Task Login()
|
||||
{
|
||||
await ExecuteAsync(async () =>
|
||||
{
|
||||
// Try interactive OIDC login first
|
||||
try
|
||||
{
|
||||
|
||||
var loginWin = new PostIt.Views.LoginPage();
|
||||
var result = await loginWin.StartLoginAsync(Settings.GetOidcClientOptions());
|
||||
|
||||
if (result is not null && !result.IsError && !string.IsNullOrWhiteSpace(result.AccessToken))
|
||||
{
|
||||
BearerToken = result.AccessToken;
|
||||
StatusMessage = "Interactive token acquired.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore and fallback to client credentials
|
||||
}
|
||||
|
||||
// Fallback to client credentials if interactive fails
|
||||
var tokenResponse = await RequestClientCredentialsTokenAsync();
|
||||
if (string.IsNullOrWhiteSpace(tokenResponse.AccessToken))
|
||||
{
|
||||
throw new InvalidOperationException("Failed to acquire a token using client credentials.");
|
||||
}
|
||||
|
||||
BearerToken = tokenResponse.AccessToken;
|
||||
StatusMessage = string.IsNullOrWhiteSpace(tokenResponse.Error)
|
||||
? "Bearer token acquired (client credentials)."
|
||||
: $"Token acquired with warning: {tokenResponse.ErrorDescription}";
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
internal async Task Save()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,37 +1,39 @@
|
|||
<ContentPage xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="PostIt.Views.LoginPage"
|
||||
Header="Login">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:PostIt.ViewModels"
|
||||
x:Class="PostIt.Views.LoginPage"
|
||||
x:DataType="vm:LoginPageViewModel"
|
||||
Header="Login">
|
||||
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="20">
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="20">
|
||||
|
||||
<TextBlock Text="Sign In"
|
||||
FontSize="24"
|
||||
HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="Sign In"
|
||||
FontSize="24"
|
||||
HorizontalAlignment="Center"/>
|
||||
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Email"/>
|
||||
<TextBox Name="EmailBox"
|
||||
PlaceholderText="Enter your email"/>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Email"/>
|
||||
<TextBox Name="EmailBox"
|
||||
PlaceholderText="Enter your email"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Password"/>
|
||||
<TextBox Name="PasswordBox"
|
||||
PlaceholderText="Enter your password"
|
||||
PasswordChar="•"/>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Password"/>
|
||||
<TextBox Name="PasswordBox"
|
||||
PlaceholderText="Enter your password"
|
||||
PasswordChar="•"/>
|
||||
</StackPanel>
|
||||
|
||||
<Button Content="Login"
|
||||
Click="OnLoginClick"/>
|
||||
<Button Content="Login"
|
||||
Click="OnLoginClickAsync"/>
|
||||
|
||||
<Button Content="Cancel"
|
||||
Click="OnCancelClick"/>
|
||||
<Button Content="Cancel"
|
||||
Click="OnCancelClick"/>
|
||||
|
||||
<TextBlock Name="StatusText" Text="Starting authentication..." />
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
<TextBlock Name="StatusText" Text="Starting authentication..." />
|
||||
<ProgressBar IsIndeterminate="{Binding IsBusy}" />
|
||||
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ContentPage>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ namespace PostIt.Views;
|
|||
public partial class LoginPage : ContentPage
|
||||
{
|
||||
private TaskCompletionSource<LoginResult?> _tcs = new();
|
||||
private LoginResult loginResult;
|
||||
|
||||
|
||||
public Settings Settings { get; }
|
||||
|
||||
|
|
@ -19,25 +21,22 @@ public partial class LoginPage : ContentPage
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
public async Task<LoginResult?> StartLoginAsync(OidcClientOptions options)
|
||||
private async void OnLoginClickAsync(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
// This is where you specify your actual login auth logic
|
||||
try
|
||||
{
|
||||
Settings.Load().Wait();
|
||||
|
||||
var client = new OidcClient(options);
|
||||
var client = new OidcClient(Settings.GetOidcClientOptions());
|
||||
var loginResult = await client.LoginAsync(new LoginRequest());
|
||||
return loginResult;
|
||||
this.loginResult = loginResult;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
this.loginResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnLoginClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
// This is where you specify your actual login auth logic
|
||||
if (Navigation is not null)
|
||||
await Navigation.PopModalAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
xmlns:AvaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
||||
mc:Ignorable="d"
|
||||
x:Class="PostIt.Views.MainPage"
|
||||
x:DataType="vm:MainPageViewModel" HorizontalAlignment="Center" VerticalAlignment="Center" >
|
||||
x:DataType="vm:MainPageViewModel"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" >
|
||||
<Design.DataContext>
|
||||
<vm:MainPageViewModel />
|
||||
</Design.DataContext>
|
||||
|
|
@ -17,7 +19,6 @@
|
|||
<TextBlock Text="PostIt Blog API Interface" FontSize="20" FontWeight="Bold" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" >
|
||||
<Button Command="{Binding Login}" Content="Acquire token" />
|
||||
<Button Command="{Binding LoadPosts}" Content="Load posts" />
|
||||
<Button Command="{Binding Search}" Content="Filter" />
|
||||
<Button Command="{Binding New}" Content="New post" />
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using PostIt.ViewModels;
|
||||
|
||||
public partial class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
// Stocke le ViewModel de la page actuellement affichée
|
||||
[ObservableProperty]
|
||||
private object? _currentPage;
|
||||
|
||||
// Instances des pages pour éviter de les recréer à chaque fois (Optionnel)
|
||||
private readonly HomePageViewModel _homePage = new();
|
||||
private readonly SettingsPageViewModel _settingsPage = new();
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
// Définir la page de démarrage par défaut
|
||||
CurrentPage = _homePage;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToHome() => CurrentPage = _homePage;
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToSettings() => CurrentPage = _settingsPage;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue