feat(auth): add OAuth client management API
This commit is contained in:
parent
d3810f5d43
commit
5cdcba7e57
176
docs/task-10-add-oauth-client-management.md
Normal file
176
docs/task-10-add-oauth-client-management.md
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Task 10: Add OAuth Client Management
|
||||||
|
|
||||||
|
## Task Description
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `src/Fengling.AuthService/Models/OAuthApplication.cs`
|
||||||
|
- Create: `src/Fengling.AuthService/Controllers/OAuthClientsController.cs`
|
||||||
|
- Modify: `src/Fengling.AuthService/Data/ApplicationDbContext.cs`
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Step 1: Create OAuthApplication model
|
||||||
|
|
||||||
|
Create: `src/Fengling.AuthService/Models/OAuthApplication.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class OAuthApplication
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string ClientId { get; set; } = string.Empty;
|
||||||
|
public string? ClientSecret { get; set; }
|
||||||
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
|
public string[] RedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] PostLogoutRedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] Scopes { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] GrantTypes { get; set; } = Array.Empty<string>();
|
||||||
|
public string ClientType { get; set; } = "public";
|
||||||
|
public string ConsentType { get; set; } = "implicit";
|
||||||
|
public string Status { get; set; } = "active";
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Update ApplicationDbContext
|
||||||
|
|
||||||
|
Edit: `src/Fengling.AuthService/Data/ApplicationDbContext.cs`
|
||||||
|
|
||||||
|
Add to context:
|
||||||
|
```csharp
|
||||||
|
public DbSet<OAuthApplication> OAuthApplications { get; set; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Add to OnModelCreating:
|
||||||
|
```csharp
|
||||||
|
builder.Entity<OAuthApplication>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.ClientId).IsUnique();
|
||||||
|
entity.Property(e => e.ClientId).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientSecret).HasMaxLength(200);
|
||||||
|
entity.Property(e => e.DisplayName).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.ConsentType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Add migration
|
||||||
|
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
dotnet ef migrations add AddOAuthApplications
|
||||||
|
dotnet ef database update
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Create OAuthClientsController
|
||||||
|
|
||||||
|
Create: `src/Fengling.AuthService/Controllers/OAuthClientsController.cs`
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Fengling.AuthService.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class OAuthClientsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
private readonly ILogger<OAuthClientsController> _logger;
|
||||||
|
|
||||||
|
public OAuthClientsController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
ILogger<OAuthClientsController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IEnumerable<OAuthApplication>>> GetClients()
|
||||||
|
{
|
||||||
|
return await _context.OAuthApplications.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<OAuthApplication>> GetClient(long id)
|
||||||
|
{
|
||||||
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<OAuthApplication>> CreateClient(OAuthApplication application)
|
||||||
|
{
|
||||||
|
_context.OAuthApplications.Add(application);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return CreatedAtAction(nameof(GetClient), new { id = application.Id }, application);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> UpdateClient(long id, OAuthApplication application)
|
||||||
|
{
|
||||||
|
if (id != application.Id)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Entry(application).State = EntityState.Modified;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> DeleteClient(long id)
|
||||||
|
{
|
||||||
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.OAuthApplications.Remove(client);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5: Commit
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add src/Fengling.AuthService/Models/ src/Fengling.AuthService/Controllers/ src/Fengling.AuthService/Data/
|
||||||
|
git commit -m "feat(auth): add OAuth client management API"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This task adds OAuth client management functionality for managing OAuth applications. This will be used by Fengling.Console to register and manage clients.
|
||||||
|
|
||||||
|
**Tech Stack**: EF Core, ASP.NET Core Controllers
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- [ ] OAuthApplication model created
|
||||||
|
- [ ] DbSet added to ApplicationDbContext
|
||||||
|
- [ ] Migration generated and applied
|
||||||
|
- [ ] CRUD API endpoints created
|
||||||
|
- [ ] Build succeeds
|
||||||
|
- [ ] Committed to git
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Clients can be registered through this API
|
||||||
|
- Fengling.Console will be pre-registered in Task 11
|
||||||
|
- Status field enables client enable/disable
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Fengling.AuthService.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class OAuthClientsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
private readonly ILogger<OAuthClientsController> _logger;
|
||||||
|
|
||||||
|
public OAuthClientsController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
ILogger<OAuthClientsController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<IEnumerable<OAuthApplication>>> GetClients()
|
||||||
|
{
|
||||||
|
return await _context.OAuthApplications.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<OAuthApplication>> GetClient(long id)
|
||||||
|
{
|
||||||
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<OAuthApplication>> CreateClient(OAuthApplication application)
|
||||||
|
{
|
||||||
|
_context.OAuthApplications.Add(application);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return CreatedAtAction(nameof(GetClient), new { id = application.Id }, application);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> UpdateClient(long id, OAuthApplication application)
|
||||||
|
{
|
||||||
|
if (id != application.Id)
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Entry(application).State = EntityState.Modified;
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> DeleteClient(long id)
|
||||||
|
{
|
||||||
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.OAuthApplications.Remove(client);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,6 +11,8 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Applicati
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DbSet<OAuthApplication> OAuthApplications { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder builder)
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(builder);
|
base.OnModelCreating(builder);
|
||||||
@ -27,5 +29,17 @@ public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Applicati
|
|||||||
{
|
{
|
||||||
entity.Property(e => e.Description).HasMaxLength(200);
|
entity.Property(e => e.Description).HasMaxLength(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Entity<OAuthApplication>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.ClientId).IsUnique();
|
||||||
|
entity.Property(e => e.ClientId).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientSecret).HasMaxLength(200);
|
||||||
|
entity.Property(e => e.DisplayName).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.ConsentType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
379
src/Fengling.AuthService/Data/Migrations/20260202015716_AddOAuthApplications.Designer.cs
generated
Normal file
379
src/Fengling.AuthService/Data/Migrations/20260202015716_AddOAuthApplications.Designer.cs
generated
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260202015716_AddOAuthApplications")]
|
||||||
|
partial class AddOAuthApplications
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("RealName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.HasIndex("Phone")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientSecret")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("ConsentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("GrantTypes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("RedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("Scopes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("OAuthApplications");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddOAuthApplications : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OAuthApplications",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
ClientId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
ClientSecret = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
RedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
PostLogoutRedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
Scopes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
GrantTypes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
ClientType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
ConsentType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OAuthApplications", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OAuthApplications_ClientId",
|
||||||
|
table: "OAuthApplications",
|
||||||
|
column: "ClientId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OAuthApplications");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,7 +17,7 @@ namespace Fengling.AuthService.Data.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "9.0.10")
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@ -150,6 +150,73 @@ namespace Fengling.AuthService.Data.Migrations
|
|||||||
b.ToTable("AspNetUsers", (string)null);
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientSecret")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("ConsentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("GrantTypes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("RedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("Scopes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("OAuthApplications");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
|
|||||||
18
src/Fengling.AuthService/Models/OAuthApplication.cs
Normal file
18
src/Fengling.AuthService/Models/OAuthApplication.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class OAuthApplication
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string ClientId { get; set; } = string.Empty;
|
||||||
|
public string? ClientSecret { get; set; }
|
||||||
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
|
public string[] RedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] PostLogoutRedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] Scopes { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] GrantTypes { get; set; } = Array.Empty<string>();
|
||||||
|
public string ClientType { get; set; } = "public";
|
||||||
|
public string ConsentType { get; set; } = "implicit";
|
||||||
|
public string Status { get; set; } = "active";
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Fengling.AuthService")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Fengling.AuthService")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d6dc0b2d369ef9fc5d87920a440e2e7a574d1c7c")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d3810f5d43139224571e9558a8f9d1caf253a2af")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Fengling.AuthService")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Fengling.AuthService")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Fengling.AuthService")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Fengling.AuthService")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
24c56473aacfcd0dbdcdcd1107e64a8ffffecbda9c09b0df77e03eaeb763c386
|
6eb848986322eda4a24415ea7940bb6189775a7373c0cc69971ba237cf3fdeb1
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
a78d5931f86313e08e6618a4c0d21573e022c26f4708676ffd124f07e58e14df
|
246db42ea15f5b11045c9c9e1cfc0e315b80d78a560048b99d9119120a177411
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"kj0YdTIP9epXJ4ydBR9yaRr5OemJ36+FlRmnBdiGrUE=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["nGadCmuBEG\u002BKUP6Powa57G4ZzOO6ibT7XQKZuYm3g44=","elQhyiEcBZcCHMIxyXyx47S4otwc/MEXjAYU/dca/hQ=","QUvWOS2l6Gf\u002Bb29f7UDXsp99Km48zx\u002BXUkHxYrdP5O4=","587UMkRW9Duvi09dG2y/rsS2zVrz865mHwElGvidCDE=","H/2oX/AhA9MsCiviWyp\u002BBcj5VIq4M2vfs7Bz0Gkt6GQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"kj0YdTIP9epXJ4ydBR9yaRr5OemJ36+FlRmnBdiGrUE=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["nGadCmuBEG\u002BKUP6Powa57G4ZzOO6ibT7XQKZuYm3g44=","elQhyiEcBZcCHMIxyXyx47S4otwc/MEXjAYU/dca/hQ=","XrkTC/5D0wT4Vj8udAqp\u002B2DDPMQ5H3P4YZDu2X62NzI=","chR\u002BAL8taCFTQytCSiXtu6MS2y5z2GmMYAK8xprfbvA=","QUvWOS2l6Gf\u002Bb29f7UDXsp99Km48zx\u002BXUkHxYrdP5O4=","Zj1gozVje0UGK1srrd8TNGYUEXHzQpz/esP\u002BUaINhMs=","587UMkRW9Duvi09dG2y/rsS2zVrz865mHwElGvidCDE=","7HE6TnPvLegH\u002BkmPfdNuw7C6mM5Vgea4zehQVjDOli4="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"cWEb6+iVjovCYrac7gX+Ogl5Z4cMpIEURSADGbv9ou0=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["nGadCmuBEG\u002BKUP6Powa57G4ZzOO6ibT7XQKZuYm3g44=","elQhyiEcBZcCHMIxyXyx47S4otwc/MEXjAYU/dca/hQ=","QUvWOS2l6Gf\u002Bb29f7UDXsp99Km48zx\u002BXUkHxYrdP5O4=","587UMkRW9Duvi09dG2y/rsS2zVrz865mHwElGvidCDE=","H/2oX/AhA9MsCiviWyp\u002BBcj5VIq4M2vfs7Bz0Gkt6GQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"cWEb6+iVjovCYrac7gX+Ogl5Z4cMpIEURSADGbv9ou0=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["nGadCmuBEG\u002BKUP6Powa57G4ZzOO6ibT7XQKZuYm3g44=","elQhyiEcBZcCHMIxyXyx47S4otwc/MEXjAYU/dca/hQ=","XrkTC/5D0wT4Vj8udAqp\u002B2DDPMQ5H3P4YZDu2X62NzI=","chR\u002BAL8taCFTQytCSiXtu6MS2y5z2GmMYAK8xprfbvA=","QUvWOS2l6Gf\u002Bb29f7UDXsp99Km48zx\u002BXUkHxYrdP5O4=","Zj1gozVje0UGK1srrd8TNGYUEXHzQpz/esP\u002BUaINhMs=","587UMkRW9Duvi09dG2y/rsS2zVrz865mHwElGvidCDE=","7HE6TnPvLegH\u002BkmPfdNuw7C6mM5Vgea4zehQVjDOli4="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
Loading…
Reference in New Issue
Block a user