Skip to content

Commit fa934b9

Browse files
committed
Add triple token exchange choice
1 parent 8f4e725 commit fa934b9

File tree

1 file changed

+155
-2
lines changed

1 file changed

+155
-2
lines changed

examples/TokenTool/TokenTool.Core/TokenTool.cs

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public async Task RunAsync(MyApp.IOConsole ioConsole)
6262
input = (ioConsole.ReadLine() ?? "").ToLower();
6363

6464
var exchange = "0";
65-
if (input.EndsWith("x") || input.EndsWith("1") || input.EndsWith("2"))
65+
if (input.EndsWith("x") || input.EndsWith("1") || input.EndsWith("2") || input.EndsWith("3"))
6666
{
6767
exchange = input.Substring(1, 1);
6868
input = input.Substring(0, 1);
@@ -320,7 +320,160 @@ public async Task RunAsync(MyApp.IOConsole ioConsole)
320320
}
321321
}
322322

323-
if (exchange == "2")
323+
if (exchange == "2" || exchange == "3")
324+
{
325+
handler = new HttpClientHandler { DefaultProxyCredentials = CredentialCache.DefaultCredentials };
326+
client = new HttpClient(handler);
327+
328+
ioConsole.WriteLine("Token Exchange");
329+
configUrlList = [
330+
"https://iam-security-training.com/provider/sts"
331+
];
332+
for (var i = 0; i < configUrlList.Count; i++)
333+
{
334+
ioConsole.WriteLine(i + ": " + configUrlList[i]);
335+
}
336+
input = "0";
337+
if (configUrlList.Count > 1)
338+
{
339+
ioConsole.WriteLine("Enter index: ");
340+
input = ioConsole.ReadLine();
341+
}
342+
configUrl = configUrlList[Convert.ToInt32(input ?? "0")];
343+
344+
configUrlList = [
345+
"",
346+
"basyx",
347+
"assetfox",
348+
"factory-x"
349+
];
350+
for (var i = 0; i < configUrlList.Count; i++)
351+
{
352+
ioConsole.WriteLine(i + ": " + configUrlList[i]);
353+
}
354+
input = "0";
355+
if (configUrlList.Count > 1)
356+
{
357+
ioConsole.WriteLine("Enter index: ");
358+
input = ioConsole.ReadLine();
359+
}
360+
target = configUrlList[Convert.ToInt32(input ?? "0")];
361+
362+
var d = new Dictionary<string, string>
363+
{
364+
{ "grant_type", "urn:ietf:params:oauth:grant-type:token-exchange" },
365+
{ "subject_token_type", "urn:ietf:params:oauth:token-type:jwt" },
366+
{ "requested_token_type", "urn:ietf:params:oauth:token-type:access_token" },
367+
{ "subject_token", accessToken },
368+
};
369+
if (target != "")
370+
{
371+
d.Add("audience", target);
372+
}
373+
var request = new HttpRequestMessage(HttpMethod.Post, $"{configUrl}/token")
374+
{
375+
Content = new FormUrlEncodedContent(d)
376+
};
377+
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
378+
379+
var response = await client.SendAsync(request);
380+
var content = await response.Content.ReadAsStringAsync();
381+
382+
accessToken = "";
383+
doc = JsonDocument.Parse(content);
384+
if (doc.RootElement.TryGetProperty("access_token", out var tokenElement))
385+
{
386+
accessToken = tokenElement.GetString();
387+
ioConsole.WriteLine("Access Token: " + accessToken);
388+
389+
using var httpClient = new HttpClient(handler);
390+
var jwksJson = await httpClient.GetStringAsync($"{configUrl}/jwks");
391+
var jwks = JObject.Parse(jwksJson)["keys"];
392+
393+
/*
394+
var handler2 = new JwtSecurityTokenHandler();
395+
var jwt2 = handler2.ReadJwtToken(accessToken);
396+
var kid = jwt2.Header["kid"].ToString();
397+
398+
// 3. Find matching key
399+
var key = jwks.First(k => k["kid"].ToString() == kid);
400+
401+
// 4. Build RSA key
402+
var e = Base64UrlEncoder.DecodeBytes(key["e"].ToString());
403+
var n = Base64UrlEncoder.DecodeBytes(key["n"].ToString());
404+
var rsa = new RSAParameters { Exponent = e, Modulus = n };
405+
var rsaKey = new RsaSecurityKey(rsa);
406+
407+
// 5. Validate token
408+
var validationParams = new TokenValidationParameters
409+
{
410+
ValidateIssuer = false,
411+
ValidateAudience = false,
412+
ValidateLifetime = true,
413+
ValidateIssuerSigningKey = true,
414+
IssuerSigningKey = rsaKey,
415+
ClockSkew = TimeSpan.FromMinutes(5)
416+
};
417+
418+
try
419+
{
420+
handler2.ValidateToken(accessToken, validationParams, out _);
421+
ioConsole.WriteLine("Token is valid");
422+
}
423+
catch (Exception ex)
424+
{
425+
ioConsole.WriteLine($"Validation failed: {ex.Message}");
426+
}
427+
*/
428+
429+
// 1) Handler
430+
var handler2 = new JsonWebTokenHandler();
431+
432+
// 2) JWK aus JWKS direkt verwenden (hier exemplarisch LINQ auf Dein jwks-Array)
433+
var jwtHeaderKid = new JsonWebToken(accessToken).Kid; // liest 'kid' robust
434+
var jwkJson = jwks.First(k => k["kid"].ToString() == jwtHeaderKid).ToString(); // k ist i. d. R. ein JObject
435+
var jwk = new JsonWebKey(jwkJson);
436+
437+
// 3) Validierungsparameter
438+
var validationParams = new TokenValidationParameters
439+
{
440+
// Signaturprüfung
441+
IssuerSigningKey = jwk, // kein manuelles RSAParameters nötig
442+
ValidateIssuerSigningKey = true,
443+
444+
// Lebenszeit
445+
ValidateLifetime = true,
446+
RequireExpirationTime = true,
447+
ClockSkew = TimeSpan.FromMinutes(5), // bei UTC+0 gut, ggf. 2–5 Minuten
448+
449+
// Issuer/Audience je nach Bedarf (bei Tests oft aus)
450+
ValidateIssuer = false, // später auf true + ValidIssuer setzen
451+
ValidateAudience = false, // später auf true + ValidAudience setzen
452+
453+
// Keine Legacy-Claim-Mappings
454+
// MapInboundClaims = false,
455+
456+
// Optional: Name-/Rollen-Claims aus dem JWT
457+
NameClaimType = "name",
458+
RoleClaimType = "role"
459+
};
460+
461+
try
462+
{
463+
var result = handler2.ValidateToken(accessToken, validationParams);
464+
if (!result.IsValid)
465+
ioConsole.WriteLine($"Validation failed: {result.Exception?.Message}");
466+
else
467+
ioConsole.WriteLine("Token is valid");
468+
}
469+
catch (Exception ex)
470+
{
471+
ioConsole.WriteLine($"Validation failed: {ex.Message}");
472+
}
473+
}
474+
}
475+
476+
if (exchange == "3")
324477
{
325478
handler = new HttpClientHandler { DefaultProxyCredentials = CredentialCache.DefaultCredentials };
326479
client = new HttpClient(handler);

0 commit comments

Comments
 (0)