feat: Implement OAuth2 authentication with Microsoft, Google, and PocketId
- Added JWT configuration to appsettings.json for secure token handling. - Updated config.json to include OAuth provider details for Microsoft, Google, and PocketId. - Added Microsoft icon SVG for UI representation. - Refactored app.config.ts to use a custom AuthInterceptor for managing access tokens. - Enhanced auth route guard to handle asynchronous authentication checks. - Created new auth models for structured request and response handling. - Developed a callback component to manage user login states and transitions. - Updated side-login component to support multiple OAuth providers with loading states. - Implemented authentication service methods for handling OAuth login flows and token management. - Added error handling and user feedback for authentication processes.
This commit is contained in:
@@ -14,12 +14,40 @@ namespace Api.Models
|
||||
{
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
||||
public DbSet<Product> Products { get; set; }
|
||||
|
||||
public DbSet<WebMessage> WebMessages { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<UserOAuthProvider> UserOAuthProviders { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// Configure User entity
|
||||
modelBuilder.Entity<User>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => e.Email).IsUnique();
|
||||
entity.Property(e => e.Email).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.FirstName).HasMaxLength(255);
|
||||
entity.Property(e => e.LastName).HasMaxLength(255);
|
||||
entity.Property(e => e.ProfilePictureUrl).HasMaxLength(500);
|
||||
});
|
||||
|
||||
// Configure UserOAuthProvider entity
|
||||
modelBuilder.Entity<UserOAuthProvider>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.HasIndex(e => new { e.Provider, e.ProviderId }).IsUnique();
|
||||
entity.Property(e => e.ProviderId).IsRequired().HasMaxLength(255);
|
||||
entity.Property(e => e.ProviderEmail).HasMaxLength(255);
|
||||
entity.Property(e => e.ProviderName).HasMaxLength(255);
|
||||
|
||||
entity.HasOne(e => e.User)
|
||||
.WithMany(u => u.OAuthProviders)
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// Seed data
|
||||
modelBuilder.Entity<Product>().HasData(
|
||||
new Product { Id = 1, Name = "Sample Product", Price = 9.99M }
|
||||
|
||||
59
Api/Models/DTOs/AuthenticationDtos.cs
Normal file
59
Api/Models/DTOs/AuthenticationDtos.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Api.Models.DTOs
|
||||
{
|
||||
public class AuthenticateRequest
|
||||
{
|
||||
[Required]
|
||||
public string IdToken { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public string Provider { get; set; } = string.Empty; // "Microsoft", "Google", "PocketId"
|
||||
|
||||
/// <summary>
|
||||
/// Optional access token for API calls (e.g., Microsoft Graph)
|
||||
/// </summary>
|
||||
public string? AccessToken { get; set; }
|
||||
}
|
||||
|
||||
public class AuthenticateResponse
|
||||
{
|
||||
public string AccessToken { get; set; } = string.Empty;
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public UserProfile User { get; set; } = null!;
|
||||
public bool IsNewUser { get; set; }
|
||||
}
|
||||
|
||||
public class UserProfile
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public string? ProfilePictureUrl { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
public List<string> Providers { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
public class JwtSettings
|
||||
{
|
||||
public string SecretKey { get; set; } = string.Empty;
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
public string Audience { get; set; } = string.Empty;
|
||||
public int ExpirationMinutes { get; set; } = 60;
|
||||
}
|
||||
|
||||
public class OAuthProviderSettings
|
||||
{
|
||||
public Dictionary<string, ProviderConfig> Providers { get; set; } = new Dictionary<string, ProviderConfig>();
|
||||
}
|
||||
|
||||
public class ProviderConfig
|
||||
{
|
||||
public string Authority { get; set; } = string.Empty;
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
public string? ClientSecret { get; set; }
|
||||
public List<string> ValidAudiences { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
72
Api/Models/User.cs
Normal file
72
Api/Models/User.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Api.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(255)]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
[StringLength(255)]
|
||||
public string? FirstName { get; set; }
|
||||
|
||||
[StringLength(255)]
|
||||
public string? LastName { get; set; }
|
||||
|
||||
[StringLength(500)]
|
||||
public string? ProfilePictureUrl { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? LastLoginAt { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
// Navigation property for OAuth providers
|
||||
public virtual ICollection<UserOAuthProvider> OAuthProviders { get; set; } = new List<UserOAuthProvider>();
|
||||
}
|
||||
|
||||
public class UserOAuthProvider
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public int UserId { get; set; }
|
||||
|
||||
[Required]
|
||||
public OAuthProvider Provider { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(255)]
|
||||
public string ProviderId { get; set; } = string.Empty;
|
||||
|
||||
[StringLength(255)]
|
||||
public string? ProviderEmail { get; set; }
|
||||
|
||||
[StringLength(255)]
|
||||
public string? ProviderName { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public DateTime? LastUsedAt { get; set; }
|
||||
|
||||
// Navigation property
|
||||
[ForeignKey("UserId")]
|
||||
public virtual User User { get; set; } = null!;
|
||||
}
|
||||
|
||||
public enum OAuthProvider
|
||||
{
|
||||
Microsoft = 1,
|
||||
Google = 2,
|
||||
PocketId = 3
|
||||
// Add more providers as needed
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user