<|start_filename|>samples/Server/Pages/Account/_ViewImports.cshtml<|end_filename|>
@using Server.Models
@namespace Server.Pages.Account
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers


<|start_filename|>src/openiddict/OpenIddict.UI.Infrastructure/Services/ApplicationService.cs<|end_filename|>
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Suite.Core;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace tomware.OpenIddict.UI.Infrastructure
{
  public class ApplicationService : IApplicationService
  {
    private readonly IApplicationRepository _repository;
    private readonly OpenIddictApplicationManager<OpenIddictEntityFrameworkCoreApplication> _manager;

    public ApplicationService(
      IApplicationRepository repository,
      OpenIddictApplicationManager<OpenIddictEntityFrameworkCoreApplication> manager
    )
    {
      _repository = repository
        ?? throw new ArgumentNullException(nameof(repository));
      _manager = manager
        ?? throw new ArgumentNullException(nameof(manager));
    }

    public async Task<IEnumerable<ApplicationInfo>> GetApplicationsAsync()
    {
      var items = await _repository.ListAsync(new AllApplications());

      return items.Select(x => ToListInfo(x));
    }

    public async Task<ApplicationInfo> GetAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var entity = await _manager.FindByIdAsync(id);

      return entity != null ? ToInfo(entity) : null;
    }

    public async Task<string> CreateAsync(ApplicationParam model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));

      var entity = await _manager.FindByClientIdAsync(model.ClientId);
      if (entity == null)
      {
        // create new entity
        var newEntity = new OpenIddictEntityFrameworkCoreApplication
        {
          ClientId = model.ClientId,
          DisplayName = model.DisplayName,
          Type = model.Type
        };

        HandleCustomProperties(model, newEntity);

        await _manager.CreateAsync(newEntity, model.ClientSecret);

        return newEntity.Id;
      }

      // update existing entity
      model.Id = entity.Id;
      await UpdateAsync(model);

      return entity.Id;
    }

    public async Task UpdateAsync(ApplicationParam model)
    {
      if (string.IsNullOrWhiteSpace(model.Id))
        throw new InvalidOperationException(nameof(model.Id));

      var entity = await _manager.FindByIdAsync(model.Id);

      SimpleMapper.Map<ApplicationParam, OpenIddictEntityFrameworkCoreApplication>(model, entity);

      HandleCustomProperties(model, entity);

      await _manager.UpdateAsync(entity, entity.ClientSecret);
    }

    public async Task DeleteAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var entity = await _manager.FindByIdAsync(id);

      await _manager.DeleteAsync(entity);
    }

    private static ApplicationInfo ToListInfo(OpenIddictEntityFrameworkCoreApplication entity)
    {
      return SimpleMapper.From<OpenIddictEntityFrameworkCoreApplication, ApplicationInfo>(entity);
    }

    private static ApplicationInfo ToInfo(OpenIddictEntityFrameworkCoreApplication entity)
    {
      var info = SimpleMapper
        .From<OpenIddictEntityFrameworkCoreApplication, ApplicationInfo>(entity);

      info.RequireConsent = entity.ConsentType == ConsentTypes.Explicit;
      info.Permissions = entity.Permissions != null
        ? JsonSerializer.Deserialize<List<string>>(entity.Permissions)
        : new List<string>();
      info.RedirectUris = entity.RedirectUris != null
        ? JsonSerializer.Deserialize<List<string>>(entity.RedirectUris)
        : new List<string>();
      info.PostLogoutRedirectUris = entity.PostLogoutRedirectUris != null
        ? JsonSerializer.Deserialize<List<string>>(entity.PostLogoutRedirectUris)
        : new List<string>();
      info.RequirePkce = entity.Requirements != null && JsonSerializer
        .Deserialize<List<string>>(entity.Requirements)
        .Contains(Requirements.Features.ProofKeyForCodeExchange);

      return info;
    }

    private static void HandleCustomProperties(
      ApplicationParam model,
      OpenIddictEntityFrameworkCoreApplication entity
    )
    {
      entity.ConsentType = model.RequireConsent ? ConsentTypes.Explicit : ConsentTypes.Implicit;
      entity.Permissions = JsonSerializer.Serialize(model.Permissions);
      entity.RedirectUris = JsonSerializer.Serialize(model.RedirectUris);
      entity.PostLogoutRedirectUris = JsonSerializer.Serialize(model.PostLogoutRedirectUris);
      entity.Requirements = model.RequirePkce ? JsonSerializer.Serialize(new List<string> {
        Requirements.Features.ProofKeyForCodeExchange
      }) : null;
    }
  }
}


<|start_filename|>src/identity/OpenIddict.UI.Identity.Infrastructure/Data/OpenIddictUIIdentityContext.cs<|end_filename|>
using Microsoft.EntityFrameworkCore;
using System;
using tomware.OpenIddict.UI.Identity.Core;

namespace tomware.OpenIddict.UI.Identity.Infrastructure
{
  public class OpenIddictUIIdentityStoreOptions
  {
    public Action<DbContextOptionsBuilder> OpenIddictUIIdentityContext { get; set; }
    public Action<IServiceProvider, DbContextOptionsBuilder> ResolveDbContextOptions { get; set; }
  }

  public interface IOpenIddictUIIdentityContext
  {
    DbSet<ClaimType> ClaimTypes { get; set; }
  }

  public class OpenIddictUIIdentityContext : OpenIddictUIIdentityContext<OpenIddictUIIdentityContext>
  {
    public OpenIddictUIIdentityContext(DbContextOptions<OpenIddictUIIdentityContext> options)
      : base(options)
    { }
  }

