Program.cs 4.4 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. // TODO: RoslynPad code for key generation
  8. var jwk = JsonWebKey.Create(File.ReadAllText("./demo-jwk.json"));
  9. //var host = "http://localhost:5172";
  10. var host = "https://demo.larcanum.net";
  11. var builder = WebApplication.CreateBuilder(args);
  12. // Add services to the container.
  13. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
  14. builder.Services.AddEndpointsApiExplorer();
  15. builder.Services.AddSwaggerGen();
  16. builder.Services.AddFido2(options =>
  17. {
  18. // server domain MUST match the actual domain name that the client uses to make the request from
  19. options.ServerDomain = host.Substring(host.LastIndexOf("/", StringComparison.Ordinal) + 1);
  20. options.ServerName = "FIDO2 Test";
  21. options.Origins = [host];
  22. options.TimestampDriftTolerance = 300000;
  23. });
  24. builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  25. .AddJwtBearer(options =>
  26. {
  27. options.Events = new JwtBearerEvents()
  28. {
  29. OnAuthenticationFailed = ctx =>
  30. {
  31. Console.WriteLine(ctx.Exception);
  32. return Task.CompletedTask;
  33. },
  34. OnTokenValidated = ctx =>
  35. {
  36. Console.WriteLine($"Valid token from {ctx.SecurityToken.Issuer}");
  37. return Task.CompletedTask;
  38. }
  39. };
  40. options.RequireHttpsMetadata = false; // dev only!!!
  41. options.Authority = host;
  42. options.TokenValidationParameters = new TokenValidationParameters
  43. {
  44. IssuerSigningKey = jwk,
  45. ValidIssuer = host,
  46. ValidAudience = host,
  47. NameClaimType = ClaimTypes.NameIdentifier, // important to get the "sub" claim mapped to User.Identity.Name
  48. RoleClaimType = ClaimTypes.Role,
  49. };
  50. });
  51. builder.Services.AddAuthorization(authorizationOptions =>
  52. {
  53. authorizationOptions.AddPolicy("ProtectedPolicy", policyBuilder => policyBuilder
  54. .RequireClaim("permissions", "MagicClaim")
  55. .RequireRole("grunt"));
  56. });
  57. builder.Services.AddMemoryCache();
  58. builder.Services.Configure<JwtConfig>(config =>
  59. {
  60. config.Key = jwk;
  61. config.Host = host;
  62. });
  63. builder.Services.AddTransient<OptionsCache>();
  64. builder.Services.AddTransient<CredentialManager>();
  65. var app = builder.Build();
  66. // Configure the HTTP request pipeline.
  67. if (app.Environment.IsDevelopment())
  68. {
  69. app.UseSwagger();
  70. app.UseSwaggerUI();
  71. }
  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();