Removes IdentityServer4 usage
parent
dc37c9a9f0
commit
838de379fd
@ -1,67 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Test;
|
||||
|
||||
namespace nuget_host
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
public static IEnumerable<IdentityResource> IdentityResources =>
|
||||
new List<IdentityResource>
|
||||
{
|
||||
new IdentityResources.OpenId(),
|
||||
new IdentityResources.Profile(),
|
||||
};
|
||||
|
||||
public static IEnumerable<ApiResource> ApiResources =>
|
||||
new List<ApiResource>
|
||||
{
|
||||
new ApiResource(IdentityServerConstants.LocalApi.ScopeName),
|
||||
new ApiResource(scope_packages)
|
||||
};
|
||||
|
||||
public const string scope_packages = "packages";
|
||||
|
||||
public static IEnumerable<Client> Clients =>
|
||||
new List<Client>
|
||||
{
|
||||
// machine to machine client
|
||||
new Client
|
||||
{
|
||||
ClientId = "client",
|
||||
ClientSecrets = { new Secret("secret".Sha256()) },
|
||||
|
||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||
// scopes that client has access to
|
||||
AllowedScopes = { scope_packages,
|
||||
IdentityServerConstants.LocalApi.ScopeName }
|
||||
},
|
||||
|
||||
// interactive ASP.NET Core MVC client
|
||||
new Client
|
||||
{
|
||||
ClientId = "mvc",
|
||||
ClientSecrets = { new Secret("secret".Sha256()) },
|
||||
|
||||
AllowedGrantTypes = GrantTypes.Code,
|
||||
|
||||
// where to redirect to after login
|
||||
RedirectUris = { "https://localhost:5002/signin-oidc" },
|
||||
|
||||
// where to redirect to after logout
|
||||
PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
|
||||
|
||||
AllowedScopes = new List<string>
|
||||
{
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.LocalApi.ScopeName,
|
||||
scope_packages
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static List<TestUser> TestUsers { get; internal set; }
|
||||
}
|
||||
}
|
@ -1,260 +0,0 @@
|
||||
// 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.Events;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Extensions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using nuget_host.Models;
|
||||
|
||||
namespace nuget_host.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// This controller processes the consent UI
|
||||
/// </summary>
|
||||
[SecurityHeaders]
|
||||
[Authorize]
|
||||
public class ConsentController : Controller
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IEventService _events;
|
||||
private readonly ILogger<ConsentController> _logger;
|
||||
|
||||
public ConsentController(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IEventService events,
|
||||
ILogger<ConsentController> logger)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_events = events;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the consent screen
|
||||
/// </summary>
|
||||
/// <param name="returnUrl"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(string returnUrl)
|
||||
{
|
||||
var vm = await BuildViewModelAsync(returnUrl);
|
||||
if (vm != null)
|
||||
{
|
||||
return View("Index", vm);
|
||||
}
|
||||
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the consent screen postback
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Index(ConsentInputModel model)
|
||||
{
|
||||
var result = await ProcessConsent(model);
|
||||
|
||||
if (result.IsRedirect)
|
||||
{
|
||||
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
|
||||
if (context?.IsNativeClient() == true)
|
||||
{
|
||||
// The client is native, so this change in how to
|
||||
// return the response is for better UX for the end user.
|
||||
return this.LoadingPage("Redirect", result.RedirectUri);
|
||||
}
|
||||
|
||||
return Redirect(result.RedirectUri);
|
||||
}
|
||||
|
||||
if (result.HasValidationError)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, result.ValidationError);
|
||||
}
|
||||
|
||||
if (result.ShowView)
|
||||
{
|
||||
return View("Index", result.ViewModel);
|
||||
}
|
||||
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
/* helper APIs for the ConsentController */
|
||||
/*****************************************/
|
||||
private async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
|
||||
{
|
||||
var result = new ProcessConsentResult();
|
||||
|
||||
// validate return url is still valid
|
||||
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
|
||||
if (request == null) return result;
|
||||
|
||||
ConsentResponse grantedConsent = null;
|
||||
|
||||
// user clicked 'no' - send back the standard 'access_denied' response
|
||||
if (model?.Button == "no")
|
||||
{
|
||||
grantedConsent = ConsentResponse.Denied;
|
||||
|
||||
// emit event
|
||||
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested));
|
||||
}
|
||||
// user clicked 'yes' - validate the data
|
||||
else if (model?.Button == "yes")
|
||||
{
|
||||
// if the user consented to some scope, build the response model
|
||||
if (model.ScopesConsented != null && model.ScopesConsented.Any())
|
||||
{
|
||||
var scopes = model.ScopesConsented;
|
||||
if (ConsentOptions.EnableOfflineAccess == false)
|
||||
{
|
||||
scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
|
||||
}
|
||||
|
||||
grantedConsent = new ConsentResponse
|
||||
{
|
||||
RememberConsent = model.RememberConsent,
|
||||
ScopesConsented = scopes.ToArray()
|
||||
};
|
||||
|
||||
// emit event
|
||||
await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
|
||||
}
|
||||
|
||||
if (grantedConsent != null)
|
||||
{
|
||||
// communicate outcome of consent back to identityserver
|
||||
await _interaction.GrantConsentAsync(request, grantedConsent);
|
||||
|
||||
// indicate that's it ok to redirect back to authorization endpoint
|
||||
result.RedirectUri = model.ReturnUrl;
|
||||
result.Client = model.Client;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to redisplay the consent UI
|
||||
result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<ConsentViewModel> BuildViewModelAsync(string returnUrl, ConsentInputModel model = null)
|
||||
{
|
||||
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||
if (request != null)
|
||||
{
|
||||
return CreateConsentViewModel(model, returnUrl, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("No consent request matching request: {0}", returnUrl);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConsentViewModel CreateConsentViewModel(
|
||||
ConsentInputModel model, string returnUrl,
|
||||
AuthorizationRequest request)
|
||||
{
|
||||
var vm = new ConsentViewModel
|
||||
{
|
||||
RememberConsent = model?.RememberConsent ?? true,
|
||||
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
|
||||
Description = model?.Description,
|
||||
|
||||
ReturnUrl = returnUrl,
|
||||
ClientName = model.ClientName,
|
||||
ClientUrl = model.ClientUri,
|
||||
ClientLogoUrl = model.LogoUri,
|
||||
AllowRememberConsent = model.AllowRememberConsent
|
||||
};
|
||||
|
||||
vm.IdentityScopes = model.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
|
||||
|
||||
var apiScopes = new List<ScopeViewModel>();
|
||||
foreach(var parsedScope in model.ValidatedResources.ParsedScopes.Scopes)
|
||||
{
|
||||
var apiScope = model.ValidatedResources.Resources.FindApiScope(parsedScope);
|
||||
if (apiScope != null)
|
||||
{
|
||||
var scopeVm = CreateScopeViewModel(model.ValidatedResources.ParsedScopes, apiScope, vm.ScopesConsented.Contains(model.ValidatedResources.ParsedScopes.RawValue) || model == null);
|
||||
apiScopes.Add(scopeVm);
|
||||
}
|
||||
}
|
||||
if (ConsentOptions.EnableOfflineAccess && model.ValidatedResources.Resources.OfflineAccess)
|
||||
{
|
||||
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
|
||||
}
|
||||
vm.ApiScopes = apiScopes;
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Value = identity.Name,
|
||||
DisplayName = identity.DisplayName ?? identity.Name,
|
||||
Description = identity.Description,
|
||||
Emphasize = identity.Emphasize,
|
||||
Required = identity.Required,
|
||||
Checked = check || identity.Required
|
||||
};
|
||||
}
|
||||
|
||||
public ScopeViewModel CreateScopeViewModel(ParsedScopes parsedScope, Scope apiScope, bool check)
|
||||
{
|
||||
var displayName = apiScope.DisplayName;
|
||||
if (!String.IsNullOrWhiteSpace(parsedScope.RawValue))
|
||||
{
|
||||
displayName += ":" + parsedScope.RawValue;
|
||||
}
|
||||
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Value = parsedScope.RawValue,
|
||||
DisplayName = displayName,
|
||||
Description = apiScope.Description,
|
||||
Emphasize = apiScope.Emphasize,
|
||||
Required = apiScope.Required,
|
||||
Checked = check || apiScope.Required
|
||||
};
|
||||
}
|
||||
|
||||
private ScopeViewModel GetOfflineAccessScope(bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
DisplayName = ConsentOptions.OfflineAccessDisplayName,
|
||||
Description = ConsentOptions.OfflineAccessDescription,
|
||||
Emphasize = true,
|
||||
Checked = check
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
[SecurityHeaders]
|
||||
[Authorize]
|
||||
public class DiagnosticsController : Controller
|
||||
{
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() };
|
||||
if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString()))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync());
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
using IdentityModel;
|
||||
using IdentityServer4;
|
||||
using IdentityServer4.Events;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using IdentityServer4.Test;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using nuget_host.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace nuget_host.Controllers
|
||||
{
|
||||
[SecurityHeaders]
|
||||
[AllowAnonymous]
|
||||
public class ExternalController : Controller
|
||||
{
|
||||
private readonly TestUserStore _users;
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IClientStore _clientStore;
|
||||
private readonly ILogger<ExternalController> _logger;
|
||||
private readonly IEventService _events;
|
||||
|
||||
public ExternalController(
|
||||
IIdentityServerInteractionService interaction,
|
||||
IClientStore clientStore,
|
||||
IEventService events,
|
||||
ILogger<ExternalController> logger,
|
||||
TestUserStore users = null)
|
||||
{
|
||||
// if the TestUserStore is not in DI, then we'll just use the global users collection
|
||||
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
|
||||
_users = users ?? new TestUserStore(TestUsers.Users);
|
||||
|
||||
_interaction = interaction;
|
||||
_clientStore = clientStore;
|
||||
_logger = logger;
|
||||
_events = events;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// initiate roundtrip to external authentication provider
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult Challenge(string scheme, string returnUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/";
|
||||
|
||||
// validate returnUrl - either it is a valid OIDC URL or back to a local page
|
||||
if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false)
|
||||
{
|
||||
// user might have clicked on a malicious link - should be logged
|
||||
throw new Exception("invalid return URL");
|
||||
}
|
||||
|
||||
// start challenge and roundtrip the return URL and scheme
|
||||
var props = new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = Url.Action(nameof(Callback)),
|
||||
Items =
|
||||
{
|
||||
{ "returnUrl", returnUrl },
|
||||
{ "scheme", scheme },
|
||||
}
|
||||
};
|
||||
|
||||
return Challenge(props, scheme);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post processing of external authentication
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Callback()
|
||||
{
|
||||
// read external identity from the temporary cookie
|
||||
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||
if (result?.Succeeded != true)
|
||||
{
|
||||
throw new Exception("External authentication error");
|
||||
}
|
||||
|
||||
if (_logger.IsEnabled(LogLevel.Debug))
|
||||
{
|
||||
var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
|
||||
_logger.LogDebug("External claims: {@claims}", externalClaims);
|
||||
}
|
||||
|
||||
// lookup our user and external provider info
|
||||
var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
|
||||
if (user == null)
|
||||
{
|
||||
// this might be where you might initiate a custom workflow for user registration
|
||||
// in this sample we don't show how that would be done, as our sample implementation
|
||||
// simply auto-provisions new external user
|
||||
user = AutoProvisionUser(provider, providerUserId, claims);
|
||||
}
|
||||
|
||||
// this allows us to collect any additional claims or properties
|
||||
// for the specific protocols used and store them in the local auth cookie.
|
||||
// this is typically used to store data needed for signout from those protocols.
|
||||
var additionalLocalClaims = new List<Claim>();
|
||||
var localSignInProps = new AuthenticationProperties();
|
||||
ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
|
||||
|
||||
// issue authentication cookie for user
|
||||
var isuser = new IdentityServerUser(user.SubjectId)
|
||||
{
|
||||
DisplayName = user.Username,
|
||||
IdentityProvider = provider,
|
||||
AdditionalClaims = additionalLocalClaims
|
||||
};
|
||||
|
||||
await HttpContext.SignInAsync(isuser, localSignInProps);
|
||||
|
||||
// delete temporary cookie used during external authentication
|
||||
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||
|
||||
// retrieve return URL
|
||||
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
|
||||
|
||||
// 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?.ClientId));
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
if (context.IsNativeClient())
|
||||
{
|
||||
// The client is native, so this change in how to
|
||||
// return the response is for better UX for the end user.
|
||||
return this.LoadingPage("Redirect", returnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
||||
private (TestUser user, string provider, string providerUserId, IEnumerable<Claim> claims) FindUserFromExternalProvider(AuthenticateResult result)
|
||||
{
|
||||
var externalUser = result.Principal;
|
||||
|
||||
// try to determine the unique id of the external user (issued by the provider)
|
||||
// the most common claim type for that are the sub claim and the NameIdentifier
|
||||
// depending on the external provider, some other claim type might be used
|
||||
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
|
||||
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
|
||||
throw new Exception("Unknown userid");
|
||||
|
||||
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
|
||||
var claims = externalUser.Claims.ToList();
|
||||
claims.Remove(userIdClaim);
|
||||
|
||||
var provider = result.Properties.Items["scheme"];
|
||||
var providerUserId = userIdClaim.Value;
|
||||
|
||||
// find external user
|
||||
var user = _users.FindByExternalProvider(provider, providerUserId);
|
||||
|
||||
return (user, provider, providerUserId, claims);
|
||||
}
|
||||
|
||||
private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable<Claim> claims)
|
||||
{
|
||||
var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList());
|
||||
return user;
|
||||
}
|
||||
|
||||
// if the external login is OIDC-based, there are certain things we need to preserve to make logout work
|
||||
// this will be different for WS-Fed, SAML2p or other protocols
|
||||
private void ProcessLoginCallback(AuthenticateResult externalResult, List<Claim> localClaims, AuthenticationProperties localSignInProps)
|
||||
{
|
||||
// if the external system sent a session id claim, copy it over
|
||||
// so we can use it for single sign-out
|
||||
var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
|
||||
if (sid != null)
|
||||
{
|
||||
localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
|
||||
}
|
||||
|
||||
// if the external provider issued an id_token, we'll keep it for signout
|
||||
var idToken = externalResult.Properties.GetTokenValue("id_token");
|
||||
if (idToken != null)
|
||||
{
|
||||
localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
// 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.Services;
|
||||
using IdentityServer4.Stores;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using IdentityServer4.Events;
|
||||
using IdentityServer4.Extensions;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// This sample controller allows a user to revoke grants given to clients
|
||||
/// </summary>
|
||||
[SecurityHeaders]
|
||||
[Authorize]
|
||||
public class GrantsController : Controller
|
||||
{
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
private readonly IClientStore _clients;
|
||||
private readonly IResourceStore _resources;
|
||||
private readonly IEventService _events;
|
||||
|
||||
public GrantsController(IIdentityServerInteractionService interaction,
|
||||
IClientStore clients,
|
||||
IResourceStore resources,
|
||||
IEventService events)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_clients = clients;
|
||||
_resources = resources;
|
||||
_events = events;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show list of grants
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View("Index", await BuildViewModelAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle postback to revoke a client
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Revoke(string clientId)
|
||||
{
|
||||
await _interaction.RevokeUserConsentAsync(clientId);
|
||||
await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId));
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private async Task<GrantsViewModel> BuildViewModelAsync()
|
||||
{
|
||||
var grants = await _interaction.GetAllUserConsentsAsync();
|
||||
|
||||
var list = new List<GrantViewModel>();
|
||||
foreach(var grant in grants)
|
||||
{
|
||||
var client = await _clients.FindClientByIdAsync(grant.ClientId);
|
||||
if (client != null)
|
||||
{
|
||||
var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes);
|
||||
|
||||
var item = new GrantViewModel()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
ClientName = client.ClientName ?? client.ClientId,
|
||||
ClientLogoUrl = client.LogoUri,
|
||||
ClientUrl = client.ClientUri,
|
||||
Description = client.Description,
|
||||
Created = grant.CreationTime,
|
||||
Expires = grant.Expiration,
|
||||
IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(),
|
||||
ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray()
|
||||
};
|
||||
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return new GrantsViewModel
|
||||
{
|
||||
Grants = list
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// 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 nuget_host.Models
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// 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;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class ProcessConsentResult
|
||||
{
|
||||
public bool IsRedirect => RedirectUri != null;
|
||||
public string RedirectUri { get; set; }
|
||||
public Client Client { get; set; }
|
||||
|
||||
public bool ShowView => ViewModel != null;
|
||||
public ConsentViewModel ViewModel { get; set; }
|
||||
|
||||
public bool HasValidationError => ValidationError != null;
|
||||
public string ValidationError { get; set; }
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
// 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 nuget_host.Models
|
||||
{
|
||||
public class ValidatedResources
|
||||
{
|
||||
public Resources Resources { get; set; }
|
||||
public ParsedScopes ParsedScopes { get; internal set; }
|
||||
public bool OfflineAccess { get; internal set; }
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
// 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.
|
||||
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
|
||||
public class DeviceAuthorizationInputModel : ConsentInputModel
|
||||
{
|
||||
public string UserCode { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// 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.
|
||||
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class DeviceAuthorizationViewModel : ConsentViewModel
|
||||
{
|
||||
public string UserCode { get; set; }
|
||||
public bool ConfirmUserCode { get; set; }
|
||||
}
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityModel.Client;
|
||||
using IdentityServer4.Configuration;
|
||||
using IdentityServer4.Events;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using IdentityServer4.Validation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using nuget_host.OAuth;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
[Authorize]
|
||||
[SecurityHeaders]
|
||||
public class DeviceController : Controller
|
||||
{
|
||||
private readonly IDeviceFlowInteractionService _interaction;
|
||||
private readonly IEventService _events;
|
||||
private readonly IOptions<IdentityServerOptions> _options;
|
||||
private readonly ILogger<DeviceController> _logger;
|
||||
|
||||
public DeviceController(
|
||||
IDeviceFlowInteractionService interaction,
|
||||
IEventService eventService,
|
||||
IOptions<IdentityServerOptions> options,
|
||||
ILogger<DeviceController> logger)
|
||||
{
|
||||
_interaction = interaction;
|
||||
_events = eventService;
|
||||
_options = options;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter;
|
||||
string userCode = Request.Query[userCodeParamName];
|
||||
if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture");
|
||||
|
||||
var vm = await BuildViewModelAsync(userCode);
|
||||
if (vm == null) return View("Error");
|
||||
|
||||
vm.ConfirmUserCode = true;
|
||||
return View("UserCodeConfirmation", vm);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> UserCodeCapture(string userCode)
|
||||
{
|
||||
var vm = await BuildViewModelAsync(userCode);
|
||||
if (vm == null) return View("Error");
|
||||
|
||||
return View("UserCodeConfirmation", vm);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Callback(DeviceAuthorizationInputModel model)
|
||||
{
|
||||
if (model == null) throw new ArgumentNullException(nameof(model));
|
||||
|
||||
var result = await ProcessConsent(model);
|
||||
if (result.HasValidationError) return View("Error");
|
||||
|
||||
return View("Success");
|
||||
}
|
||||
|
||||
private async Task<ProcessConsentResult> ProcessConsent(DeviceAuthorizationInputModel model)
|
||||
{
|
||||
var result = new ProcessConsentResult();
|
||||
|
||||
var request = await _interaction.GetAuthorizationContextAsync(model.UserCode);
|
||||
if (request == null) return result;
|
||||
|
||||
ConsentResponse grantedConsent = null;
|
||||
|
||||
// user clicked 'no' - send back the standard 'access_denied' response
|
||||
if (model.Button == "no")
|
||||
{
|
||||
grantedConsent = ConsentResponse.Denied;
|
||||
|
||||
// emit event
|
||||
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested));
|
||||
}
|
||||
// user clicked 'yes' - validate the data
|
||||
else if (model.Button == "yes")
|
||||
{
|
||||
// if the user consented to some scope, build the response model
|
||||
if (model.ScopesConsented != null && model.ScopesConsented.Any())
|
||||
{
|
||||
var scopes = model.ScopesConsented;
|
||||
if (ConsentOptions.EnableOfflineAccess == false)
|
||||
{
|
||||
scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
|
||||
}
|
||||
|
||||
grantedConsent = new ConsentResponse
|
||||
{
|
||||
RememberConsent = model.RememberConsent,
|
||||
ScopesConsented = scopes.ToArray()
|
||||
};
|
||||
|
||||
// emit event
|
||||
await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
|
||||
}
|
||||
|
||||
if (grantedConsent != null)
|
||||
{
|
||||
// communicate outcome of consent back to identityserver
|
||||
await _interaction.HandleRequestAsync(model.UserCode, grantedConsent);
|
||||
|
||||
// indicate that's it ok to redirect back to authorization endpoint
|
||||
result.RedirectUri = model.ReturnUrl;
|
||||
result.Client = model.Client;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need to redisplay the consent UI
|
||||
result.ViewModel = await BuildViewModelAsync(model.UserCode, model);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<DeviceAuthorizationViewModel> BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null)
|
||||
{
|
||||
var request = await _interaction.GetAuthorizationContextAsync(userCode);
|
||||
if (request != null)
|
||||
{
|
||||
return CreateConsentViewModel(userCode, model, request);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private DeviceAuthorizationViewModel CreateConsentViewModel(
|
||||
string userCode, DeviceAuthorizationInputModel model,
|
||||
DeviceFlowAuthorizationRequest request)
|
||||
{
|
||||
var vm = new DeviceAuthorizationViewModel
|
||||
{
|
||||
UserCode = userCode,
|
||||
Description = model?.Description,
|
||||
|
||||
RememberConsent = model?.RememberConsent ?? true,
|
||||
ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),
|
||||
|
||||
ClientName = model?.ClientName,
|
||||
ClientUrl = model?.ClientUri,
|
||||
ClientLogoUrl = model?.LogoUri,
|
||||
AllowRememberConsent = model != null && model.AllowRememberConsent
|
||||
};
|
||||
|
||||
vm.IdentityScopes = model.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray();
|
||||
|
||||
var apiScopes = new List<ScopeViewModel>();
|
||||
foreach (var parsedScope in model.ValidatedResources.ParsedScopes.Scopes)
|
||||
{
|
||||
var apiScope = model.ValidatedResources.Resources.FindApiScope(parsedScope);
|
||||
if (apiScope != null)
|
||||
{
|
||||
var vreq = CreateValidatedRequest(request, apiScope);
|
||||
|
||||
var scopeVm = CreateScopeViewModel(apiScope,vreq);
|
||||
apiScopes.Add(scopeVm);
|
||||
}
|
||||
}
|
||||
if (ConsentOptions.EnableOfflineAccess && model.ValidatedResources.Resources.OfflineAccess)
|
||||
{
|
||||
apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null));
|
||||
}
|
||||
vm.ApiScopes = apiScopes;
|
||||
|
||||
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
|
||||
{
|
||||
Value = identity.Name,
|
||||
DisplayName = identity.DisplayName ?? identity.Name,
|
||||
Description = identity.Description,
|
||||
Emphasize = identity.Emphasize,
|
||||
Required = identity.Required,
|
||||
Checked = check || identity.Required
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
Description = apiScope.Description,
|
||||
Emphasize = parsedScopeValue.Emphasize,
|
||||
Required = apiScope.Required,
|
||||
Checked = check || apiScope.Required
|
||||
};
|
||||
}
|
||||
private ScopeViewModel GetOfflineAccessScope(bool check)
|
||||
{
|
||||
return new ScopeViewModel
|
||||
{
|
||||
Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
DisplayName = ConsentOptions.OfflineAccessDisplayName,
|
||||
Description = ConsentOptions.OfflineAccessDescription,
|
||||
Emphasize = true,
|
||||
Checked = check
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// 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 IdentityModel;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class DiagnosticsViewModel
|
||||
{
|
||||
public DiagnosticsViewModel(AuthenticateResult result)
|
||||
{
|
||||
AuthenticateResult = result;
|
||||
|
||||
if (result.Properties.Items.ContainsKey("client_list"))
|
||||
{
|
||||
var encoded = result.Properties.Items["client_list"];
|
||||
var bytes = Base64Url.Decode(encoded);
|
||||
var value = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
Clients = JsonConvert.DeserializeObject<string[]>(value);
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticateResult AuthenticateResult { get; }
|
||||
public IEnumerable<string> Clients { get; } = new List<string>();
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
|
||||
public ErrorViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public ErrorViewModel(string error)
|
||||
{
|
||||
Error = new ErrorMessage { Error = error };
|
||||
}
|
||||
|
||||
public ErrorMessage Error { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
using IdentityServer4.Models;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class RepositoryIdentityResource : IdentityResource
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
@model ConsentViewModel
|
||||
|
||||
<div class="page-consent">
|
||||
<div class="lead">
|
||||
@if (Model.ClientLogoUrl != null)
|
||||
{
|
||||
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
|
||||
}
|
||||
<h1>
|
||||
@Model.ClientName
|
||||
<small class="text-muted">is requesting your permission</small>
|
||||
</h1>
|
||||
<p>Uncheck the permissions you do not wish to grant.</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<partial name="_ValidationSummary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form asp-action="Index">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
@if (Model.IdentityScopes.Any())
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
Personal Information
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var scope in Model.IdentityScopes)
|
||||
{
|
||||
<partial name="_ScopeListItem" model="@scope" />
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.ApiScopes.Any())
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-tasks"></span>
|
||||
Application Access
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var scope in Model.ApiScopes)
|
||||
{
|
||||
<partial name="_ScopeListItem" model="scope" />
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-tasks"></span>
|
||||
Description
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input class="form-control" placeholder="Description or name of device" asp-for="Description" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.AllowRememberConsent)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" asp-for="RememberConsent">
|
||||
<label class="form-check-label" asp-for="RememberConsent">
|
||||
<strong>Remember My Decision</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
|
||||
<button name="button" value="no" class="btn btn-secondary">No, Do Not Allow</button>
|
||||
</div>
|
||||
<div class="col-sm-4 col-lg-auto">
|
||||
@if (Model.ClientUrl != null)
|
||||
{
|
||||
<a class="btn btn-outline-info" href="@Model.ClientUrl">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
<strong>@Model.ClientName</strong>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,7 +0,0 @@
|
||||
|
||||
<div class="page-device-success">
|
||||
<div class="lead">
|
||||
<h1>Success</h1>
|
||||
<p>You have successfully authorized the device</p>
|
||||
</div>
|
||||
</div>
|
@ -1,23 +0,0 @@
|
||||
@model string
|
||||
|
||||
<div class="page-device-code">
|
||||
<div class="lead">
|
||||
<h1>User Code</h1>
|
||||
<p>Please enter the code displayed on your device.</p>
|
||||
</div>
|
||||
|
||||
<partial name="_ValidationSummary" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<form asp-action="UserCodeCapture">
|
||||
<div class="form-group">
|
||||
<label for="userCode">User Code:</label>
|
||||
<input class="form-control" for="userCode" name="userCode" autofocus />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" name="button">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,108 +0,0 @@
|
||||
@model DeviceAuthorizationViewModel
|
||||
|
||||
<div class="page-device-confirmation">
|
||||
<div class="lead">
|
||||
@if (Model.ClientLogoUrl != null)
|
||||
{
|
||||
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
|
||||
}
|
||||
<h1>
|
||||
@Model.ClientName
|
||||
<small class="text-muted">is requesting your permission</small>
|
||||
</h1>
|
||||
@if (Model.ConfirmUserCode)
|
||||
{
|
||||
<p>Please confirm that the authorization request quotes the code: <strong>@Model.UserCode</strong>.</p>
|
||||
}
|
||||
<p>Uncheck the permissions you do not wish to grant.</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<partial name="_ValidationSummary" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form asp-action="Callback">
|
||||
<input asp-for="UserCode" type="hidden" value="@Model.UserCode" />
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
@if (Model.IdentityScopes.Any())
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
Personal Information
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var scope in Model.IdentityScopes)
|
||||
{
|
||||
<partial name="_ScopeListItem" model="@scope" />
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.ApiScopes.Any())
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-tasks"></span>
|
||||
Application Access
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var scope in Model.ApiScopes)
|
||||
{
|
||||
<partial name="_ScopeListItem" model="scope" />
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="glyphicon glyphicon-tasks"></span>
|
||||
Description
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input class="form-control" placeholder="Description or name of device" asp-for="Description" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.AllowRememberConsent)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" asp-for="RememberConsent">
|
||||
<label class="form-check-label" asp-for="RememberConsent">
|
||||
<strong>Remember My Decision</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
|
||||
<button name="button" value="no" class="btn btn-secondary">No, Do Not Allow</button>
|
||||
</div>
|
||||
<div class="col-sm-4 col-lg-auto">
|
||||
@if (Model.ClientUrl != null)
|
||||
{
|
||||
<a class="btn btn-outline-info" href="@Model.ClientUrl">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
<strong>@Model.ClientName</strong>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,64 +0,0 @@
|
||||
@model DiagnosticsViewModel
|
||||
|
||||
<div class="diagnostics-page">
|
||||
<div class="lead">
|
||||
<h1>Authentication Cookie</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Claims</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl>
|
||||
@foreach (var claim in Model.AuthenticateResult.Principal.Claims)
|
||||
{
|
||||
<dt>@claim.Type</dt>
|
||||
<dd>@claim.Value</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Properties</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl>
|
||||
@foreach (var prop in Model.AuthenticateResult.Properties.Items)
|
||||
{
|
||||
<dt>@prop.Key</dt>
|
||||
<dd>@prop.Value</dd>
|
||||
}
|
||||
@if (Model.Clients.Any())
|
||||
{
|
||||
<dt>Clients</dt>
|
||||
<dd>
|
||||
@{
|
||||
var clients = Model.Clients.ToArray();
|
||||
for(var i = 0; i < clients.Length; i++)
|
||||
{
|
||||
<text>@clients[i]</text>
|
||||
if (i < clients.Length - 1)
|
||||
{
|
||||
<text>, </text>
|
||||
}
|
||||
}
|
||||
}
|
||||
</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
@ -1,33 +1,11 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
|
||||
}
|
||||
ViewData["Title"] = "Home Page";}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
<h1>
|
||||
<img src="~/icon.jpg">
|
||||
Welcome to IdentityServer4
|
||||
<small class="text-muted">(version @version)</small>
|
||||
Welcome to Nuget host
|
||||
</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
IdentityServer publishes a
|
||||
<a href="~/.well-known/openid-configuration">discovery document</a>
|
||||
where you can find metadata and links to all the endpoints, key material, etc.
|
||||
</li>
|
||||
<li>
|
||||
Click <a href="~/diagnostics">here</a> to see the claims for your current session.
|
||||
</li>
|
||||
<li>
|
||||
Click <a href="~/grants">here</a> to manage your stored grants.
|
||||
</li>
|
||||
<li>
|
||||
Here are links to the
|
||||
<a href="https://github.com/identityserver/IdentityServer4">source code repository</a>,
|
||||
and <a href="https://github.com/IdentityServer/IdentityServer4/tree/main/samples">ready to use samples</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -1,40 +0,0 @@
|
||||
@model ErrorViewModel
|
||||
|
||||
@{
|
||||
var error = Model?.Error?.Error;
|
||||
var errorDescription = Model?.Error?.ErrorDescription;
|
||||
var request_id = Model?.Error?.RequestId;
|
||||
}
|
||||
|
||||
<div class="error-page">
|
||||
<div class="lead">
|
||||
<h1>Error</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="alert alert-danger">
|
||||
Sorry, there was an error
|
||||
|
||||
@if (error != null)
|
||||
{
|
||||
<strong>
|
||||
<em>
|
||||
: @error
|
||||
</em>
|
||||
</strong>
|
||||
|
||||
if (errorDescription != null)
|
||||
{
|
||||
<div>@errorDescription</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (request_id != null)
|
||||
{
|
||||
<div class="request-id">Request Id: @request_id</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,34 +0,0 @@
|
||||
@model ScopeViewModel
|
||||
|
||||
<li class="list-group-item">
|
||||
<label>
|
||||
<input class="consent-scopecheck"
|
||||
type="checkbox"
|
||||
name="ScopesConsented"
|
||||
id="scopes_@Model.Value"
|
||||
value="@Model.Value"
|
||||
checked="@Model.Checked"
|
||||
disabled="@Model.Required" />
|
||||
@if (Model.Required)
|
||||
{
|
||||
<input type="hidden"
|
||||
name="ScopesConsented"
|
||||
value="@Model.Value" />
|
||||
}
|
||||
<strong>@Model.DisplayName</strong>
|
||||
@if (Model.Emphasize)
|
||||
{
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
}
|
||||
</label>
|
||||
@if (Model.Required)
|
||||
{
|
||||
<span><em>(required)</em></span>
|
||||
}
|
||||
@if (Model.Description != null)
|
||||
{
|
||||
<div class="consent-description">
|
||||
<label for="scopes_@Model.Value">@Model.Description</label>
|
||||
</div>
|
||||
}
|
||||
</li>
|
Loading…
Reference in New Issue