  public class OpenIddictUIIdentityContext<TContext> : DbContext, IOpenIddictUIIdentityContext
    where TContext : DbContext, IOpenIddictUIIdentityContext
  {
    public OpenIddictUIIdentityContext(DbContextOptions<TContext> options)
      : base(options)
    { }

    public DbSet<ClaimType> ClaimTypes { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
      base.OnModelCreating(builder);

      builder.ApplyConfiguration(new ClaimTypeEntityConfiguration());
    }
  }
}


<|start_filename|>src/identity/OpenIddict.UI.Identity.Core/Interfaces/IClaimTypeService.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace tomware.OpenIddict.UI.Identity.Core
{
  public interface IClaimTypeService
  {
    Task<IEnumerable<ClaimTypeInfo>> GetClaimTypesAsync();

    Task<ClaimTypeInfo> GetAsync(Guid id);

    Task<Guid> CreateAsync(ClaimTypeParam model);

    Task UpdateAsync(ClaimTypeParam model);

    Task DeleteAsync(Guid id);
  }
}

<|start_filename|>samples/Server/Pages/Shared/_NavItemsPartial.cshtml<|end_filename|>
@inject SignInManager<ApplicationUser> SignInManager

<li class="nav-item">
  <a class="nav-link text-dark" asp-page="/Index">Home</a>
</li>
@if (!SignInManager.IsSignedIn(User))
{
  <li class="nav-item">
    <a class="nav-link text-dark" asp-page="/account/login">Login</a>
  </li>
}
else {
  <li class="nav-item">
    <a class="nav-link text-dark" asp-page="/account/logout">Logout</a>
  </li>
  <li class="nav-item">
    <a class="nav-link text-dark" asp-page="/account/manage/index">@User.Identity.Name</a>
  </li>
}

<|start_filename|>src/openiddict/OpenIddict.UI.Infrastructure/Interfaces/IScopeRepository.cs<|end_filename|>
using OpenIddict.EntityFrameworkCore.Models;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Infrastructure
{
  public interface IScopeRepository 
    : IAsyncRepository<OpenIddictEntityFrameworkCoreScope, string>
  { }
}

<|start_filename|>tests/Integration/ScopeApiTest.cs<|end_filename|>
using Server;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Api;
using tomware.OpenIddict.UI.Tests.Helpers;
using Xunit;

namespace tomware.OpenIddict.UI.Tests.Integration
{
  public class ScopeApiTest : IntegrationContext
  {
    private const string TEST_SCOPE = "test_scope";

    public ScopeApiTest(IntegrationApplicationFactory<Startup> fixture)
      : base(fixture)
    { }

    [Theory]
    [InlineData("/api/scopes", HttpVerb.Get)]
    [InlineData("/api/scopes/01D7ACA3-575C-4E60-859F-DB95B70F8190", HttpVerb.Get)]
    [InlineData("/api/scopes", HttpVerb.Post)]
    // [InlineData("/api/scopes", HttpVerb.Put)]
    // [InlineData("/api/scopes/01D7ACA3-575C-4E60-859F-DB95B70F8190", HttpVerb.Delete)]
    public async Task IsNotAuthenticated_ReturnsUnauthorized(
      string endpoint,
      HttpVerb verb
    )
    {
      var authorized = false;

      // Arrange
      HttpResponseMessage response = null;
      // Act
      switch (verb)
      {
        case HttpVerb.Post:
          response = await PostAsync(endpoint, new ScopeViewModel(), authorized);
          break;
        case HttpVerb.Put:
          response = await PutAsync(endpoint, new ScopeViewModel(), authorized);
          break;
        case HttpVerb.Delete:
          response = await DeleteAsync(endpoint, authorized);
          break;
        case HttpVerb.Get:
          response = await GetAsync(endpoint, authorized);
          break;
      }

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    public async Task GetClaimTypesAsync_ReturnsAList()
    {
      // Arrange
      var endpoint = "/api/scopes";

      // Act
      var response = await GetAsync(endpoint);

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<List<ScopeViewModel>>();
      Assert.NotNull(model);
      Assert.True(model.Count >= 0);
    }

    [Fact]
    public async Task GetScopeNamesAsync_ReturnsAList()
    {
      // Arrange
      var endpoint = "/api/scopes/names";

      // Act
      var response = await GetAsync(endpoint);

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<List<string>>();
      Assert.NotNull(model);
      Assert.True(model.Count >= 0);
    }

    [Fact]
    public async Task CreateAsync_ScopeCreated()
    {
      // Arrange
      var endpoint = "/api/scopes";

      // Act
      var response = await PostAsync(endpoint, new ScopeViewModel
      {
        Name = TEST_SCOPE,
        DisplayName = "displayname",
        Description = "description"
      });

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.Created, response.StatusCode);

      var id = await response.Content.ReadAsJson<string>();
      Assert.NotNull(id);
    }

    [Fact]
    public async Task GetAsync_RoleReceived()
    {
      // Arrange
      var endpoint = "/api/scopes";
      var createResponse = await PostAsync(endpoint, new ScopeViewModel
      {
        Name = TEST_SCOPE,
        DisplayName = "displayname",
        Description = "description",
        Resources = new List<string>
        {
          "resource1",
          "resource2"
        }
      });
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await GetAsync($"{endpoint}/{id}");

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<ScopeViewModel>();

      Assert.NotNull(model);
      Assert.Equal(id, model.Id);
      Assert.Equal(TEST_SCOPE, model.Name);
      Assert.Equal("displayname", model.DisplayName);
      Assert.Equal("description", model.Description);
      Assert.Equal(2, model.Resources.Count);
      Assert.Equal("resource1", model.Resources[0]);
      Assert.Equal("resource2", model.Resources[1]);
    }

    [Fact]
    public async Task UpdateAsync_ScopeUpdated()
    {
      // Arrange
      var endpoint = "/api/scopes";
      var createResponse = await PostAsync(endpoint, new ScopeViewModel
      {
        Name = TEST_SCOPE,
        DisplayName = "displayname",
        Description = "description"
      });
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await PutAsync(endpoint, new ScopeViewModel
      {
        Id = id,
        Name = TEST_SCOPE,
        DisplayName = "displayname updated",
        Description = "description updated"
      });

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
    }

