diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9800232..f4d023b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,7 +23,7 @@ "${workspaceFolder}/nuget-host.csproj", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary", - "/restore" + "--ignore-failed-sources" ], "problemMatcher": "$msCompile" }, diff --git a/Models/RepositoryIdentityResource.cs b/Models/RepositoryIdentityResource.cs new file mode 100644 index 0000000..8be220c --- /dev/null +++ b/Models/RepositoryIdentityResource.cs @@ -0,0 +1,9 @@ +using IdentityServer4.Models; + +namespace nuget_host.Models +{ + public class RepositoryIdentityResource : IdentityResource + { + + } +} \ No newline at end of file diff --git a/Quickstart/Account/ExternalController.cs b/Quickstart/Account/ExternalController.cs index 1a7479e..607997b 100644 --- a/Quickstart/Account/ExternalController.cs +++ b/Quickstart/Account/ExternalController.cs @@ -128,7 +128,8 @@ namespace IdentityServerHost.Quickstart.UI // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); + await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, + context?.ClientId)); if (context != null) { diff --git a/Quickstart/Consent/ConsentController.cs b/Quickstart/Consent/ConsentController.cs index 99d5b11..9c3954a 100644 --- a/Quickstart/Consent/ConsentController.cs +++ b/Quickstart/Consent/ConsentController.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using IdentityServer4.Validation; using System.Collections.Generic; using System; - +using IdentityServer4.Models; namespace IdentityServerHost.Quickstart.UI { /// @@ -149,7 +149,7 @@ namespace IdentityServerHost.Quickstart.UI // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; + result.Client = model.Client; } else { @@ -186,26 +186,25 @@ namespace IdentityServerHost.Quickstart.UI Description = model?.Description, ReturnUrl = returnUrl, - - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent + ClientName = model.ClientName, + ClientUrl = model.ClientUri, + ClientLogoUrl = model.LogoUri, + AllowRememberConsent = model.AllowRememberConsent }; - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.IdentityScopes = model.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); - foreach(var parsedScope in request.ValidatedResources.ParsedScopes) + foreach(var parsedScope in model.ValidatedResources.ParsedScopes.Scopes) { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); + var apiScope = model.ValidatedResources.Resources.FindApiScope(parsedScope); if (apiScope != null) { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); + var scopeVm = CreateScopeViewModel(model.ValidatedResources.ParsedScopes, apiScope, vm.ScopesConsented.Contains(model.ValidatedResources.ParsedScopes.RawValue) || model == null); apiScopes.Add(scopeVm); } } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) + if (ConsentOptions.EnableOfflineAccess && model.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } @@ -227,17 +226,17 @@ namespace IdentityServerHost.Quickstart.UI }; } - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) + public ScopeViewModel CreateScopeViewModel(ParsedScopes parsedScope, Scope apiScope, bool check) { - var displayName = apiScope.DisplayName ?? apiScope.Name; - if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) + var displayName = apiScope.DisplayName; + if (!String.IsNullOrWhiteSpace(parsedScope.RawValue)) { - displayName += ":" + parsedScopeValue.ParsedParameter; + displayName += ":" + parsedScope.RawValue; } return new ScopeViewModel { - Value = parsedScopeValue.RawValue, + Value = parsedScope.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, diff --git a/Quickstart/Consent/ConsentInputModel.cs b/Quickstart/Consent/ConsentInputModel.cs index f608fe3..2658eaa 100644 --- a/Quickstart/Consent/ConsentInputModel.cs +++ b/Quickstart/Consent/ConsentInputModel.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; +using IdentityServer4.Models; namespace IdentityServerHost.Quickstart.UI { @@ -13,5 +14,11 @@ namespace IdentityServerHost.Quickstart.UI public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } + public string ClientName { get; internal set; } + public string ClientUri { get; internal set; } + public string LogoUri { get; internal set; } + public bool AllowRememberConsent { get; internal set; } + public ValidatedResources ValidatedResources { get; internal set; } + public Client Client { get; internal set; } } } \ No newline at end of file diff --git a/Quickstart/Consent/ConsentViewModel.cs b/Quickstart/Consent/ConsentViewModel.cs index af4b9c5..1543f8f 100644 --- a/Quickstart/Consent/ConsentViewModel.cs +++ b/Quickstart/Consent/ConsentViewModel.cs @@ -8,12 +8,13 @@ namespace IdentityServerHost.Quickstart.UI { public class ConsentViewModel : ConsentInputModel { - public string ClientName { get; set; } - public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } - public bool AllowRememberConsent { get; set; } + public string ClientUrl { get; set; } - public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } + + public IEnumerable IdentityScopes { get; set; } + + } } diff --git a/Quickstart/Consent/ParsedScopes.cs b/Quickstart/Consent/ParsedScopes.cs new file mode 100644 index 0000000..90f29b0 --- /dev/null +++ b/Quickstart/Consent/ParsedScopes.cs @@ -0,0 +1,29 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using System.Collections.Generic; +using IdentityServer4.Models; + +namespace IdentityServerHost.Quickstart.UI +{ + public class ParsedScopes + { + public ParsedScopes(ParsedSecret secret) + { + RawValue = secret.Properties.ContainsKey(KEY_SCOPES) ? null : secret.Properties[KEY_SCOPES]; + Emphasize = secret.Properties.ContainsKey(KEY_OL); + if (secret.Properties.ContainsKey(KEY_SCOPES)) Scopes = secret.Properties[KEY_SCOPES].Split(','); + } + + public const string KEY_SCOPES = "scopes"; + public const string KEY_OL = "ol"; + public string RawValue { + get ; + } + + + public string[] Scopes { get ; protected set; } + public bool Emphasize { get; } + } +} \ No newline at end of file diff --git a/Quickstart/Consent/QSAuthorizationRequest.cs b/Quickstart/Consent/QSAuthorizationRequest.cs new file mode 100644 index 0000000..6328f0d --- /dev/null +++ b/Quickstart/Consent/QSAuthorizationRequest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using IdentityServer4.Models; +using IdentityServer4.Validation; +using NuGet.Packaging; + +namespace nuget_host.OAuth +{ + internal class NHAuthorizationRequest : AuthorizationRequest + { + internal NHAuthorizationRequest(ValidatedAuthorizeRequest request) : base() + { + ClientId = request.ClientId; + RedirectUri = request.RedirectUri; + DisplayMode = request.DisplayMode; + UiLocales = request.UiLocales; + IdP = request.GetIdP(); + Tenant = request.GetTenant(); + LoginHint = request.LoginHint; + PromptMode = request.PromptMode; + AcrValues = request.GetAcrValues(); + ScopesRequested = request.RequestedScopes; + Parameters.Add(request.Raw); + RequestObjectValues.AddRange(request.RequestObjectValues); + } + + } +} \ No newline at end of file diff --git a/Quickstart/Consent/ValidatedResource.cs b/Quickstart/Consent/ValidatedResource.cs new file mode 100644 index 0000000..fc77281 --- /dev/null +++ b/Quickstart/Consent/ValidatedResource.cs @@ -0,0 +1,17 @@ +// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. + + +using IdentityServer4.Models; +using System.Collections.Generic; + +namespace IdentityServerHost.Quickstart.UI +{ + public class ValidatedResources + { + public Resources Resources { get; set; } + public ParsedScopes ParsedScopes { get; internal set; } + public bool OfflineAccess { get; internal set; } + + } +} \ No newline at end of file diff --git a/Quickstart/Device/DeviceAuthorizationInputModel.cs b/Quickstart/Device/DeviceAuthorizationInputModel.cs index a221181..d586bc6 100644 --- a/Quickstart/Device/DeviceAuthorizationInputModel.cs +++ b/Quickstart/Device/DeviceAuthorizationInputModel.cs @@ -4,8 +4,10 @@ namespace IdentityServerHost.Quickstart.UI { + public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } + } } \ No newline at end of file diff --git a/Quickstart/Device/DeviceController.cs b/Quickstart/Device/DeviceController.cs index 390afda..bfb78f7 100644 --- a/Quickstart/Device/DeviceController.cs +++ b/Quickstart/Device/DeviceController.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using IdentityModel.Client; using IdentityServer4.Configuration; using IdentityServer4.Events; using IdentityServer4.Extensions; @@ -16,6 +17,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using nuget_host.OAuth; namespace IdentityServerHost.Quickstart.UI { @@ -131,7 +133,7 @@ namespace IdentityServerHost.Quickstart.UI // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; + result.Client = model.Client; } else { @@ -153,7 +155,9 @@ namespace IdentityServerHost.Quickstart.UI return null; } - private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) + private DeviceAuthorizationViewModel CreateConsentViewModel( + string userCode, DeviceAuthorizationInputModel model, + DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { @@ -163,25 +167,27 @@ namespace IdentityServerHost.Quickstart.UI RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent + ClientName = model?.ClientName, + ClientUrl = model?.ClientUri, + ClientLogoUrl = model?.LogoUri, + AllowRememberConsent = model != null && model.AllowRememberConsent }; - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.IdentityScopes = model.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); - foreach (var parsedScope in request.ValidatedResources.ParsedScopes) + foreach (var parsedScope in model.ValidatedResources.ParsedScopes.Scopes) { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); + var apiScope = model.ValidatedResources.Resources.FindApiScope(parsedScope); if (apiScope != null) { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); + var vreq = CreateValidatedRequest(request, apiScope); + + var scopeVm = CreateScopeViewModel(apiScope,vreq); apiScopes.Add(scopeVm); } } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) + if (ConsentOptions.EnableOfflineAccess && model.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } @@ -190,6 +196,24 @@ namespace IdentityServerHost.Quickstart.UI return vm; } + private ValidatedAuthorizeRequest CreateValidatedRequest(DeviceFlowAuthorizationRequest request, Scope apiScope) + { + throw new NotImplementedException(); + } + + private ScopeViewModel CreateScopeViewModel(Scope scope, ValidatedAuthorizeRequest req) + { + return new ScopeViewModel + { + Value = scope.Name, + DisplayName = scope.DisplayName ?? scope.Name, + Description = scope.Description, + Emphasize = scope.Emphasize, + Required = scope.Required, + Checked = req.Client != null + }; + } + private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel @@ -203,15 +227,15 @@ namespace IdentityServerHost.Quickstart.UI }; } - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) + public ScopeViewModel CreateScopeViewModel(ParsedScopes parsedScopeValue, Scope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? - DisplayName = apiScope.DisplayName ?? apiScope.Name, + DisplayName = apiScope.DisplayName, Description = apiScope.Description, - Emphasize = apiScope.Emphasize, + Emphasize = parsedScopeValue.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; diff --git a/nuget-host.csproj b/nuget-host.csproj index 82e08b6..6cac9c6 100644 --- a/nuget-host.csproj +++ b/nuget-host.csproj @@ -7,22 +7,22 @@ - - - - - - + + + + + + - - + + - + diff --git a/omnisharp.json b/omnisharp.json new file mode 100644 index 0000000..e2247f9 --- /dev/null +++ b/omnisharp.json @@ -0,0 +1,8 @@ +{ + "msbuild": { + "useBundledOnly": false, + "Configuration": "Debug", + "CscToolPath": "/usr/bin", + "CscToolExe": "csc" + } +} \ No newline at end of file