Skip to content

Commit

Permalink
Merge pull request #52 from DuendeSoftware/features/resource_isolation
Browse files Browse the repository at this point in the history
Resource Indicators
  • Loading branch information
brockallen authored Dec 29, 2020
2 parents 52965fe + ea208f5 commit 724bb31
Show file tree
Hide file tree
Showing 67 changed files with 3,140 additions and 872 deletions.
7 changes: 7 additions & 0 deletions clients/Duende.IdentityServer.Clients.sln
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceBasedApi", "src\API
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcJarJwt", "src\MvcJarJwt\MvcJarJwt.csproj", "{C371C605-087E-4523-BF15-DD75241283C3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleScopesResources", "src\ConsoleScopesResources\ConsoleScopesResources.csproj", "{68C3A94B-1CE1-4434-A59D-5119AF648C38}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -163,6 +165,10 @@ Global
{C371C605-087E-4523-BF15-DD75241283C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C371C605-087E-4523-BF15-DD75241283C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C371C605-087E-4523-BF15-DD75241283C3}.Release|Any CPU.Build.0 = Release|Any CPU
{68C3A94B-1CE1-4434-A59D-5119AF648C38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68C3A94B-1CE1-4434-A59D-5119AF648C38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68C3A94B-1CE1-4434-A59D-5119AF648C38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68C3A94B-1CE1-4434-A59D-5119AF648C38}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -195,6 +201,7 @@ Global
{215D863E-9D87-42F4-AD17-209C330C6ECB} = {AFE7085F-051E-4829-955F-3426FE643BDD}
{933613BD-35B0-4F79-A7A7-02F663C55B61} = {AFE7085F-051E-4829-955F-3426FE643BDD}
{C371C605-087E-4523-BF15-DD75241283C3} = {158628D7-8B68-451E-AF22-B64F473C5943}
{68C3A94B-1CE1-4434-A59D-5119AF648C38} = {D027D36B-262B-450A-B444-5B7893B5142E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BAD78470-3D66-466E-9C17-2A67F0905B18}
Expand Down
5 changes: 4 additions & 1 deletion clients/src/APIs/SimpleApi/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Clients;
using Clients;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -13,6 +13,9 @@ public void ConfigureServices(IServiceCollection services)
services.AddCors();
services.AddDistributedMemoryCache();

// remove Microsoft's custom claim mapping
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

// this API will accept any access token from the authority
services.AddAuthentication("token")
.AddJwtBearer("token", options =>
Expand Down
12 changes: 12 additions & 0 deletions clients/src/ConsoleScopesResources/ConsoleScopesResources.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Constants\Constants.csproj" />
</ItemGroup>

</Project>
183 changes: 183 additions & 0 deletions clients/src/ConsoleScopesResources/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Clients;
using IdentityModel.Client;

namespace ConsoleScopesResources
{
class Program
{
private static DiscoveryCache Cache;

static async Task Main(string[] args)
{
Console.Title = "Console Resources and Scopes Client";
Cache = new DiscoveryCache("https://localhost:5001");

var leave = false;

while (leave == false)
{
Console.Clear();

"Resource setup:\n".ConsoleGreen();

"resource1: resource1.scope1 resource1.scope2 shared.scope".ConsoleGreen();
"resource2: resource2.scope1 resource2.scope2 shared.scope\n".ConsoleGreen();
"resource3 (isolated): resource3.scope1 resource3.scope2 shared.scope\n".ConsoleGreen();
"scopes without resource association: scope3 scope4 transaction\n\n".ConsoleGreen();


// scopes without associated resource
"a) scope3 scope4".ConsoleYellow();

// one scope, single resource
"b) resource1.scope1".ConsoleYellow();

// two scopes, single resources
"c) resource1.scope1 resource1.scope2".ConsoleYellow();

// two scopes, one has a resource, one doesn't
"d) resource1.scope1 scope3".ConsoleYellow();

// two scopes, two resource
"e) resource1.scope1 resource2.scope1".ConsoleYellow();

// shared scope between two resources
"f) shared.scope".ConsoleYellow();

// shared scope between two resources and scope that belongs to resource
"g) resource1.scope1 shared.scope".ConsoleYellow();

// parameterized scope
"h) transaction:123".ConsoleYellow();

// no scope
"i) no scope".ConsoleYellow();

// no scope
"j) no scope (resource: resource1)".ConsoleYellow();

// no scope
"k) no scope (resource: resource3)".ConsoleYellow();

// isolated scope without resource parameter
"l) resource3.scope1".ConsoleYellow();

// isolated scope without resource parameter
"m) resource3.scope1 (resource: resource3)".ConsoleYellow();

// isolated scope without resource parameter
"n) resource3.scope1 (resource: resource2)".ConsoleYellow();

"\nx) quit".ConsoleYellow();

var input = Console.ReadKey();

switch (input.Key)
{
case ConsoleKey.A:
await RequestToken("scope3 scope4");
break;

case ConsoleKey.B:
await RequestToken("resource1.scope1");
break;

case ConsoleKey.C:
await RequestToken("resource1.scope1 resource1.scope2");
break;

case ConsoleKey.D:
await RequestToken("resource1.scope1 scope3");
break;

case ConsoleKey.E:
await RequestToken("resource1.scope1 resource2.scope1");
break;

case ConsoleKey.F:
await RequestToken("shared.scope");
break;

case ConsoleKey.G:
await RequestToken("resource1.scope1 shared.scope");
break;

case ConsoleKey.H:
await RequestToken("transaction:123");
break;

case ConsoleKey.I:
await RequestToken("");
break;

case ConsoleKey.J:
await RequestToken("", "urn:resource1");
break;

case ConsoleKey.K:
await RequestToken("", "urn:resource3");
break;

case ConsoleKey.L:
await RequestToken("resource3.scope1");
break;

case ConsoleKey.M:
await RequestToken("resource3.scope1", "urn:resource3");
break;

case ConsoleKey.N:
await RequestToken("resource3.scope1", "urn:resource2");
break;

case ConsoleKey.X:
leave = true;
break;
}
}
}

static async Task RequestToken(string scope, string resource = null)
{
var client = new HttpClient();
var disco = await Cache.GetAsync();

var request = new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "console.resource.scope",
ClientSecret = "secret",

Scope = scope
};

if (!string.IsNullOrEmpty(resource))
{
request.Parameters = new Dictionary<string, string>
{
{ "resource", resource }
};
}

var response = await client.RequestClientCredentialsTokenAsync(request);

if (response.IsError)
{
Console.WriteLine();
Console.WriteLine(response.Error);
Console.ReadLine();
return;
}

Console.WriteLine();
Console.WriteLine();

response.Show();
Console.ReadLine();
}
}
}
29 changes: 29 additions & 0 deletions hosts/Configuration/ClientsConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,35 @@ public static IEnumerable<Client> Get()
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = { "transaction" }
},