    [Fact]
    public async Task DeleteAsync_ScopeDeleted()
    {
      // Arrange
      var endpoint = "/api/scopes";
      var createResponse = await PostAsync(endpoint, new ScopeViewModel
      {
        Name = TEST_SCOPE,
        DisplayName = "displayname",
        Description = "description"
      });
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await DeleteAsync($"{endpoint}/{id}");

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
    }
  }
}

<|start_filename|>samples/Server/Pages/Account/AccessDenied.cshtml<|end_filename|>
﻿@page
@model AccessDeniedModel

@{
  ViewData["Title"] = "Access Denied";
}

<header>
  <h1>@ViewData["Title"]</h1>
  <p>You don't have access to this page!</p>
</header>


<|start_filename|>src/identity/OpenIddict.UI.Identity.Infrastructure/Data/Configuration/ClaimTypeEntityConfiguration.cs<|end_filename|>
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using tomware.OpenIddict.UI.Identity.Core;

namespace tomware.OpenIddict.UI.Identity.Infrastructure
{
  public class ClaimTypeEntityConfiguration : IEntityTypeConfiguration<ClaimType>
  {
    public void Configure(EntityTypeBuilder<ClaimType> builder)
    {
      // table
      builder.ToTable("ClaimType");

      // columns
      builder.HasKey(x => x.Id);
      builder.HasIndex(x => x.Name).IsUnique();
      builder.Property(x => x.Name).IsRequired();
      builder.Property(x => x.ClaimValueType).IsRequired();
    }
  }
}

<|start_filename|>src/identity/OpenIddict.UI.Identity.Api/Role/RoleViewModel.cs<|end_filename|>
using System.ComponentModel.DataAnnotations;

namespace tomware.OpenIddict.UI.Identity.Api
{
  public class RoleViewModel
  {
    public string Id { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string Name { get; set; }
  }
}


<|start_filename|>samples/Server/Pages/Account/Manage/_ViewImports.cshtml<|end_filename|>
﻿@using Server.Pages.Account.Manage


<|start_filename|>tests/Unit/AccountControllerTest.cs<|end_filename|>
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Moq;
using tomware.OpenIddict.UI.Identity.Api;
using Xunit;

namespace tomware.OpenIddict.UI.Tests.Unit
{
  public class AccountControllerTest
  {
    [Fact]
    public async Task Register_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.Register(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }


    [Fact]
    public async Task ChangePassword_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.ChangePassword(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task GetUser_WithNullId_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.GetUser(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task UpdateAsync_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.UpdateAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task UpdateAsync_WithInvalidModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();
      var model = new UserViewModel
      {
        Id = Guid.NewGuid().ToString(), // required
        UserName = "UserName" // required
        // Email // required
      };

      // ModelState must be manually adjusted
      controller.ModelState.AddModelError(string.Empty, "Email required!");

      // Act
      var result = await controller.UpdateAsync(model);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestObjectResult>(result);
    }

    [Fact]
    public async Task DeleteAsync_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.DeleteAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    private static AccountController GetController()
    {
      var service = new Mock<IAccountApiService>();

      return new AccountController(service.Object);
    }
  }
}

<|start_filename|>tests/Unit/RoleControllerTest.cs<|end_filename|>
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Moq;
using tomware.OpenIddict.UI.Identity.Api;
using Xunit;

namespace tomware.OpenIddict.UI.Tests.Unit
{
  public class RoleControllerTest
  {
    [Fact]
    public async Task GetAsync_WithNullId_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.GetAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task GetAsync_WithNotExistingId_ReturnsNotFound()
    {
      // Arrange
      var serviceMock = new Mock<IRoleService>();
      var controller = GetController(serviceMock);
      var id = "id";

      serviceMock
        .Setup(r => r.GetAsync(id))
        .Returns(Task.FromResult<RoleViewModel>(null));

      // Act
      var result = await controller.GetAsync(id);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<NotFoundResult>(result);
    }

    [Fact]
    public async Task CreateAsync_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.CreateAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task CreateAsync_WithInvalidModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();
      var model = new RoleViewModel
      {
        // Name // required
      };

      // ModelState must be manually adjusted
      controller.ModelState.AddModelError(string.Empty, "Name required!");

      // Act
      var result = await controller.CreateAsync(model);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestObjectResult>(result);
    }

    [Fact]
    public async Task UpdateAsync_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.UpdateAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    [Fact]
    public async Task UpdateAsync_WithInvalidModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();
      var model = new RoleViewModel
      {
        // Name // required
      };

      // ModelState must be manually adjusted
      controller.ModelState.AddModelError(string.Empty, "Name required!");

      // Act
      var result = await controller.UpdateAsync(model);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestObjectResult>(result);
    }

    [Fact]
    public async Task DeleteAsync_WithNullModel_ReturnsBadRequest()
    {
      // Arrange
      var controller = GetController();

      // Act
      var result = await controller.DeleteAsync(null);

      // Assert
      Assert.NotNull(result);
      Assert.IsType<BadRequestResult>(result);
    }

    private static RoleController GetController(Mock<IRoleService> service = null)
    {
      service ??= new Mock<IRoleService>();

      return new RoleController(service.Object);
    }
  }
}

<|start_filename|>samples/Server/Pages/Account/Logout.cshtml<|end_filename|>
@page

@using Microsoft.AspNetCore.Identity

@inject SignInManager<ApplicationUser> SignInManager

@model LogoutModel
@{
  ViewData["Title"] = "Logout";
}

