diff --git a/Bimix.UI.Shared/Components/AuthGuard.razor b/Bimix.UI.Shared/Components/AuthGuard.razor
index 225a102..06d437f 100644
--- a/Bimix.UI.Shared/Components/AuthGuard.razor
+++ b/Bimix.UI.Shared/Components/AuthGuard.razor
@@ -25,7 +25,6 @@ else if (_isAuthenticated)
{
Console.WriteLine("AuthGuard: Checking authentication...");
- // ZAWSZE sprawdź localStorage przy inicjalizacji
_isAuthenticated = await AuthService.CheckAuthenticationAsync();
_isLoading = false;
diff --git a/Bimix.UI.Shared/Components/LoginCard.razor b/Bimix.UI.Shared/Components/LoginCard.razor
index db146c1..6ce8c58 100644
--- a/Bimix.UI.Shared/Components/LoginCard.razor
+++ b/Bimix.UI.Shared/Components/LoginCard.razor
@@ -1,3 +1,4 @@
+
@using Microsoft.Extensions.Configuration
@using Bimix.UI.Shared.Services
@inject IJSRuntime JS
@@ -41,10 +42,37 @@
private bool _isLoading = false;
private string _errorMessage = string.Empty;
private static LoginCard? _instance;
+ private bool _isInitialized = false;
- protected override void OnInitialized()
+ protected override async Task OnAfterRenderAsync(bool firstRender)
{
- _instance = this;
+ if (firstRender)
+ {
+ _instance = this;
+ await InitializeGoogleSignIn();
+ }
+ }
+
+ private async Task InitializeGoogleSignIn()
+ {
+ try
+ {
+ if (_isInitialized) return;
+
+ var clientId = Configuration["GoogleAuth:ClientId"];
+ if (string.IsNullOrEmpty(clientId))
+ {
+ throw new Exception("Google ClientId is not configured.");
+ }
+
+ await JS.InvokeVoidAsync("initGoogleSignIn", clientId);
+ _isInitialized = true;
+ }
+ catch (Exception ex)
+ {
+ _errorMessage = "Błąd inicjalizacji Google Sign-In.";
+ Console.Error.WriteLine($"Google Sign-In initialization error: {ex.Message}");
+ }
}
private async Task HandleGoogleSignIn()
@@ -55,55 +83,53 @@
_errorMessage = string.Empty;
StateHasChanged();
- var clientId = Configuration["GoogleAuth:ClientId"];
-
- if (string.IsNullOrEmpty(clientId))
- {
- throw new Exception("Google ClientId is not configured.");
- }
-
-
- await JS.InvokeVoidAsync("initGoogleSignIn", clientId);
+ await JS.InvokeVoidAsync("requestGoogleSignIn");
}
catch (Exception ex)
{
- _errorMessage = "Błąd podczas logownia. Spróbuj ponownie";
+ _errorMessage = "Błąd podczas logowania. Spróbuj ponownie";
_isLoading = false;
StateHasChanged();
}
}
[JSInvokable]
- public static async Task OnGoogleSignInSuccess(string idToken)
+ public static async Task OnGoogleSignInSuccess(string accessToken, string fullName, string email, string avatarUrl)
{
- Console.WriteLine($"Google ID Token: {idToken}");
+ Console.WriteLine($"Google Sign-In Success: {email}");
if (_instance != null)
{
- await _instance.AuthService.SetAuthenticationAsync(idToken);
+ var userInfo = new UserInfo
+ {
+ FullName = fullName,
+ Email = email,
+ AvatarUrl = avatarUrl
+ };
+
+ await _instance.AuthService.SetAuthenticationAsync(accessToken, userInfo);
_instance._isLoading = false;
_instance._errorMessage = string.Empty;
- _instance.NavigationManager.NavigateTo("/dashboard", replace:true);
+ _instance.NavigationManager.NavigateTo("/dashboard", replace: true);
await _instance.InvokeAsync(() => _instance.StateHasChanged());
}
}
[JSInvokable]
- public static async Task OnGoggleSignInError(string error)
+ public static async Task OnGoogleSignInError(string error)
{
Console.WriteLine($"Google SignIn Error: {error}");
if (_instance != null)
{
_instance._isLoading = false;
- _instance._errorMessage = "Błąd logowanie Google. Spróbuj ponownie";
+ _instance._errorMessage = "Błąd logowania Google. Spróbuj ponownie";
await _instance.InvokeAsync(() => _instance.StateHasChanged());
}
}
-
}
\ No newline at end of file
diff --git a/Bimix.UI.Shared/Services/AuthService.cs b/Bimix.UI.Shared/Services/AuthService.cs
index cb99e6b..0099663 100644
--- a/Bimix.UI.Shared/Services/AuthService.cs
+++ b/Bimix.UI.Shared/Services/AuthService.cs
@@ -1,11 +1,19 @@
+using System.Text.Json;
using Microsoft.JSInterop;
namespace Bimix.UI.Shared.Services;
+public class UserInfo
+{
+ public string FullName { get; set; } = string.Empty;
+ public string Email { get; set; } = string.Empty;
+ public string AvatarUrl { get; set; } = string.Empty;
+}
public class AuthService
{
private readonly IJSRuntime _jsRuntime;
- private bool? _isAuthenticated;
+ private bool? _isAuthenticated;
+ private UserInfo? _userInfo = null;
public event Action? AuthenticationStateChanged;
@@ -15,13 +23,21 @@ public class AuthService
}
public bool IsAuthenticated => _isAuthenticated ?? false;
+ public UserInfo? CurrentUser => _userInfo;
public async Task CheckAuthenticationAsync()
{
try
{
var token = await _jsRuntime.InvokeAsync("localStorage.getItem", "google_token");
+ var userInfoJson = await _jsRuntime.InvokeAsync("localStorage.getItem", "user_info");
+
_isAuthenticated = !string.IsNullOrEmpty(token);
+
+ if (_isAuthenticated.Value && !string.IsNullOrEmpty(userInfoJson))
+ {
+ _userInfo = JsonSerializer.Deserialize(userInfoJson);
+ }
Console.WriteLine($"AuthService.CheckAuthentication: token={(!string.IsNullOrEmpty(token) ? "EXISTS" : "NULL")}, isAuth={_isAuthenticated}");
@@ -31,17 +47,26 @@ public class AuthService
{
Console.WriteLine($"AuthService.CheckAuthentication ERROR: {ex.Message}");
_isAuthenticated = false;
+ _userInfo = null;
return false;
}
}
- public async Task SetAuthenticationAsync(string token)
+ public async Task SetAuthenticationAsync(string token, UserInfo? userInfo = null)
{
try
{
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "google_token", token);
+
+ if (userInfo != null)
+ {
+ _userInfo = userInfo;
+ var userInfoJson = JsonSerializer.Serialize(userInfo);
+ await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "user_info", userInfoJson);
+ }
+
_isAuthenticated = true;
- Console.WriteLine($"AuthService.SetAuthentication: token saved, isAuth={_isAuthenticated}");
+ Console.WriteLine($"AuthService.SetAuthentication: token saved, user={_userInfo?.Email}");
AuthenticationStateChanged?.Invoke(true);
}
catch (Exception ex)
@@ -55,8 +80,10 @@ public class AuthService
try
{
await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "google_token");
+ await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "user_info");
_isAuthenticated = false;
- Console.WriteLine($"AuthService.ClearAuthentication: token removed");
+ _userInfo = null;
+ Console.WriteLine($"AuthService.ClearAuthentication: token and user ingfo removed");
AuthenticationStateChanged?.Invoke(false);
}
catch (Exception ex)
diff --git a/Bimix.UI.Shared/wwwroot/js/auth.js b/Bimix.UI.Shared/wwwroot/js/auth.js
index 3ed0254..5fb6860 100644
--- a/Bimix.UI.Shared/wwwroot/js/auth.js
+++ b/Bimix.UI.Shared/wwwroot/js/auth.js
@@ -1,35 +1,119 @@
-window.initGoogleSignIn = async function(clientId) {
- try {
- if (!clientId) {
- throw new Error('ClientId is required');
+let googleClient = null;
+let isSigningIn = false;
+
+function waitForGoogleApi() {
+ return new Promise((resolve, reject) => {
+ if (window.google?.accounts?.oauth2) {
+ resolve(window.google);
+ return;
}
- // Inicjalizacja Google Sign-In z dynamicznym ClientId
- google.accounts.id.initialize({
- client_id: clientId,
- callback: handleGoogleResponse,
- auto_select: false,
- cancel_on_tap_outside: true
- });
+ const maxAttempts = 20;
+ let attempts = 0;
+
+ const checkGoogle = setInterval(() => {
+ attempts++;
+ if (window.google?.accounts?.oauth2) {
+ clearInterval(checkGoogle);
+ resolve(window.google);
+ } else if (attempts >= maxAttempts) {
+ clearInterval(checkGoogle);
+ reject(new Error('Google OAuth2 API failed to load within the timeout period'));
+ }
+ }, 100);
+ });
+}
- // Wyświetl popup logowania
- google.accounts.id.prompt((notification) => {
- if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
- console.log('Google Sign-In popup not displayed');
+async function handleAuthError(error, context = '') {
+ const errorMessage = error?.message || error?.type || error?.toString() || 'Unknown error';
+ const fullError = `${context}: ${errorMessage}`;
+ console.error('Google Auth Error:', { context, error, fullError });
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError', fullError);
+}
+
+async function fetchUserInfo(accessToken) {
+ const response = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', {
+ headers: { 'Authorization': `Bearer ${accessToken}` }
+ });
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.error('Failed to fetch user info:', errorText);
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ `Failed to fetch user info: HTTP ${response.status}`);
+ return null;
+ }
+
+ return await response.json();
+}
+
+window.initGoogleSignIn = async function(clientId) {
+ if (googleClient) {
+ return googleClient;
+ }
+
+ try {
+ const google = await waitForGoogleApi();
+
+ googleClient = google.accounts.oauth2.initTokenClient({
+ client_id: clientId,
+ scope: 'email profile',
+ callback: async (tokenResponse) => {
+ try {
+ if (tokenResponse.error) {
+ console.error('Token response error:', tokenResponse.error);
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ tokenResponse.error);
+ return;
+ }
+
+ const userInfo = await fetchUserInfo(tokenResponse.access_token);
+ if (!userInfo) return;
+
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInSuccess',
+ tokenResponse.access_token,
+ userInfo.name || '',
+ userInfo.email || '',
+ userInfo.picture || ''
+ );
+ } catch (error) {
+ console.error('Callback error:', error);
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ error.message || 'Unknown callback error');
+ } finally {
+ isSigningIn = false;
+ }
+ },
+ error_callback: async (error) => {
+ console.error('OAuth flow error:', error);
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ error.type || 'OAuth flow error');
+ isSigningIn = false;
}
});
+ return googleClient;
} catch (error) {
- console.error('Google Sign-In initialization error:', error);
- DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError', error.message);
+ console.error('Initiaxcrun xctrace list deviceslization error:', error);
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ error.message || 'Failed to initialize Google Sign-In');
+ isSigningIn = false;
}
};
-function handleGoogleResponse(response) {
- if (response.credential) {
- // Token otrzymany - wyślij do Blazor
- DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInSuccess', response.credential);
- } else {
- DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError', 'No credential received');
+window.requestGoogleSignIn = async function() {
+ if (isSigningIn) {
+ console.log('Sign-in already in progress');
+ return;
}
-}
+
+ if (!googleClient) {
+ console.error('Google Sign-In not initialized');
+ await DotNet.invokeMethodAsync('Bimix.UI.Shared', 'OnGoogleSignInError',
+ 'Google Sign-In not initialized. Call initGoogleSignIn first.');
+ return;
+ }
+
+ isSigningIn = true;
+ googleClient.requestAccessToken();
+};
\ No newline at end of file
diff --git a/Bimix.UI.Web/Components/App.razor b/Bimix.UI.Web/Components/App.razor
index c0ae221..2b804f5 100644
--- a/Bimix.UI.Web/Components/App.razor
+++ b/Bimix.UI.Web/Components/App.razor
@@ -9,7 +9,7 @@
-
+
@@ -30,9 +30,7 @@
-
-