@rendermode InteractiveServer @attribute [Authorize] @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Authorization @using MudBlazor @using System.Net.Http.Json @using Microsoft.AspNetCore.Identity @using Microsoft.AspNetCore.Components @using OpenIddict.Abstractions @using RobotNet.IdentityServer.Data @using Microsoft.EntityFrameworkCore @using System.Threading @using static OpenIddict.Abstractions.OpenIddictConstants @inherits LayoutComponentBase @inject AuthenticationStateProvider AuthenticationStateProvider @inject ISnackbar Snackbar @inject IDialogService DialogService @inject NavigationManager NavigationManager @inject IOpenIddictApplicationManager ApplicationManager @inject IOpenIddictScopeManager ScopeManager
OpenIddict Manager Quản lý ứng dụng OAuth2 & OpenID Connect một cách dễ dàng
@filteredApplications.Count Apps
Danh sách Application Thêm Application
Client Type Display Name Secret Endpoints Actions @context.ClientId @context.Id[..8]... @(context.ClientType == ClientTypes.Confidential ? "Confidential" : "Public") @context.DisplayName
@if (!string.IsNullOrEmpty(context.ClientSecret)) { Yes } else { No }
@if (context.RedirectUris.Any()) { @context.RedirectUris.Count URIs } else { No URIs }
@(editingApplication != null ? "Chỉnh sửa Application" : "Tạo Application Mới") Thông tin cơ bản Public Confidential Explicit Implicit @if (applicationForm.ClientType == ClientTypes.Confidential) { } Cấu hình Endpoints
Redirect URIs @foreach (var uri in applicationForm.RedirectUris) { }
Post Logout URIs @foreach (var uri in applicationForm.PostLogoutRedirectUris) { }
Permissions & Requirements @foreach (var permission in permissionChecks) { @permission.Key.Split('.').Last() } @foreach (var selectedPermission in GetSelectedPermissions()) { } @if (applicationForm.ClientType == ClientTypes.Public) { Requirements @foreach (var requirement in requirementChecks) { } }
Hủy @(editingApplication != null ? "Cập nhật" : "Tạo mới")
Chi tiết Application @if (selectedApplication != null) { Thông tin cơ bản
ID: @selectedApplication.Id
Client ID: @selectedApplication.ClientId
Display Name: @selectedApplication.DisplayName
Type: @selectedApplication.ClientType
Consent: @selectedApplication.ConsentType
🔒 Bảo mật
Client Secret: @if (!string.IsNullOrEmpty(selectedApplication.ClientSecret)) { } else { Không }
@if (selectedApplication.RedirectUris.Any()) { 🔗 Redirect URIs @foreach (var uri in selectedApplication.RedirectUris) { } } @if (selectedApplication.Permissions.Any()) { 🛡️ Permissions @foreach (var permission in selectedApplication.Permissions) { } } @if (selectedApplication.Requirements.Any()) { ⚙️ Requirements @if (selectedApplication.Requirements.Any()) { @foreach (var requirement in selectedApplication.Requirements) { } } else { Không có requirements nào được thiết lập } }
}
Đóng
@code { private List filteredApplications = new(); private bool loadingApplications = false; private bool ShowApplicationDialog = false; private bool ShowDetailsDialog = false; private bool showClientSecret = false; private ApplicationInfo? editingApplication = null; private ApplicationInfo? selectedApplication = null; private ApplicationForm applicationForm = new(); private string redirectUriInput = string.Empty; private string postLogoutUriInput = string.Empty; private string customScopeInput = string.Empty; private HashSet customScopes = new(); private Dictionary permissionChecks = new(); private Dictionary requirementChecks = new(); private List availableScopes = new(); public class ApplicationInfo { public string Id { get; set; } = string.Empty; public string ApplicationType { get; set; } = string.Empty; public string ClientId { get; set; } = string.Empty; public string DisplayName { get; set; } = string.Empty; public string ClientType { get; set; } = string.Empty; public string ConsentType { get; set; } = string.Empty; public List RedirectUris { get; set; } = new(); public List PostLogoutRedirectUris { get; set; } = new(); public List Permissions { get; set; } = new(); public List Requirements { get; set; } = new(); public string? ClientSecret { get; set; } } public class ApplicationForm { public string ClientId { get; set; } = string.Empty; public string DisplayName { get; set; } = string.Empty; public string ClientType { get; set; } = ClientTypes.Public; public string ConsentType { get; set; } = ConsentTypes.Explicit; public string? ClientSecret { get; set; } public List RedirectUris { get; set; } = new(); public List PostLogoutRedirectUris { get; set; } = new(); } private string applicationSearchTerm = ""; protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity?.IsAuthenticated == true) { InitializePermissionChecks(); InitializeRequirementChecks(); await LoadApplicationsAsync(); await LoadAvailableScopesAsync(); } } private void InitializePermissionChecks() { permissionChecks.Clear(); permissionChecks.Add(Permissions.Endpoints.Authorization, false); permissionChecks.Add(Permissions.Endpoints.EndSession, false); permissionChecks.Add(Permissions.Endpoints.Token, false); permissionChecks.Add(Permissions.Endpoints.Introspection, false); permissionChecks.Add(Permissions.GrantTypes.AuthorizationCode, false); permissionChecks.Add(Permissions.GrantTypes.RefreshToken, false); permissionChecks.Add(Permissions.GrantTypes.ClientCredentials, false); permissionChecks.Add(Permissions.ResponseTypes.Code, false); permissionChecks.Add(Permissions.ResponseTypes.Token, false); permissionChecks.Add(Permissions.Scopes.Email, false); permissionChecks.Add(Permissions.Scopes.Profile, false); permissionChecks.Add(Permissions.Scopes.Roles, false); foreach (var scope in availableScopes) { var scopePermission = Permissions.Prefixes.Scope + scope; if (!permissionChecks.ContainsKey(scopePermission)) { permissionChecks.Add(scopePermission, false); } } } private void InitializeRequirementChecks() { requirementChecks = new() { { Requirements.Features.ProofKeyForCodeExchange, false } }; } private void OnClientTypeChanged(string newClientType) { applicationForm.ClientType = newClientType; if (newClientType == ClientTypes.Public) { applicationForm.ClientSecret = null; } StateHasChanged(); } private void OnPermissionSelectionChanged(IEnumerable selectedValues) { foreach (var key in permissionChecks.Keys.ToList()) { permissionChecks[key] = false; } foreach (var value in selectedValues) { if (permissionChecks.ContainsKey(value)) { permissionChecks[value] = true; } } StateHasChanged(); } private IEnumerable GetSelectedPermissions() { return permissionChecks.Where(x => x.Value).Select(x => x.Key); } private void RemovePermission(string permission) { if (permissionChecks.ContainsKey(permission)) { permissionChecks[permission] = false; StateHasChanged(); } } private IEnumerable FilteredApplications => string.IsNullOrWhiteSpace(applicationSearchTerm) ? filteredApplications : filteredApplications.Where(r => (r.ClientId != null && r.ClientId.Contains(applicationSearchTerm, StringComparison.OrdinalIgnoreCase)) || (r.DisplayName != null && r.DisplayName.Contains(applicationSearchTerm, StringComparison.OrdinalIgnoreCase)) || (r.ClientType != null && r.ClientType.Contains(applicationSearchTerm, StringComparison.OrdinalIgnoreCase)) || (r.Id != null && r.Id.Contains(applicationSearchTerm, StringComparison.OrdinalIgnoreCase))); private async Task LoadApplicationsAsync() { try { loadingApplications = true; filteredApplications.Clear(); await foreach (var app in ApplicationManager.ListAsync()) { string? clientSecret = null; try { var properties = await ApplicationManager.GetPropertiesAsync(app); clientSecret = properties.ContainsKey("client_secret") ? properties["client_secret"].ToString() : null; if (string.IsNullOrEmpty(clientSecret)) { var clientType = await ApplicationManager.GetClientTypeAsync(app); if (clientType == ClientTypes.Confidential) { clientSecret = "***"; } } } catch { var clientType = await ApplicationManager.GetClientTypeAsync(app); if (clientType == ClientTypes.Confidential) { clientSecret = "***"; } } filteredApplications.Add(new ApplicationInfo { Id = await ApplicationManager.GetIdAsync(app) ?? string.Empty, ApplicationType = await ApplicationManager.GetApplicationTypeAsync(app) ?? string.Empty, ClientId = await ApplicationManager.GetClientIdAsync(app) ?? string.Empty, DisplayName = await ApplicationManager.GetDisplayNameAsync(app) ?? string.Empty, ClientType = await ApplicationManager.GetClientTypeAsync(app) ?? string.Empty, ConsentType = await ApplicationManager.GetConsentTypeAsync(app) ?? string.Empty, RedirectUris = (await ApplicationManager.GetRedirectUrisAsync(app)).Select(u => u.ToString()).ToList(), PostLogoutRedirectUris = (await ApplicationManager.GetPostLogoutRedirectUrisAsync(app)).Select(u => u.ToString()).ToList(), Permissions = (await ApplicationManager.GetPermissionsAsync(app)).ToList(), Requirements = (await ApplicationManager.GetRequirementsAsync(app)).ToList(), ClientSecret = clientSecret }); } } catch (Exception ex) { Snackbar.Add($"Lỗi khi tải applications: {ex.Message}", Severity.Error); } finally { loadingApplications = false; StateHasChanged(); } } private async Task LoadAvailableScopesAsync() { try { availableScopes.Clear(); await foreach (var scope in ScopeManager.ListAsync()) { var scopeName = await ScopeManager.GetNameAsync(scope); if (!string.IsNullOrEmpty(scopeName)) { availableScopes.Add(scopeName); } } } catch (Exception ex) { Snackbar.Add($"Lỗi khi tải scopes: {ex.Message}", Severity.Error); } } private void ViewApplicationDetails(ApplicationInfo application) { selectedApplication = application; showClientSecret = false; ShowDetailsDialog = true; } private void CloseDetailsDialog() { ShowDetailsDialog = false; selectedApplication = null; showClientSecret = false; } private void ToggleClientSecretVisibility() { showClientSecret = !showClientSecret; } private async void OpenApplicationDialog(ApplicationInfo? application = null) { await LoadAvailableScopesAsync(); InitializePermissionChecks(); ShowApplicationDialog = true; editingApplication = application; ResetApplicationForm(); if (application != null) { applicationForm = new ApplicationForm { ClientId = application.ClientId, DisplayName = application.DisplayName, ClientType = application.ClientType, ConsentType = application.ConsentType, RedirectUris = new(application.RedirectUris), PostLogoutRedirectUris = new(application.PostLogoutRedirectUris), ClientSecret = application.ClientType == ClientTypes.Confidential ? string.Empty : null }; foreach (var permission in application.Permissions) { if (permissionChecks.ContainsKey(permission)) permissionChecks[permission] = true; else if (permission.StartsWith(Permissions.Prefixes.Scope)) customScopes.Add(permission[Permissions.Prefixes.Scope.Length..]); } foreach (var requirement in application.Requirements) { if (requirementChecks.ContainsKey(requirement)) requirementChecks[requirement] = true; } } StateHasChanged(); } private void EditApplication(ApplicationInfo application) => OpenApplicationDialog(application); private void ResetApplicationForm() { applicationForm = new(); redirectUriInput = string.Empty; postLogoutUriInput = string.Empty; customScopeInput = string.Empty; customScopes.Clear(); foreach (var key in permissionChecks.Keys.ToList()) permissionChecks[key] = false; foreach (var key in requirementChecks.Keys.ToList()) requirementChecks[key] = false; } private void AddRedirectUri() { if (!string.IsNullOrWhiteSpace(redirectUriInput)) { if (Uri.TryCreate(redirectUriInput.Trim(), UriKind.Absolute, out _)) { if (!applicationForm.RedirectUris.Contains(redirectUriInput.Trim())) { applicationForm.RedirectUris.Add(redirectUriInput.Trim()); redirectUriInput = string.Empty; StateHasChanged(); } else { Snackbar.Add("URI này đã tồn tại", Severity.Warning); } } else { Snackbar.Add("URI không hợp lệ. Vui lòng nhập URI đầy đủ (ví dụ: https://example.com/login-callback)", Severity.Error); } } } private void RemoveRedirectUri(string uri) => applicationForm.RedirectUris.Remove(uri); private void AddPostLogoutUri() { if (!string.IsNullOrWhiteSpace(postLogoutUriInput)) { if (Uri.TryCreate(postLogoutUriInput.Trim(), UriKind.Absolute, out _)) { if (!applicationForm.PostLogoutRedirectUris.Contains(postLogoutUriInput.Trim())) { applicationForm.PostLogoutRedirectUris.Add(postLogoutUriInput.Trim()); postLogoutUriInput = string.Empty; StateHasChanged(); } else { Snackbar.Add("URI này đã tồn tại", Severity.Warning); } } else { Snackbar.Add("URI không hợp lệ. Vui lòng nhập URI đầy đủ (ví dụ: https://example.com/logout-callback)", Severity.Error); } } } private void RemovePostLogoutUri(string uri) => applicationForm.PostLogoutRedirectUris.Remove(uri); private void AddCustomScope() { if (!string.IsNullOrWhiteSpace(customScopeInput) && !customScopes.Contains(customScopeInput)) { customScopes.Add(customScopeInput); customScopeInput = string.Empty; } } private void RemoveCustomScope(string scope) => customScopes.Remove(scope); private async Task SaveApplication() { try { if (string.IsNullOrWhiteSpace(applicationForm.ClientId)) { Snackbar.Add("Client ID là bắt buộc", Severity.Error); return; } if (applicationForm.ClientType == ClientTypes.Confidential) { if (editingApplication == null && string.IsNullOrWhiteSpace(applicationForm.ClientSecret)) { Snackbar.Add("Client Secret là bắt buộc cho Confidential client", Severity.Error); return; } } if (editingApplication != null) { var existingApp = await ApplicationManager.FindByClientIdAsync(editingApplication.ClientId); if (existingApp != null) { var descriptor = new OpenIddictApplicationDescriptor { ClientId = applicationForm.ClientId, DisplayName = applicationForm.DisplayName, ClientType = applicationForm.ClientType, ConsentType = applicationForm.ConsentType }; if (applicationForm.ClientType == ClientTypes.Confidential) { if (!string.IsNullOrWhiteSpace(applicationForm.ClientSecret)) { descriptor.ClientSecret = applicationForm.ClientSecret; } } else if (applicationForm.ClientType == ClientTypes.Public) { descriptor.ClientSecret = null; } var currentDescriptor = new OpenIddictApplicationDescriptor(); await ApplicationManager.PopulateAsync(currentDescriptor, existingApp); if (applicationForm.ClientType == ClientTypes.Confidential && string.IsNullOrWhiteSpace(applicationForm.ClientSecret)) { descriptor.ClientSecret = currentDescriptor.ClientSecret; } foreach (var uriString in applicationForm.RedirectUris) { if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) { descriptor.RedirectUris.Add(uri); } else { Snackbar.Add($"Redirect URI không hợp lệ: {uriString}", Severity.Error); return; } } foreach (var uriString in applicationForm.PostLogoutRedirectUris) { if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) { descriptor.PostLogoutRedirectUris.Add(uri); } else { Snackbar.Add($"Post Logout URI không hợp lệ: {uriString}", Severity.Error); return; } } permissionChecks.Where(x => x.Value).ToList().ForEach(kvp => descriptor.Permissions.Add(kvp.Key)); customScopes.ToList().ForEach(scope => descriptor.Permissions.Add(Permissions.Prefixes.Scope + scope)); requirementChecks.Where(x => x.Value).ToList().ForEach(kvp => descriptor.Requirements.Add(kvp.Key)); await ApplicationManager.UpdateAsync(existingApp, descriptor); } } else { var descriptor = new OpenIddictApplicationDescriptor { ClientId = applicationForm.ClientId, DisplayName = applicationForm.DisplayName, ClientType = applicationForm.ClientType, ConsentType = applicationForm.ConsentType, ClientSecret = applicationForm.ClientType == ClientTypes.Confidential ? applicationForm.ClientSecret : null }; foreach (var uriString in applicationForm.RedirectUris) { if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) { descriptor.RedirectUris.Add(uri); } } foreach (var uriString in applicationForm.PostLogoutRedirectUris) { if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri)) { descriptor.PostLogoutRedirectUris.Add(uri); } } permissionChecks.Where(x => x.Value).ToList().ForEach(kvp => descriptor.Permissions.Add(kvp.Key)); customScopes.ToList().ForEach(scope => descriptor.Permissions.Add(Permissions.Prefixes.Scope + scope)); requirementChecks.Where(x => x.Value).ToList().ForEach(kvp => descriptor.Requirements.Add(kvp.Key)); await ApplicationManager.CreateAsync(descriptor); } Snackbar.Add(editingApplication != null ? "Cập nhật application thành công" : "Tạo application thành công", Severity.Success); ShowApplicationDialog = false; await LoadApplicationsAsync(); } catch (Exception ex) { Snackbar.Add($"Lỗi khi lưu application: {ex.Message}", Severity.Error); } } private void CancelApplicationDialog() { ShowApplicationDialog = false; ResetApplicationForm(); } private async Task DeleteApplication(string clientId) { var confirm = await DialogService.ShowMessageBox("Xác nhận xóa", $"Bạn có chắc chắn muốn xóa application '{clientId}'?", yesText: "Xóa", cancelText: "Hủy"); if (confirm == true) { try { var app = await ApplicationManager.FindByClientIdAsync(clientId); if (app != null) { await ApplicationManager.DeleteAsync(app); Snackbar.Add("Xóa application thành công", Severity.Success); await LoadApplicationsAsync(); } } catch (Exception ex) { Snackbar.Add($"Lỗi khi xóa application: {ex.Message}", Severity.Error); } } } }