@if (SignInManager.IsSignedIn(User))
{
  <section>
    <p>Would you like to logout of the application?</p>
    <form class="form" id="logout" method="post">
      <input type="hidden" name="logoutId" value="@Model.LogoutId" />
      <div class="form-group">
        <button type="submit">Submit</button>
      </div>
    </form>
  </section>
}
else
{
  <section>
    <p>You have successfully logged out of the application.</p>
  </section>
}

<|start_filename|>src/suite/OpenIddict.UI.Suite.Core/Interfaces/IAsyncRepository.cs<|end_filename|>
using System.Collections.Generic;
using System.Threading.Tasks;

namespace tomware.OpenIddict.UI.Suite.Core
{
  public interface IAsyncRepository<TEntity, TKey> 
    where TEntity : class
  {
    Task<TEntity> GetByIdAsync(TKey id);
    Task<IReadOnlyList<TEntity>> ListAsync(ISpecification<TEntity> spec);
    Task<TEntity> AddAsync(TEntity entity);
    Task UpdateAsync(TEntity entity);
    Task DeleteAsync(TEntity entity);
    Task<int> CountAsync(ISpecification<TEntity> spec);
  }
}


<|start_filename|>.vscode/settings.json<|end_filename|>
{
  "dotnet-test-explorer.testProjectPath": "tests",
  "editor.tabSize": 2,
  "editor.rulers": [
    100
  ],
  "omnisharp.enableRoslynAnalyzers": true,
  "TodoParser": {
    "include": ["cs, cshtml, ts"],
    "folderExclude": ["node_modules", ".vscode", ".devcontainer", ".github"],
    "only": ["samples", "src", "tests"],
    "showInProblems": false,
    "markers": ["NOTE:", "REMINDER:", "TODO:", ["FIXME", "Warning"]],
    "autoDefaultMarkers": true
  }
}

<|start_filename|>src/identity/OpenIddict.UI.Identity.Api/ClaimType/ClaimTypeViewModel.cs<|end_filename|>
using System;
using System.ComponentModel.DataAnnotations;

namespace tomware.OpenIddict.UI.Identity.Api
{
  public class ClaimTypeViewModel
  {
    public Guid? Id { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string Name { get; set; }

    public string Description { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string ClaimValueType { get; set; }
  }
}


<|start_filename|>tests/Integration/ApplicationApiTest.cs<|end_filename|>
using Server;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Api;
using tomware.OpenIddict.UI.Tests.Helpers;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace tomware.OpenIddict.UI.Tests.Integration
{
  public class ApplicationApiTest : IntegrationContext
  {
    private const string TEST_APPLICATION = "test_application";

    public ApplicationApiTest(IntegrationApplicationFactory<Startup> fixture)
      : base(fixture)
    { }

    [Theory]
    [InlineData("/api/application", HttpVerb.Get)]
    [InlineData("/api/application/01D7ACA3-575C-4E60-859F-DB95B70F8190", HttpVerb.Get)]
    [InlineData("/api/application", HttpVerb.Post)]
    // [InlineData("/api/application", HttpVerb.Put)]
    // [InlineData("/api/application/01D7ACA3-575C-4E60-859F-DB95B70F8190", HttpVerb.Delete)]
    public async Task IsNotAuthenticated_ReturnsUnauthorized(
      string endpoint,
      HttpVerb verb
    )
    {
      // Arrange
      HttpResponseMessage response = null;
      var authorized = false;

      // Act
      switch (verb)
      {
        case HttpVerb.Post:
          response = await PostAsync(endpoint, new ApplicationViewModel(), authorized);
          break;
        case HttpVerb.Put:
          response = await PutAsync(endpoint, new ApplicationViewModel(), authorized);
          break;
        case HttpVerb.Delete:
          response = await DeleteAsync(endpoint, authorized);
          break;
        case HttpVerb.Get:
          response = await GetAsync(endpoint, authorized);
          break;
      }

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    }

    [Fact]
    public async Task GetClaimTypesAsync_ReturnsAList()
    {
      // Arrange
      var endpoint = "/api/application";

      // Act
      var response = await GetAsync(endpoint);

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<List<ApplicationViewModel>>();
      Assert.NotNull(model);
      Assert.True(model.Count >= 0);
    }

    [Fact]
    public async Task CreateAsync_ApplicationCreated()
    {
      // Arrange
      var endpoint = "/api/application";

      // Act
      var response = await PostAsync(endpoint, GetViewModel());

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.Created, response.StatusCode);

      var id = await response.Content.ReadAsJson<string>();
      Assert.NotNull(id);
    }

    [Fact]
    public async Task GetAsync_ApplicationReceived()
    {
      // Arrange
      var endpoint = "/api/application";
      var createResponse = await PostAsync(endpoint, GetViewModel());
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await GetAsync($"{endpoint}/{id}");

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<ApplicationViewModel>();

      Assert.NotNull(model);
      Assert.Equal(id, model.Id);
      Assert.Equal(TEST_APPLICATION, model.ClientId);
      Assert.Equal("displayname", model.DisplayName);
      Assert.Null(model.ClientSecret);
      Assert.False(model.RequirePkce);
      Assert.False(model.RequireConsent);
      Assert.Empty(model.Permissions);
      Assert.Empty(model.RedirectUris);
      Assert.Empty(model.PostLogoutRedirectUris);
      Assert.Equal(ClientTypes.Public, model.Type);
    }

    [Fact]
    public async Task UpdateAsync_ApplicationUpdated()
    {
      // Arrange
      var endpoint = "/api/application";
      var createResponse = await PostAsync(endpoint, GetViewModel());
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await PutAsync(endpoint, GetViewModel(id));

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
    }

    [Fact]
    public async Task DeleteAsync_ApplicationDeleted()
    {
      // Arrange
      var endpoint = "/api/application";
      var createResponse = await PostAsync(endpoint, GetViewModel());
      var id = await createResponse.Content.ReadAsJson<string>();

      // Act
      var response = await DeleteAsync($"{endpoint}/{id}");

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
    }

    [Fact]
    public async Task GetOptionsAsync_ReturnsAListOfApplicationOptions()
    {
      // Arrange
      var endpoint = "/api/application/options";

      // Act
      var response = await GetAsync(endpoint);

      // Assert
      Assert.NotNull(response);
      Assert.Equal(HttpStatusCode.OK, response.StatusCode);

      var model = await response.Content.ReadAsJson<ApplicationOptionsViewModel>();
      Assert.NotNull(model);
      Assert.True(model.Types.Count == 2);
    }

    private static ApplicationViewModel GetViewModel(string id = null)
    {
      return new ApplicationViewModel
      {
        Id = id,
        ClientId = TEST_APPLICATION,
        DisplayName = "displayname",
        // ClientSecret = "client<PASSWORD>", // only when Type is confidential
        RequirePkce = false,
        RequireConsent = false,
        Permissions = new List<string>(),
        RedirectUris = new List<string>(),
        PostLogoutRedirectUris = new List<string>(),
        Type = ClientTypes.Public
      };
    }
  }
}

<|start_filename|>samples/Server/Helpers/Constants.cs<|end_filename|>
namespace Server.Helpers
{
  public static class Constants
  {
    public const string ADMIN_MAILADDRESS = "<EMAIL>";

