diff --git a/IdentityServer/Controllers/ManageController.cs b/IdentityServer/Controllers/ManageController.cs index a99c623..c32cf1d 100644 --- a/IdentityServer/Controllers/ManageController.cs +++ b/IdentityServer/Controllers/ManageController.cs @@ -542,19 +542,15 @@ namespace Teknik.IdentityServer.Controllers ClientName = model.Name, ClientUri = model.HomepageUrl, LogoUri = model.LogoUrl, - AllowedGrantTypes = new List() - { - GrantType.AuthorizationCode, - GrantType.ClientCredentials - }, + + AllowedGrantTypes = model.AllowedGrants, + AllowedScopes = model.AllowedScopes, ClientSecrets = { new IdentityServer4.Models.Secret(clientSecret.Sha256()) }, - RequireConsent = true, - RedirectUris = { model.CallbackUrl @@ -565,8 +561,7 @@ namespace Teknik.IdentityServer.Controllers origin }, - AllowedScopes = model.AllowedScopes, - + RequireConsent = true, AllowOfflineAccess = true }; @@ -617,6 +612,36 @@ namespace Teknik.IdentityServer.Controllers newOrigin.Origin = origin; configContext.Add(newUri); + // Update their allowed grants + var curGrants = configContext.Set().Where(c => c.ClientId == foundClient.Id).ToList(); + if (curGrants != null) + { + configContext.RemoveRange(curGrants); + } + foreach (var grantType in model.AllowedGrants) + { + var newGrant = new ClientGrantType(); + newGrant.Client = foundClient; + newGrant.ClientId = foundClient.Id; + newGrant.GrantType = grantType; + configContext.Add(newGrant); + } + + // Update their allowed scopes + var curScopes = configContext.Set().Where(c => c.ClientId == foundClient.Id).ToList(); + if (curScopes != null) + { + configContext.RemoveRange(curScopes); + } + foreach (var scope in model.AllowedScopes) + { + var newScope = new ClientScope(); + newScope.Client = foundClient; + newScope.ClientId = foundClient.Id; + newScope.Scope = scope; + configContext.Add(newScope); + } + // Save all the changed configContext.SaveChanges(); diff --git a/IdentityServer/Models/Manage/CreateClientModel.cs b/IdentityServer/Models/Manage/CreateClientModel.cs index 2055649..90c0a97 100644 --- a/IdentityServer/Models/Manage/CreateClientModel.cs +++ b/IdentityServer/Models/Manage/CreateClientModel.cs @@ -13,5 +13,6 @@ namespace Teknik.IdentityServer.Models.Manage public string LogoUrl { get; set; } public string CallbackUrl { get; set; } public ICollection AllowedScopes { get; set; } + public ICollection AllowedGrants { get; set; } } } diff --git a/IdentityServer/Models/Manage/EditClientModel.cs b/IdentityServer/Models/Manage/EditClientModel.cs index 758ca0b..7602d90 100644 --- a/IdentityServer/Models/Manage/EditClientModel.cs +++ b/IdentityServer/Models/Manage/EditClientModel.cs @@ -13,5 +13,7 @@ namespace Teknik.IdentityServer.Models.Manage public string HomepageUrl { get; set; } public string LogoUrl { get; set; } public string CallbackUrl { get; set; } + public ICollection AllowedScopes { get; set; } + public ICollection AllowedGrants { get; set; } } } diff --git a/IdentityServer/Properties/launchSettings.json b/IdentityServer/Properties/launchSettings.json index 6c18273..ed61191 100644 --- a/IdentityServer/Properties/launchSettings.json +++ b/IdentityServer/Properties/launchSettings.json @@ -21,7 +21,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:6002/" + "applicationUrl": "https://localhost:9002/" }, "IdentityServer - Prod": { "commandName": "Project", @@ -29,7 +29,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Production" }, - "applicationUrl": "https://localhost:6002/" + "applicationUrl": "https://localhost:9002/" } } } \ No newline at end of file diff --git a/IdentityServer/Views/Account/LoginWith2fa.cshtml b/IdentityServer/Views/Account/LoginWith2fa.cshtml index f48fef8..16c8bdf 100644 --- a/IdentityServer/Views/Account/LoginWith2fa.cshtml +++ b/IdentityServer/Views/Account/LoginWith2fa.cshtml @@ -18,7 +18,6 @@
-
diff --git a/IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml b/IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml index 1c6e14f..e907435 100644 --- a/IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml +++ b/IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml @@ -20,7 +20,6 @@
-
diff --git a/Teknik/Areas/User/Controllers/UserController.cs b/Teknik/Areas/User/Controllers/UserController.cs index af9c846..9c07625 100644 --- a/Teknik/Areas/User/Controllers/UserController.cs +++ b/Teknik/Areas/User/Controllers/UserController.cs @@ -437,7 +437,8 @@ namespace Teknik.Areas.Users.Controllers HomepageUrl = client.ClientUri, LogoUrl = client.LogoUri, CallbackUrl = string.Join(',', client.RedirectUris), - AllowedScopes = client.AllowedScopes + AllowedScopes = client.AllowedScopes, + GrantType = IdentityHelper.GrantsToGrantType(client.AllowedGrantTypes.ToArray()) }); } @@ -1239,7 +1240,7 @@ namespace Teknik.Areas.Users.Controllers [HttpPost] [ValidateAntiForgeryToken] - public async Task CreateClient(string name, string homepageUrl, string logoUrl, string callbackUrl, [FromServices] ICompositeViewEngine viewEngine) + public async Task CreateClient(string name, string homepageUrl, string logoUrl, string callbackUrl, IdentityClientGrant grantType, string scopes, [FromServices] ICompositeViewEngine viewEngine) { try { @@ -1255,7 +1256,15 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "Invalid logo URL" }); // Validate the code with the identity server - var result = await IdentityHelper.CreateClient(_config, User.Identity.Name, name, homepageUrl, logoUrl, callbackUrl, "openid", "role", "account-info", "security-info", "teknik-api.read", "teknik-api.write"); + var result = await IdentityHelper.CreateClient( + _config, + User.Identity.Name, + name, + homepageUrl, + logoUrl, + callbackUrl, + IdentityHelper.GrantTypeToGrants(grantType), + scopes.Split(',')); if (result.Success) { @@ -1267,6 +1276,8 @@ namespace Teknik.Areas.Users.Controllers model.HomepageUrl = homepageUrl; model.LogoUrl = logoUrl; model.CallbackUrl = callbackUrl; + model.GrantType = grantType; + model.AllowedScopes = scopes.Split(','); string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/User/Views/User/Settings/ClientView.cshtml", model); @@ -1287,14 +1298,15 @@ namespace Teknik.Areas.Users.Controllers Client foundClient = await IdentityHelper.GetClient(_config, User.Identity.Name, clientId); if (foundClient != null) { - ClientViewModel model = new ClientViewModel() + ClientModifyViewModel model = new ClientModifyViewModel() { Id = foundClient.ClientId, Name = foundClient.ClientName, HomepageUrl = foundClient.ClientUri, LogoUrl = foundClient.LogoUri, CallbackUrl = string.Join(',', foundClient.RedirectUris), - AllowedScopes = foundClient.AllowedScopes + AllowedScopes = foundClient.AllowedScopes, + GrantType = IdentityHelper.GrantsToGrantType(foundClient.AllowedGrantTypes.ToArray()).ToString() }; return Json(new { result = true, client = model }); @@ -1304,7 +1316,7 @@ namespace Teknik.Areas.Users.Controllers [HttpPost] [ValidateAntiForgeryToken] - public async Task EditClient(string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl, [FromServices] ICompositeViewEngine viewEngine) + public async Task EditClient(string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl, IdentityClientGrant grantType, string scopes, [FromServices] ICompositeViewEngine viewEngine) { try { @@ -1325,7 +1337,16 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "Client does not exist" }); // Validate the code with the identity server - var result = await IdentityHelper.EditClient(_config, User.Identity.Name, clientId, name, homepageUrl, logoUrl, callbackUrl); + var result = await IdentityHelper.EditClient( + _config, + User.Identity.Name, + clientId, + name, + homepageUrl, + logoUrl, + callbackUrl, + IdentityHelper.GrantTypeToGrants(grantType), + scopes.Split(',')); if (result.Success) { @@ -1337,6 +1358,8 @@ namespace Teknik.Areas.Users.Controllers model.HomepageUrl = homepageUrl; model.LogoUrl = logoUrl; model.CallbackUrl = callbackUrl; + model.GrantType = grantType; + model.AllowedScopes = scopes.Split(','); string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/User/Views/User/Settings/ClientView.cshtml", model); diff --git a/Teknik/Areas/User/Utility/IdentityHelper.cs b/Teknik/Areas/User/Utility/IdentityHelper.cs index 68b3835..e0c24c9 100644 --- a/Teknik/Areas/User/Utility/IdentityHelper.cs +++ b/Teknik/Areas/User/Utility/IdentityHelper.cs @@ -71,6 +71,47 @@ namespace Teknik.Areas.Users.Utility return new IdentityResult() { Success = false, Message = "HTTP Error: " + response.StatusCode + " | " + (await response.Content.ReadAsStringAsync()) }; } + public static string[] GrantTypeToGrants(IdentityClientGrant grantType) + { + List grants = new List(); + switch (grantType) + { + case IdentityClientGrant.Implicit: + grants.Add(GrantType.Implicit); + break; + case IdentityClientGrant.AuthorizationCode: + grants.Add(GrantType.AuthorizationCode); + break; + case IdentityClientGrant.ClientCredentials: + grants.Add(GrantType.ClientCredentials); + break; + default: + grants.Add(GrantType.Hybrid); + break; + } + return grants.ToArray(); + } + + public static IdentityClientGrant GrantsToGrantType(string[] grants) + { + if (grants.Contains(GrantType.Implicit)) + { + return IdentityClientGrant.Implicit; + } + else if (grants.Contains(GrantType.AuthorizationCode)) + { + return IdentityClientGrant.AuthorizationCode; + } + else if (grants.Contains(GrantType.ClientCredentials)) + { + return IdentityClientGrant.ClientCredentials; + } + else + { + return IdentityClientGrant.ClientCredentials; + } + } + // API Functions public static async Task CreateUser(Config config, string username, string password, string recoveryEmail) @@ -350,7 +391,7 @@ namespace Teknik.Areas.Users.Utility throw new Exception(result.Message); } - public static async Task CreateClient(Config config, string username, string name, string homepageUrl, string logoUrl, string callbackUrl, params string[] allowedScopes) + public static async Task CreateClient(Config config, string username, string name, string homepageUrl, string logoUrl, string callbackUrl, string[] allowedGrants, string[] allowedScopes) { var manageUrl = CreateUrl(config, $"Manage/CreateClient"); @@ -362,12 +403,13 @@ namespace Teknik.Areas.Users.Utility homepageUrl = homepageUrl, logoUrl = logoUrl, callbackUrl = callbackUrl, - allowedScopes = allowedScopes + allowedScopes = allowedScopes, + allowedGrants = allowedGrants }); return response; } - public static async Task EditClient(Config config, string username, string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl) + public static async Task EditClient(Config config, string username, string clientId, string name, string homepageUrl, string logoUrl, string callbackUrl, string[] allowedGrants, string[] allowedScopes) { var manageUrl = CreateUrl(config, $"Manage/EditClient"); @@ -379,7 +421,9 @@ namespace Teknik.Areas.Users.Utility name = name, homepageUrl = homepageUrl, logoUrl = logoUrl, - callbackUrl = callbackUrl + callbackUrl = callbackUrl, + allowedScopes = allowedScopes, + allowedGrants = allowedGrants }); return response; } diff --git a/Teknik/Areas/User/ViewModels/ClientModifyViewModel.cs b/Teknik/Areas/User/ViewModels/ClientModifyViewModel.cs new file mode 100644 index 0000000..fa64f0b --- /dev/null +++ b/Teknik/Areas/User/ViewModels/ClientModifyViewModel.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Teknik.Utilities; +using Teknik.ViewModels; + +namespace Teknik.Areas.Users.ViewModels +{ + public class ClientModifyViewModel : ViewModelBase + { + public string Id { get; set; } + public string Name { get; set; } + public string HomepageUrl { get; set; } + public string LogoUrl { get; set; } + public string CallbackUrl { get; set; } + public ICollection AllowedScopes { get; set; } + public string GrantType { get; set; } + } +} diff --git a/Teknik/Areas/User/ViewModels/ClientViewModel.cs b/Teknik/Areas/User/ViewModels/ClientViewModel.cs index 6f909cd..1f22e19 100644 --- a/Teknik/Areas/User/ViewModels/ClientViewModel.cs +++ b/Teknik/Areas/User/ViewModels/ClientViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Teknik.Utilities; using Teknik.ViewModels; namespace Teknik.Areas.Users.ViewModels @@ -14,5 +15,6 @@ namespace Teknik.Areas.Users.ViewModels public string LogoUrl { get; set; } public string CallbackUrl { get; set; } public ICollection AllowedScopes { get; set; } + public IdentityClientGrant GrantType { get; set; } } } diff --git a/Teknik/Areas/User/Views/User/Settings/ClientView.cshtml b/Teknik/Areas/User/Views/User/Settings/ClientView.cshtml index d87c414..767ad1b 100644 --- a/Teknik/Areas/User/Views/User/Settings/ClientView.cshtml +++ b/Teknik/Areas/User/Views/User/Settings/ClientView.cshtml @@ -27,6 +27,33 @@
+
+
+
+ Allowed Scopes +
+
+
+
+ @(string.Join(", ", Model.AllowedScopes)) +
+
+
+
+
+
+ Grant Type +
+
+
+
+ @Model.GrantType.GetDescription() +
+
+
+ +
+
@@ -39,7 +66,7 @@
-
+
Authorization Callback Url diff --git a/Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml b/Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml index 6c2f83a..67c519d 100644 --- a/Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml +++ b/Teknik/Areas/User/Views/User/Settings/DeveloperSettings.cshtml @@ -6,6 +6,8 @@ Layout = "~/Areas/User/Views/User/Settings/Settings.cshtml"; } + +