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 \n Error:\n {0}" , result . Error ) ;
120+ return ;
121+ }
122+
123+ Console . WriteLine ( "\n \n Claims:" ) ;
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