    public const string TESTING_ENVIRONMENT = "Testing";

    public static bool IsTestingEnvironment(string environmentName)
    {
      return environmentName == Server.Helpers.Constants.TESTING_ENVIRONMENT;
    }
  }

  public static class Roles
  {
    public const string ADMINISTRATOR_ROLE = "Administrator";
  }
}

<|start_filename|>src/identity/OpenIddict.UI.Identity.Api/Role/RoleService.cs<|end_filename|>
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace tomware.OpenIddict.UI.Identity.Api
{
  public interface IRoleService
  {
    Task<IEnumerable<RoleViewModel>> GetRolesAsync();

    Task<RoleViewModel> GetAsync(string id);

    Task<string> CreateAsync(RoleViewModel model);

    Task UpdateAsync(RoleViewModel model);

    Task DeleteAsync(string id);
  }

  public class RoleService : IRoleService
  {
    private readonly RoleManager<IdentityRole> _manager;

    public RoleService(RoleManager<IdentityRole> manager)
    {
      _manager = manager;
    }

    public async Task<IEnumerable<RoleViewModel>> GetRolesAsync()
    {
      var items = _manager.Roles
        .OrderBy(r => r.Name)
        .ToList();

      var models = items.Select(c => ToModel(c));

      return await Task.FromResult(models);
    }

    public async Task<RoleViewModel> GetAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var role = await _manager.FindByIdAsync(id);

      return role != null ? ToModel(role) : null;
    }

    public async Task<string> CreateAsync(RoleViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));

      var newRole = new IdentityRole(model.Name);
      if (!await _manager.RoleExistsAsync(model.Name))
      {
        await _manager.CreateAsync(newRole);

        return newRole.Id;
      }

      var role = await _manager.FindByNameAsync(model.Name);

      return role.Id;
    }

    public async Task UpdateAsync(RoleViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));
      if (string.IsNullOrWhiteSpace(model.Id)) throw new InvalidOperationException(nameof(model.Id));

      var role = await _manager.FindByIdAsync(model.Id);
      role.Name = model.Name;

      await _manager.UpdateAsync(role);
    }

    public async Task DeleteAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var role = await _manager.FindByIdAsync(id);

      await _manager.DeleteAsync(role);
    }

    private static RoleViewModel ToModel(IdentityRole entity)
    {
      return new RoleViewModel
      {
        Id = entity.Id,
        Name = entity.Name
      };
    }
  }
}

<|start_filename|>samples/Server/Pages/Account/Manage/Index.cshtml<|end_filename|>
@page
@model IndexModel

@{
  ViewData["Title"] = "Profile";
  ViewData["ActivePage"] = ManageNavPages.Index;
}

<form class="form" id="profile-form" method="post">
  <div class="form__group">
    <label asp-for="Username">Username</label>
    <input asp-for="Username" disabled />
  </div>
  <div class="form__group">
    <label asp-for="PhoneNumber">Phone number</label>
    <input asp-for="PhoneNumber" disabled/>
  </div>
</form>


<|start_filename|>src/identity/OpenIddict.UI.Identity.Core/Interfaces/IClaimTypeRepository.cs<|end_filename|>
using System;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Identity.Core
{
  public interface IClaimTypeRepository : IAsyncRepository<ClaimType, Guid>
  { }
}

