Program.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System.Security.Claims;
  2. using Fido2NetLib;
  3. using Microsoft.AspNetCore.Authentication.JwtBearer;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.IdentityModel.Tokens;
  6. using Passwordless;
  7. var jwk = JsonWebKey.Create(File.ReadAllText("./demo-jwk.json"));
  8. //var host = "http://localhost:5172";
  9. var host = "https://demo.larcanum.net";
  10. var builder = WebApplication.CreateBuilder(args);
  11. // Add services to the container.
  12. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  13. builder.Services.AddEndpointsApiExplorer();
  14. builder.Services.AddSwaggerGen();
  15. builder.Services.AddFido2(options =>
  16. {
  17. // server domain MUST match the actual domain name that the client uses to make the request from
  18. options.ServerDomain = host.Substring(host.LastIndexOf("/", StringComparison.Ordinal) + 1);
  19. options.ServerName = "FIDO2 Test";
  20. options.Origins = [host, "http://localhost:5172"];
  21. options.TimestampDriftTolerance = 300000;
  22. });
  23. builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  24. .AddJwtBearer(options =>
  25. {
  26. options.Events = new JwtBearerEvents()
  27. {
  28. OnAuthenticationFailed = ctx =>
  29. {
  30. Console.WriteLine(ctx.Exception);
  31. return Task.CompletedTask;
  32. },
  33. OnTokenValidated = ctx =>
  34. {
  35. Console.WriteLine($"Valid token from {ctx.SecurityToken.Issuer}");
  36. return Task.CompletedTask;
  37. }
  38. };
  39. options.RequireHttpsMetadata = false; // dev only!!!
  40. options.Authority = host;
  41. options.TokenValidationParameters = new TokenValidationParameters
  42. {
  43. IssuerSigningKey = KeyConverter.ExtractPublicKey(jwk),
  44. ValidIssuer = host,
  45. ValidAudience = host,
  46. NameClaimType = ClaimTypes.NameIdentifier, // important to get the "sub" claim mapped to User.Identity.Name
  47. RoleClaimType = ClaimTypes.Role,
  48. };
  49. });
  50. builder.Services.AddAuthorization(authorizationOptions =>
  51. {
  52. authorizationOptions.AddPolicy("ProtectedPolicy", policyBuilder => policyBuilder
  53. .RequireClaim("permissions", "MagicClaim")
  54. .RequireRole("grunt"));
  55. });
  56. builder.Services.AddMemoryCache();
  57. builder.Services.Configure<JwtConfig>(config =>
  58. {
  59. config.Key = jwk;
  60. config.Host = host;
  61. });
  62. builder.Services.AddTransient<OptionsCache>();
  63. builder.Services.AddTransient<CredentialManager>();
  64. var app = builder.Build();
  65. // Configure the HTTP request pipeline.
  66. if (app.Environment.IsDevelopment())
  67. {
  68. app.UseSwagger();
  69. app.UseSwaggerUI();
  70. }
  71. app.UseDefaultFiles();
  72. app.UseStaticFiles();
  73. app.UseHttpsRedirection();
  74. app.UseAuthorization();
  75. app.MapGet("/buildCredentialOptions", ([FromQuery] string login, CredentialManager credMan) =>
  76. credMan.BuildCredentialOptions(login))
  77. .WithName("BuildCredentialOptions")
  78. .WithOpenApi();
  79. app.MapPost("/registerCredential", async ([FromQuery] string login, [FromBody] AuthenticatorAttestationRawResponse attestationResponse, CredentialManager credMan) =>
  80. await credMan.RegisterCredential(login, attestationResponse))
  81. .WithName("RegisterCredential")
  82. .WithOpenApi();
  83. app.MapGet("/buildAssertionOptions", async ([FromQuery] string login, CredentialManager credMan) =>
  84. await credMan.BuildAssertionOptions(login))
  85. .WithName("BuildAssertionOptions")
  86. .WithOpenApi();
  87. app.MapPost("/verifyCredential", async ([FromBody] AuthenticatorAssertionRawResponse assertionResponse, CredentialManager credMan) =>
  88. Results.Json(await credMan.VerifyCredential(assertionResponse)))
  89. .WithName("VerifyCredential")
  90. .WithOpenApi();
  91. app
  92. .MapGet("/protected", (HttpContext context) =>
  93. {
  94. var data = new Dictionary<string, object>
  95. {
  96. ["Status"] = "Success!",
  97. ["UserName"] = context.User?.Identity?.Name ?? "<unknown>",
  98. ["Permissions"] = context.User?.Claims.Where(c => c.Type == "permissions").Select(c => c.Value) ?? Enumerable.Empty<string>(),
  99. ["IsAdmin"] = context.User?.IsInRole("admin"),
  100. ["IsGrunt"] = context.User?.IsInRole("grunt"),
  101. ["IsNoob"] = context.User?.IsInRole("noob"),
  102. };
  103. return data;
  104. })
  105. .WithName("Protected")
  106. .RequireAuthorization("ProtectedPolicy");
  107. // .RequireAuthorization(policy =>
  108. // {
  109. // policy.RequireAuthenticatedUser();
  110. // });
  111. app.Run();