I am working through a book on ASP.NET Core Web API and I am trying to implement authorization.
Here is my Program.cs:
var builder = WebApplication.CreateBuilder(args);
var connString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Logging.ClearProviders()
.AddSimpleConsole()
.AddDebug();
builder.Host.UseSerilog((ctx, lc) =>
{
lc.ReadFrom.Configuration(ctx.Configuration);
lc.Enrich.WithMachineName();
lc.Enrich.WithThreadId();
// This causes SeriLog to log to a table called LogEvents in the database. If the
// table does not exist, it is created.
lc.WriteTo.MSSqlServer(
connectionString: connString,
sinkOptions: new MSSqlServerSinkOptions
{
TableName = "LogEvents",
AutoCreateSqlTable = true
},
columnOptions: new ColumnOptions()
{
AdditionalColumns = new SqlColumn[]
{
new SqlColumn()
{
ColumnName = "SourceContext",
PropertyName = "SourceContext",
DataType = System.Data.SqlDbType.NVarChar
}
}
}
);
},
// Keep sending log events to previously defined logging providers
writeToProviders: true);
// Add services to the container.
builder.Services.AddControllers(options =>
{
options.ModelBindingMessageProvider.SetValueIsInvalidAccessor((x) => $"The value '{x}' is invalid.");
options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor((x) => $"The field {x} must be a number.");
options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => $"The value '{x}' is not valid for {y}.");
options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => $"A value is required.");
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "My Test API", Version = "v1" });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
builder.Services.AddCors(options => {
options.AddDefaultPolicy(cfg => {
cfg.WithOrigins(builder.Configuration["AllowedOrigins"]);
cfg.AllowAnyHeader();
cfg.AllowAnyMethod();
});
options.AddPolicy(name: "AnyOrigin",
cfg => {
cfg.AllowAnyOrigin();
cfg.AllowAnyHeader();
cfg.AllowAnyMethod();
});
});
if (string.IsNullOrEmpty(connString))
{
throw new Exception("Connection string not found");
}
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connString));
builder.Services.AddIdentityCore<ApiUser>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequiredLength = 12;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme =
options.DefaultChallengeScheme =
options.DefaultForbidScheme =
options.DefaultScheme =
options.DefaultSignInScheme =
options.DefaultSignOutScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["JWT:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["JWT:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(builder.Configuration["JWT:SigningKey"]))
};
});
builder.Services.AddAuthorization();
builder.Services.AddIdentity<ApiUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
if (app.Configuration.GetValue<bool>("UseDeveloperExceptionPage"))
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
app.UseHttpsRedirection();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/auth/test/1",
[Authorize]
[EnableCors("AnyOrigin")]
[ResponseCache(NoStore = true)]() =>
{
return Results.Ok("You are authorized!");
});
app.MapControllers();
app.Run();
The AccountController's Login method returns a JWT that I am supposed to use to authorize additional API calls.
The book says that if I try the Test method (defined using minimal API at the bottom) in Swagger WITHOUT logging then I should get a http 401 - Unauthorized error. Instead, I get a http 405 "Method Not Allowed" error.
But when I do log in and get the JWT and pass it to the test method, I still get the 405.
What am I doing wrong here?