<|start_filename|>src/openiddict/OpenIddict.UI.Infrastructure/Services/ScopeService.cs<|end_filename|>
using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Infrastructure
{
  public class ScopeService : IScopeService
  {
    private readonly IScopeRepository _repository;
    private readonly OpenIddictScopeManager<OpenIddictEntityFrameworkCoreScope> _manager;

    public ScopeService(
      IScopeRepository repository,
      OpenIddictScopeManager<OpenIddictEntityFrameworkCoreScope> manager
    )
    {
      _repository = repository
        ?? throw new ArgumentNullException(nameof(repository));
      _manager = manager
        ?? throw new ArgumentNullException(nameof(manager));
    }

    public async Task<IEnumerable<ScopeInfo>> GetScopesAsync()
    {
      var items = await _repository.ListAsync(new AllScopes());

      return items.Select(x => ToInfo(x));
    }

    public async Task<ScopeInfo> GetAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var entity = await _manager.FindByIdAsync(id);

      return entity != null ? ToInfo(entity) : null;
    }

    public async Task<string> CreateAsync(ScopeParam model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));

      var entity = await _manager.FindByNameAsync(model.Name);
      if (entity == null)
      {
        // create new entity
        var newEntity = new OpenIddictEntityFrameworkCoreScope
        {
          Name = model.Name,
          DisplayName = model.DisplayName,
          Description = model.Description,
        };

        HandleCustomProperties(model, newEntity);

        await _manager.CreateAsync(newEntity);

        return newEntity.Id;
      }

      // update existing entity
      model.Id = entity.Id;
      await UpdateAsync(model);

      return entity.Id;
    }

    public async Task UpdateAsync(ScopeParam model)
    {
      if (string.IsNullOrWhiteSpace(model.Id)) 
        throw new InvalidOperationException(nameof(model.Id));

      var entity = await _manager.FindByIdAsync(model.Id);

      SimpleMapper.Map<ScopeParam, OpenIddictEntityFrameworkCoreScope>(model, entity);

      HandleCustomProperties(model, entity);

      await _manager.UpdateAsync(entity);
    }

    public async Task DeleteAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var entity = await _manager.FindByIdAsync(id);

      await _manager.DeleteAsync(entity);
    }

    private static ScopeInfo ToInfo(OpenIddictEntityFrameworkCoreScope entity)
    {
      var info = SimpleMapper
        .From<OpenIddictEntityFrameworkCoreScope, ScopeInfo>(entity);

      info.Resources = entity.Resources != null
        ? JsonSerializer.Deserialize<List<string>>(entity.Resources)
        : new List<string>();

      return info;
    }

    private static void HandleCustomProperties(
      ScopeParam model,
      OpenIddictEntityFrameworkCoreScope entity
    )
    {
      entity.Resources = JsonSerializer.Serialize(model.Resources);
    }
  }
}


