Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 0c9177c

Browse files
authored
Merge pull request #387 from IdentityModel/brock/dpop
Brock/dpop
2 parents 8e4c324 + a2a791a commit 0c9177c

36 files changed

+2282
-24
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@af513c7a016048ae468971c52ed77d9562c7c819
2323

24+
- name: Setup net6
25+
uses: actions/setup-dotnet@v1
26+
with:
27+
dotnet-version: '7.0.x'
28+
2429
- name: Setup net6
2530
uses: actions/setup-dotnet@v1
2631
with:

IdentityModel.OidcClient.sln

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29519.87
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.7.34031.279
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0B7CC363-CF34-4B40-A769-7E928A6B25AF}"
77
EndProject
@@ -15,9 +15,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OidcClient.Tests", "test\Oi
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleClientWithBrowser", "clients\ConsoleClientWithBrowser\ConsoleClientWithBrowser.csproj", "{D8307A14-403E-48A4-870F-88CF9FE8CACA}"
1717
EndProject
18-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityTokenValidator", "src\IdentityTokenValidator\IdentityTokenValidator.csproj", "{B5E03BAE-BDB8-4516-AE1A-F9F262C87DE8}"
18+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityTokenValidator", "src\IdentityTokenValidator\IdentityTokenValidator.csproj", "{B5E03BAE-BDB8-4516-AE1A-F9F262C87DE8}"
1919
EndProject
20-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JwtValidationTests", "test\JwtValidationTests\JwtValidationTests.csproj", "{927224F5-C4B6-404F-8028-60E867D8D24C}"
20+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JwtValidationTests", "test\JwtValidationTests\JwtValidationTests.csproj", "{927224F5-C4B6-404F-8028-60E867D8D24C}"
21+
EndProject
22+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DPoP", "src\DPoP\DPoP.csproj", "{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C}"
23+
EndProject
24+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DPoPTests", "test\DPoPTests\DPoPTests.csproj", "{0E1807AF-4142-4A3D-925C-BBA019E4E777}"
25+
EndProject
26+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleClientWithBrowserAndDPoP", "clients\ConsoleClientWithBrowserAndDPoP\ConsoleClientWithBrowserAndDPoP.csproj", "{7DDDA872-49C0-43F0-8B88-2531BF828DDE}"
2127
EndProject
2228
Global
2329
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -45,6 +51,18 @@ Global
4551
{927224F5-C4B6-404F-8028-60E867D8D24C}.Debug|Any CPU.Build.0 = Debug|Any CPU
4652
{927224F5-C4B6-404F-8028-60E867D8D24C}.Release|Any CPU.ActiveCfg = Release|Any CPU
4753
{927224F5-C4B6-404F-8028-60E867D8D24C}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{0E1807AF-4142-4A3D-925C-BBA019E4E777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{0E1807AF-4142-4A3D-925C-BBA019E4E777}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{0E1807AF-4142-4A3D-925C-BBA019E4E777}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{0E1807AF-4142-4A3D-925C-BBA019E4E777}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{7DDDA872-49C0-43F0-8B88-2531BF828DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{7DDDA872-49C0-43F0-8B88-2531BF828DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{7DDDA872-49C0-43F0-8B88-2531BF828DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{7DDDA872-49C0-43F0-8B88-2531BF828DDE}.Release|Any CPU.Build.0 = Release|Any CPU
4866
EndGlobalSection
4967
GlobalSection(SolutionProperties) = preSolution
5068
HideSolutionNode = FALSE
@@ -55,6 +73,9 @@ Global
5573
{D8307A14-403E-48A4-870F-88CF9FE8CACA} = {A4154BEB-4B4A-4A48-B75D-B52432304F36}
5674
{B5E03BAE-BDB8-4516-AE1A-F9F262C87DE8} = {0B7CC363-CF34-4B40-A769-7E928A6B25AF}
5775
{927224F5-C4B6-404F-8028-60E867D8D24C} = {3DEB81D4-5B40-4D20-AC50-66D1CD6EA24A}
76+
{56EC3A58-33FE-4DCD-9D64-2A985DC58C2C} = {0B7CC363-CF34-4B40-A769-7E928A6B25AF}
77+
{0E1807AF-4142-4A3D-925C-BBA019E4E777} = {3DEB81D4-5B40-4D20-AC50-66D1CD6EA24A}
78+
{7DDDA872-49C0-43F0-8B88-2531BF828DDE} = {A4154BEB-4B4A-4A48-B75D-B52432304F36}
5879
EndGlobalSection
5980
GlobalSection(ExtensibilityGlobals) = postSolution
6081
SolutionGuid = {66951C2E-691F-408C-9283-F2455F390A9A}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="..\..\src\DPoP\DPoP.csproj" />
10+
<ProjectReference Include="..\..\src\IdentityTokenValidator\IdentityTokenValidator.csproj" />
11+
<ProjectReference Include="..\..\src\OidcClient\OidcClient.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
16+
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
17+
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
18+
</ItemGroup>
19+
20+
</Project>
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using IdentityModel.OidcClient;
2+
using Serilog;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Net.Http;
7+
using System.Text.Json;
8+
using System.Threading.Tasks;
9+
using Serilog.Sinks.SystemConsole.Themes;
10+
using IdentityModel.DPoP;
11+
12+
namespace ConsoleClientWithBrowserAndDPoP
13+
{
14+
public class Program
15+
{
16+
static readonly string Api = "https://demo.duendesoftware.com/api/dpop/test";
17+
static readonly string Authority = "https://demo.duendesoftware.com";
18+
19+
private static OidcClient _oidcClient;
20+
private static HttpClient _apiClient = new HttpClient { BaseAddress = new Uri(Api) };
21+
22+
public static async Task Main()
23+
{
24+
Console.WriteLine("+-----------------------+");
25+
Console.WriteLine("| Sign in with OIDC |");
26+
Console.WriteLine("+-----------------------+");
27+
Console.WriteLine("");
28+
Console.WriteLine("Press any key to sign in...");
29+
Console.ReadKey();
30+
31+
await SignIn();
32+
}
33+
34+
private static async Task SignIn()
35+
{
36+
var browser = new SystemBrowser();
37+
string redirectUri = string.Format($"http://127.0.0.1:{browser.Port}");
38+
39+
var proofKey = GetProofKey();
40+
41+
var tokenDpopHandler = new ProofTokenMessageHandler(proofKey, new SocketsHttpHandler());
42+
var apiDpopHandler = new ProofTokenMessageHandler(proofKey, new SocketsHttpHandler());
43+
44+
var options = new OidcClientOptions
45+
{
46+
Authority = Authority,
47+
ClientId = "native.dpop",
48+
RedirectUri = redirectUri,
49+
Scope = "openid profile api offline_access",
50+
FilterClaims = false,
51+
Browser = browser,
52+
53+
BackchannelHandler = tokenDpopHandler,
54+
RefreshTokenInnerHttpHandler = apiDpopHandler
55+
};
56+
57+
var serilog = new LoggerConfiguration()
58+
.MinimumLevel.Debug()
59+
.Enrich.FromLogContext()
60+
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
61+
.CreateLogger();
62+
63+
options.LoggerFactory.AddSerilog(serilog);
64+
65+
_oidcClient = new OidcClient(options);
66+
67+
LoginResult result = null;
68+
if (File.Exists("refresh_token"))
69+
{
70+
var refreshToken = File.ReadAllText("refresh_token");
71+
72+
var handler = new RefreshTokenDelegatingHandler(
73+
_oidcClient,
74+
null,
75+
refreshToken,
76+
"DPoP",
77+
apiDpopHandler);
78+
79+
_apiClient = new HttpClient(handler)
80+
{
81+
BaseAddress = new Uri(Api)
82+
};
83+
84+
await NextSteps();
85+
}
86+
else
87+
{
88+
result = await _oidcClient.LoginAsync(new LoginRequest());
89+
File.WriteAllText("refresh_token", result.TokenResponse.RefreshToken);
90+
91+
_apiClient = new HttpClient(result.RefreshTokenHandler)
92+
{
93+
BaseAddress = new Uri(Api)
94+
};
95+
}
96+
97+
98+
99+
ShowResult(result);
100+
await NextSteps();
101+
}
102+
103+
private static string GetProofKey()
104+
{
105+
if (File.Exists("proofkey"))
106+
{
107+
return File.ReadAllText("proofkey");
108+
}
109+
110+
var proofKey = JsonWebKeys.CreateRsaJson();
111+
File.WriteAllText("proofkey", proofKey);
112+
return proofKey;
113+
}
114+
115+
private static void ShowResult(LoginResult result)
116+
{
117+
if (result.IsError)
118+
{
119+
Console.WriteLine("\n\nError:\n{0}", result.Error);
120+
return;
121+
}
122+
123+
Console.WriteLine("\n\nClaims:");
124+
foreach (var claim in result.User.Claims)
125+
{
126+
Console.WriteLine("{0}: {1}", claim.Type, claim.Value);
127+
}
128+
129+
var values = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(result.TokenResponse.Raw);
130+
131+
Console.WriteLine($"token response...");
132+
foreach (var item in values)
133+
{
134+
Console.WriteLine($"{item.Key}: {item.Value}");
135+
}
136+
}
137+
138+
private static async Task NextSteps()
139+
{
140+
var menu = " x...exit c...call api ";
141+
142+
while (true)
143+
{
144+
Console.WriteLine("\n\n");
145+
146+
Console.Write(menu);
147+
var key = Console.ReadKey();
148+
149+
if (key.Key == ConsoleKey.X) return;
150+
if (key.Key == ConsoleKey.C) await CallApi();
151+
}
152+
}
153+
154+
private static async Task CallApi()
155+
{
156+
var response = await _apiClient.GetAsync("");
157+
158+
if (response.IsSuccessStatusCode)
159+
{
160+
var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
161+
Console.WriteLine("\n\n");
162+
Console.WriteLine(json.RootElement);
163+
}
164+
else
165+
{
166+
Console.WriteLine($"Error: {response.ReasonPhrase}");
167+
}
168+
}
169+
170+
171+
}
172+
}

0 commit comments

Comments
 (0)