///////////////////////////////////////////
// Console Resources and Scopes Sample
//////////////////////////////////////////
new Client
{
ClientId = "console.resource.scope",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,

AllowedScopes =
{
"resource1.scope1",
"resource1.scope2",

"resource2.scope1",
"resource2.scope2",

"resource3.scope1",
"resource3.scope2",

"shared.scope",

"transaction",
"scope3",
"scope4",
IdentityServerConstants.LocalApi.ScopeName
}
},

///////////////////////////////////////////
// X509 mTLS Client
Expand Down
30 changes: 21 additions & 9 deletions hosts/Configuration/Resources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,17 @@ public class Resources

// resource specific scopes
new ApiScope("resource1.scope1"),
new ApiScope("resource2.scope1"),
new ApiScope("resource1.scope2"),

new ApiScope("resource2.scope1"),
new ApiScope("resource2.scope2"),

new ApiScope("resource3.scope1"),
new ApiScope("resource3.scope2"),

// a scope without resource association
new ApiScope("scope3"),
new ApiScope("scope4"),

// a scope shared by multiple resources
new ApiScope("shared.scope"),
Expand All @@ -52,19 +59,16 @@ public class Resources
public static readonly IEnumerable<ApiResource> ApiResources =
new[]
{
new ApiResource("resource1", "Resource 1")
new ApiResource("urn:resource1", "Resource 1")
{
ApiSecrets = { new Secret("secret".Sha256()) },

Scopes = { "resource1.scope1", "shared.scope" }
Scopes = { "resource1.scope1", "resource1.scope2", "shared.scope" }
},

new ApiResource("resource2", "Resource 2")
new ApiResource("urn:resource2", "Resource 2")
{
ApiSecrets =
{
new Secret("secret".Sha256())
},
ApiSecrets = { new Secret("secret".Sha256()) },

// additional claims to put into access token
UserClaims =
Expand All @@ -73,7 +77,15 @@ public class Resources
JwtClaimTypes.Email
},

Scopes = { "resource2.scope1", "shared.scope" }
Scopes = { "resource2.scope1", "resource2.scope2", "shared.scope" }
},

new ApiResource("urn:resource3", "Resource 3 (isolated)")
{
ApiSecrets = { new Secret("secret".Sha256()) },

RequireResourceIndicator = true,
Scopes = { "resource3.scope1", "resource3.scope2", "shared.scope" }
}
};
}
Expand Down
3 changes: 2 additions & 1 deletion migrations/SqlServer/Migrations/ConfigurationDb.sql
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ CREATE TABLE [ApiResources] (
[Description] nvarchar(1000) NULL,
[AllowedAccessTokenSigningAlgorithms] nvarchar(100) NULL,
[ShowInDiscoveryDocument] bit NOT NULL,
[RequireResourceIndicator] bit NOT NULL,
[Created] datetime2 NOT NULL,
[Updated] datetime2 NULL,
[LastAccessed] datetime2 NULL,
Expand Down Expand Up @@ -375,7 +376,7 @@ CREATE UNIQUE INDEX [IX_IdentityResources_Name] ON [IdentityResources] ([Name]);
GO

INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20201125135748_Configuration', N'3.1.0');
VALUES (N'20201214213517_Configuration', N'3.1.0');

GO

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
Description = table.Column<string>(maxLength: 1000, nullable: true),
AllowedAccessTokenSigningAlgorithms = table.Column<string>(maxLength: 100, nullable: true),
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
RequireResourceIndicator = table.Column<bool>(nullable: false),
Created = table.Column<DateTime>(nullable: false),
Updated = table.Column<DateTime>(nullable: true),
LastAccessed = table.Column<DateTime>(nullable: true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property<bool>("NonEditable")
.HasColumnType("bit");
b.Property<bool>("RequireResourceIndicator")
.HasColumnType("bit");
b.Property<bool>("ShowInDiscoveryDocument")
.HasColumnType("bit");
Expand Down
Loading

0 comments on commit 724bb31

Please sign in to comment.