<|start_filename|>samples/Server/wwwroot/css/site.css<|end_filename|>
*{margin:0;padding:0}
body{font-family:sans-serif;height:100vh}
h1,h2,h3,h4,h5,h6{color:#2c3e50}
a{color:#e95420;text-decoration:none}
a:hover{text-decoration:underline}
a:active{text-decoration:underline}
ul{list-style:none;padding-left:0}
.button,[type=button],[type=submit],a.button,button{display:inline-block;background:#2c3e50;border:1px solid #2c3e50;color:#fff;text-decoration:none;padding:.25rem 1rem;vertical-align:middle;white-space:nowrap;cursor:pointer;margin:.25rem 0;font-size:.8em;border-radius:18px}
.button:hover,[type=button]:hover,[type=submit]:hover,a.button:hover,button:hover{background:#3e5771}
.button:disabled,[type=button]:disabled,[type=submit]:disabled,a.button:disabled,button:disabled{background:#aabdd1}
.button--link{display:table;font-size:1em;border:none;color:#e95420;background:0 0!important;padding:0!important;text-decoration:none;cursor:pointer}
.button--link:hover{text-decoration:underline}
.button--secondary{background:#507192;border:1px solid #507192}
.button--secondary:hover{background:#698aac}
.button--disabled{background:#aabdd1}
.form{margin:5px 0}.form fieldset{border:none}
.form legend{font-size:1em;font-weight:700;margin-bottom:10px}
.form label{font-size:1em;display:block;margin-bottom:8px}
.form input[type=date],.form input[type=datetime],.form input[type=email],.form input[type=number],.form input[type=password],.form input[type=search],.form input[type=text],.form input[type=time],.form input[type=url],.form select,.form textarea{background:rgba(255,255,255,.1);border:none;border-radius:4px;margin:0;outline:0;padding:5px;width:100%;background-color:#e8eeef;margin-bottom:5px;box-sizing:border-box;box-shadow:0 1px 0 rgba(0,0,0,.03) inset;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.03) inset}
.form input[type=date]:focus,.form input[type=datetime]:focus,.form input[type=email]:focus,.form input[type=number]:focus,.form input[type=password]:focus,.form input[type=search]:focus,.form input[type=text]:focus,.form input[type=time]:focus,.form input[type=url]:focus,.form select:focus,.form textarea:focus{background:#d2d9dd}
.form select{-webkit-appearance:menulist-button;height:35px}
.form .number{height:30px;width:30px;display:inline-block;font-size:.8em;margin-right:4px;line-height:30px;text-align:center;text-shadow:0 1px 0 rgba(255,255,255,.2);border-radius:15px 15px 15px 0}
.form__validation-summary{color:red;margin:10px 0;font-size:.8em}
.form__group{margin-bottom:10px}
.form__group label{font-weight:700}
.form__group span{color:red;font-size:.8em}
.pane{display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr;grid-template-areas:'pane-content pane-aside'}
.pane__content{grid-area:pane-content}
.pane__aside{grid-area:pane-aside;margin-left:10px}
@media (max-width:1024px){.pane{display:grid;grid-template-columns:1fr;grid-template-rows:auto 1fr;grid-template-areas:'pane-content' 'pane-aside'}
.pane__aside{margin-top:15px;margin-left:0}}
.header{grid-area:header;color:#fff;border-bottom:1px solid #2c3e50;padding:.5em 0;display:grid;grid-template-columns:500px 1fr;grid-template-rows:1fr;grid-template-areas:'logo login-info'}
.header__logo{grid-area:logo;display:flex;flex-direction:row;align-items:flex-start;align-self:center;font-weight:700;font-size:1.6em}
.header__login-info{grid-area:login-info;display:flex;flex-direction:row;align-self:center;justify-content:flex-end}
.header__login-info li{display:inline-block;padding-left:5px}
.content{grid-area:content;color:#333;background-color:#fff;overflow-y:auto;padding:1em 0}
.footer{grid-area:footer;height:auto;color:#333;background-color:#fff;padding:5px 0;border-top:1px solid #2c3e50;display:grid;grid-template-columns:300px 1fr;grid-template-rows:1fr;grid-template-areas:'footer-left footer-right';font-size:.8em}
.footer__left{grid-area:footer-left;display:flex;flex-direction:row;align-items:flex-start;align-self:center}
.footer__right{grid-area:footer-right;display:flex;flex-direction:row;align-items:flex-end;align-self:center;justify-content:flex-end}
.footer__right ul{display:inherit}
.footer__right ul li{margin-left:5px}
.footer__right ul li:hover{cursor:pointer}
.footer__right .selected-language{font-weight:700;color:#fff;padding:0 5px;background-color:#e95420}
.shell{display:grid;grid-template-columns:1fr;grid-template-rows:auto 1fr auto;grid-template-areas:'header' 'content' 'footer';height:100vh;width:75%;margin-left:auto;margin-right:auto}
@media (max-width:1024px){.shell{padding:0;width:90%}}


<|start_filename|>src/identity/OpenIddict.UI.Identity.Infrastructure/Data/Repositories/ClaimTypeRepository.cs<|end_filename|>
using System;
using tomware.OpenIddict.UI.Identity.Core;

namespace tomware.OpenIddict.UI.Identity.Infrastructure
{
  public class ClaimTypeRepository<TContext>
    : EfRepository<ClaimType, Guid>, IClaimTypeRepository
    where TContext : OpenIddictUIIdentityContext
  {
    public ClaimTypeRepository(TContext dbContext) : base(dbContext)
    {
    }
  }
}

<|start_filename|>src/openiddict/OpenIddict.UI.Infrastructure/Specifications/ApplicationSpecifications.cs<|end_filename|>
using OpenIddict.EntityFrameworkCore.Models;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Infrastructure
{
  public sealed class AllApplications : BaseSpecification<OpenIddictEntityFrameworkCoreApplication>
  {
    public AllApplications()
    {
      ApplyOrderBy(x => x.DisplayName);
      ApplyNoTracking();
    }
  }
}


<|start_filename|>samples/Server/Pages/Account/Login.cshtml<|end_filename|>
@page
@model LoginModel

@{
  ViewData["Title"] = "Login";
}

<div class="pane">
  <div class="pane__content" >
    <h4>Use a local account to log in.</h4>
    <form class="form" id="account" method="post">
      <div class="form__validation-summary" asp-validation-summary="All"></div>
      <div class="form__group">
        <label asp-for="Input.Username">Username</label>
        <input asp-for="Input.Username" />
        <span asp-validation-for="Input.Username"></span>
      </div>
      <div class="form__group">
        <label asp-for="Input.Password">Password</label>
        <input asp-for="Input.Password" />
        <span asp-validation-for="Input.Password"></span>
      </div>
      <div class="form__group">
        <div>
          <label asp-for="Input.RememberMe">
            <input asp-for="Input.RememberMe" />
            Remember Me
          </label>
        </div>
      </div>
      <div>
        <button type="submit">Log in</button>
      </div>
    </form>
  </div>
  <div class="pane__aside">
  </div>
</div>


<|start_filename|>src/openiddict/OpenIddict.UI.Api/Application/ApplicationApiService.cs<|end_filename|>
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Suite.Core;
using tomware.OpenIddict.UI.Infrastructure;

namespace tomware.OpenIddict.UI.Api
{
  public interface IApplicationApiService
  {
    Task<IEnumerable<ApplicationViewModel>> GetApplicationsAsync();

    Task<ApplicationViewModel> GetAsync(string clientId);

    Task<string> CreateAsync(ApplicationViewModel model);

    Task UpdateAsync(ApplicationViewModel model);

    Task DeleteAsync(string clientId);

    Task<ApplicationOptionsViewModel> GetOptionsAsync();
  }

  public class ApplicationApiService : IApplicationApiService
  {
    private readonly IApplicationService _service;
    private readonly IOptions<OpenIddictUIApiOptions> _options;

    public ApplicationApiService(
      IApplicationService service,
      IOptions<OpenIddictUIApiOptions> options
    )
    {
      _service = service
        ?? throw new ArgumentNullException(nameof(service));
      _options = options
        ?? throw new ArgumentNullException(nameof(options));
    }

    public async Task<IEnumerable<ApplicationViewModel>> GetApplicationsAsync()
    {
      var items = await _service.GetApplicationsAsync();

      return items.Select(c => ToModel(c));
    }

    public async Task<ApplicationViewModel> GetAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var claimType = await _service.GetAsync(id);

      return claimType != null ? ToModel(claimType) : null;
    }

    public async Task<string> CreateAsync(ApplicationViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));

      var param = ToParam(model);

      return await _service.CreateAsync(param);
    }

    public async Task UpdateAsync(ApplicationViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));
      if (string.IsNullOrWhiteSpace(model.Id))
        throw new InvalidOperationException(nameof(model.Id));

      var param = ToParam(model);

      await _service.UpdateAsync(param);
    }

    public async Task DeleteAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      await _service.DeleteAsync(id);
    }

    public async Task<ApplicationOptionsViewModel> GetOptionsAsync()
    {
      var options = _options.Value;

      var model = new ApplicationOptionsViewModel
      {
        Permissions = options.Permissions
      };

      return await Task.FromResult(model);
    }

    private static ApplicationParam ToParam(ApplicationViewModel model)
    {
      return SimpleMapper.From<ApplicationViewModel, ApplicationParam>(model);
    }

    private static ApplicationViewModel ToModel(ApplicationInfo info)
    {
      return SimpleMapper.From<ApplicationInfo, ApplicationViewModel>(info);
    }
  }
}

<|start_filename|>src/suite/OpenIddict.UI.Suite.Api/Extensions/RoutePrefixExtension.cs<|end_filename|>
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;
using System;
using System.Collections.Generic;
using System.Linq;

namespace tomware.OpenIddict.UI.Suite.Api
{
  public static class RoutePrefixExtension
  {
    public static void UseOpenIddictUIRoutePrefix(
      this MvcOptions opts,
      string prefix,
      IEnumerable<Type> controllerTypes
    )
    {
      opts.Conventions.Insert(0, new RoutePrefixConvention(
        new RouteAttribute(prefix),
        controllerTypes
      ));
    }
  }

  internal class RoutePrefixConvention : IApplicationModelConvention
  {
    private readonly AttributeRouteModel _routePrefix;
    private readonly IEnumerable<Type> _controllerTypes;

    public RoutePrefixConvention(IRouteTemplateProvider route, IEnumerable<Type> controllerTypes)
    {
      _routePrefix = new AttributeRouteModel(route);
      _controllerTypes = controllerTypes 
        ?? throw new ArgumentNullException(nameof(controllerTypes));
    }

    public void Apply(ApplicationModel application)
    {
      var controllers = application.Controllers
        .Where(c => _controllerTypes.Contains(c.ControllerType));
      foreach (var controller in controllers)
      {
        var matchedSelectors = controller.Selectors
          .Where(x => x.AttributeRouteModel != null)
          .ToList();
        if (matchedSelectors.Any())
        {
          foreach (var selectorModel in matchedSelectors)
          {
            selectorModel.AttributeRouteModel = AttributeRouteModel
              .CombineAttributeRouteModel(_routePrefix, selectorModel.AttributeRouteModel);
          }
        }

        var unmatchedSelectors = controller.Selectors
          .Where(x => x.AttributeRouteModel == null)
          .ToList();
        if (unmatchedSelectors.Any())
        {
          foreach (var selectorModel in unmatchedSelectors)
          {
            selectorModel.AttributeRouteModel = _routePrefix;
          }
        }
      }
    }
  }
}

<|start_filename|>src/identity/OpenIddict.UI.Identity.Core/DTOs/ClaimTypeParam.cs<|end_filename|>
using System;

namespace tomware.OpenIddict.UI.Identity.Core
{
  public class ClaimTypeParam
  {
    public Guid? Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string ClaimValueType { get; set; }
  }
}

<|start_filename|>samples/Server/Pages/Account/Manage/Index.cshtml.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Server.Models;

namespace Server.Pages.Account.Manage
{
  public partial class IndexModel : PageModel
  {
    private readonly UserManager<ApplicationUser> userManager;

    public IndexModel(
      UserManager<ApplicationUser> userManager
    )
    {
      this.userManager = userManager;
    }

    public string Username { get; set; }

    public string PhoneNumber { get; set; }


    public async Task<IActionResult> OnGetAsync()
    {
      var user = await userManager.GetUserAsync(User);
      if (user == null)
      {
        return NotFound(
          string.Format("Unable to load user with ID '{0}'.",
          userManager.GetUserId(User))
        );
      }

      await LoadAsync(user);

      return Page();
    }

    private async Task LoadAsync(ApplicationUser user)
    {
      var userName = await userManager.GetUserNameAsync(user);
      var phoneNumber = await userManager.GetPhoneNumberAsync(user);

      Username = userName;
      PhoneNumber = phoneNumber;
    }
  }
}


<|start_filename|>src/openiddict/OpenIddict.UI.Infrastructure/Interfaces/IApplicationRepository.cs<|end_filename|>
using OpenIddict.EntityFrameworkCore.Models;
using tomware.OpenIddict.UI.Suite.Core;

namespace tomware.OpenIddict.UI.Infrastructure
{
  public interface IApplicationRepository 
    : IAsyncRepository<OpenIddictEntityFrameworkCoreApplication, string>
  { }
}

<|start_filename|>src/openiddict/OpenIddict.UI.Api/Scope/ScopeService.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using tomware.OpenIddict.UI.Suite.Core;
using tomware.OpenIddict.UI.Infrastructure;

namespace tomware.OpenIddict.UI.Api
{
  public interface IScopeApiService
  {
    Task<IEnumerable<ScopeViewModel>> GetScopesAsync();

    Task<IEnumerable<string>> GetScopeNamesAsync();

    Task<ScopeViewModel> GetAsync(string id);

    Task<string> CreateAsync(ScopeViewModel model);

    Task UpdateAsync(ScopeViewModel model);

    Task DeleteAsync(string id);
  }

  public class ScopeApiService : IScopeApiService
  {
    private readonly IScopeService _service;

    public ScopeApiService(IScopeService service)
    {
      _service = service;
    }

    public async Task<IEnumerable<ScopeViewModel>> GetScopesAsync()
    {
      var items = await _service.GetScopesAsync();

      return items.Select(c => ToModel(c));
    }

    public async Task<IEnumerable<string>> GetScopeNamesAsync()
    {
      var items = await _service.GetScopesAsync();

      return items.Select(i => i.Name);
    }

    public async Task<ScopeViewModel> GetAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      var claimType = await _service.GetAsync(id);

      return claimType != null ? ToModel(claimType) : null;
    }

    public async Task<string> CreateAsync(ScopeViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));

      var param = ToParam(model);

      return await _service.CreateAsync(param);
    }

    public async Task UpdateAsync(ScopeViewModel model)
    {
      if (model == null) throw new ArgumentNullException(nameof(model));
      if (string.IsNullOrWhiteSpace(model.Id))
        throw new InvalidOperationException(nameof(model.Id));

      var param = ToParam(model);

      await _service.UpdateAsync(param);
    }

    public async Task DeleteAsync(string id)
    {
      if (id == null) throw new ArgumentNullException(nameof(id));

      await _service.DeleteAsync(id);
    }

    private static ScopeParam ToParam(ScopeViewModel model)
    {
      return SimpleMapper.From<ScopeViewModel, ScopeParam>(model);
    }

    private static ScopeViewModel ToModel(ScopeInfo info)
    {
      return SimpleMapper.From<ScopeInfo, ScopeViewModel>(info);
    }
  }
}