I have recently been attempting to add swagger to my project, ive followed all the guides but something seems to have gone wrong, ive used swagger for years but ive never had this issue before. It seems that its completely ignoring most of my routes and just using the PUT function, i cant explain it, but ive attached an image of my swagger and all the relevant code, i hope someone has experienced this befiore
Image of my swagger: https://prnt.sc/o7aIJEz0r7xc
Swaggerconfig:
public class SwaggerConfig
{
public static void Register(HttpConfiguration config)
{
config.EnableSwagger(c => {
c.SingleApiVersion("v1", "DefaultApi");
c.ApiKey("Token")
.Description("Bearer token")
.Name("Authorization")
.In("header");
}).EnableSwaggerUi(c =>
{
c.EnableApiKeySupport("Authorization", "header");
});
}
}
Httproute:
HttpConfiguration config = new HttpConfiguration();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
SwaggerConfig.Register(config);
app.UseWebApi(config);
Controller:
{
[Route("api/[controller]")]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class AuthController : ApiController
{
public static DataContext DB = new DataContext();
//public static string test = "This is my current test key";
[HttpGet, Authorize(Roles = "Admin")]
[Route("GetMe")]
public IHttpActionResult GetMe()
{
var userName = User?.Identity?.Name;
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
var role = identity.FindFirst(ClaimTypes.Role).Value;
return Ok(new { userName, role, claims });
}
[HttpGet]
[Route("CorsTest")]
public IHttpActionResult CorsTest()
{
return Ok("xD");
}
//add role to user function
//Users (gets all users)
//
[HttpPost]
[Route("register")]
public IHttpActionResult Register(UserRegister request)
{
if (request.Username.Equals("") || request.Password.Equals(""))
{
return BadRequest("Plase input username and password");
}
string query = "Select * from JWTUSER Where userName= '" + request.Username + "'";
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(query, DB.npgsqlConn);
DataTable dataTable = new DataTable();
sda.Fill(dataTable);
if (dataTable.Rows.Count >= 1)
{
return BadRequest("This username is already being used");
}
User newUser = new User();
CreatePasswordHash(request.Password, out byte[] passwordHash, out byte[] passwordSalt);
newUser.Username = request.Username;
newUser.Role = request.Role;
newUser.PasswordHash = passwordHash;
newUser.PasswordSalt = passwordSalt;
newUser.Email = request.Email;
newUser.PhoneNumber = request.PhoneNumber;
DB.RegisterUser(newUser);
return Ok(newUser);
}
[Authorize(Roles = "Admin")]
[HttpPut]
[Route("updateRole")]
public IHttpActionResult UpdateRole(UpdateRole request)
{
//string userId = SubjectId
//Replace body for register, instead of using username, extract username from database.
User newUser = new User();
if (request.Username.Equals(""))
{
return BadRequest("Plase input username");
}
string query = "Select * from JWTUSER Where userName= '" + request.Username + "'";
NpgsqlDataAdapter sda = new NpgsqlDataAdapter(query, DB.npgsqlConn);
DataTable dataTable = new DataTable();
sda.Fill(dataTable);
if (dataTable.Rows.Count > 1)
{
return BadRequest("Big error more then 1 user with this username");
}
if (dataTable.Rows.Count == 0)
{
return BadRequest("User not found");
}
foreach (var row in dataTable.AsEnumerable())
{
newUser.Username = row.Field<string>("userName");
newUser.Role = row.Field<string>("role");
newUser.PasswordHash = row.Field<byte[]>("passwordHash");
newUser.PasswordSalt = row.Field<byte[]>("passwordSalt");
}
newUser.Role = request.Role;
DB.UpdateRole(newUser);
return Ok("Role update to " + newUser.Role);
}
.net 6 swagger setup
**Program.cs**
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSwaggerGen(opt =>
{
opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description =
"JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345.54321\""
});
opt.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{ Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } },
new string[] { }
}
});
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
//Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
After a few hours of pain i realized that i had forgotten the most important part
config.MapHttpAttributeRoutes();
Thank you everyone for trying to help my broken mind <3.
Related
Summary
What I want is to be able to add a custom AuthorizeAttribute to a method which is then recognised by swagger and displays the Available authorizations popup. The issue I'm having is getting the IOperationFilter to work correctly with the IAuthorizationFilter.
Code
startup.cs
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v2", new OpenApiInfo { Title = "API", Version = "v2" });
// Adds "(Auth)" to the summary so that you can see which endpoints have Authorization
c.OperationFilter<AppendAuthorizeToSummaryOperationFilter<Filters.Authorization2>>();
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Bearer",
Description = "Standard Authorization header using the SessionKey scheme. Example: \"{token}\"",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
c.OperationFilter<Filters.AuthorizeCheckOperationFilter>();
});
controller.cs
[Filters.Authorization2]
[HttpGet]
public ApiResult<List<Request>> GetRequest(int ID)
Authorization2.cs
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class Authorization2 : AuthorizeAttribute, IAuthorizationFilter
{
public Authorization2()
{
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//Get the session from the cache
Session sess = GetSession(context.HttpContext);
if (sess.IsValid)
{
//set sess
}
else
{
//If it's not there then return with bad news
context.Result = Unauthorized();
context.HttpContext.Response.StatusCode = 401;
}
}
}
AuthorizeCheckOperationFilter.cs
internal class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
context.ApiDescription.TryGetMethodInfo(out var methodInfo);
if (methodInfo == null)
return;
var hasAuthorizeAttribute = false;
if (methodInfo.MemberType == MemberTypes.Method)
{
// NOTE: Check the controller itself has Authorize attribute
hasAuthorizeAttribute = methodInfo.DeclaringType.GetCustomAttributes(true).OfType<Authorization2>().Any();
// NOTE: Controller has Authorize attribute, so check the endpoint itself.
// Take into account the allow anonymous attribute
if (hasAuthorizeAttribute)
hasAuthorizeAttribute = !methodInfo.GetCustomAttributes(true).OfType<AllowAnonymousAttribute>().Any();
else
hasAuthorizeAttribute = methodInfo.GetCustomAttributes(true).OfType<Authorization2>().Any();
}
if (!hasAuthorizeAttribute)
return;
if (!operation.Responses.Any(r => r.Key == StatusCodes.Status401Unauthorized.ToString()))
operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new OpenApiResponse { Description = "Unauthorized" });
if (!operation.Responses.Any(r => r.Key == StatusCodes.Status403Forbidden.ToString()))
operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse { Description = "Forbidden" });
// NOTE: This adds the "Padlock" icon to the endpoint in swagger,
// we can also pass through the names of the policies in the string[]
// which will indicate which permission you require.
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
new List<string>()
}
}
};
}
}
With the code as it is, the padlock on the end point shows up and the header is set but the method throws an 500 error:
System.InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
Attempt 1
I tried adding:
services.AddAuthentication(Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme);
but I get the same error. If I try:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.LoginPath = new PathString("/auth/login");
options.AccessDeniedPath = new PathString("/auth/denied");
});
My request redirects to the loginPath, which causes a 404.
Attempt 2
If try a different tacktick and use a TypeFilterAttribute as so:
modify startup.cs
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
//c.OperationFilter<Filters.AuthorizeCheckOperationFilter>();
Authorization.cs
public class AuthorizationHandler : IAuthorizationFilter
{
public AuthorizationHandler()
{
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//Get the session from the cache
Session sess = GetSession(context.HttpContext);
if (sess.IsValid)
{
//set sess
}
else
{
//If it's not there then return with bad news
context.Result = Unauthorized();
context.HttpContext.Response.StatusCode = 401;
}
}
}
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute() : base(typeof(AuthorizationHandler))
{
}
}
and update the method to use [Filters.Authorize] the call to the method works as expected but now every method get's the lock, not just the ones with the attribute.
Question
How do I modify my code to only have locks on the methods with the attribute and process the authorization correctly?
After following this response to a different question:
https://stackoverflow.com/a/61365691/2885471
I managed to get it to work. In essence I created a security definition in the SwaggerGen service configuration, put the security requirement in a IOperationFilter class and then add the the class to the OperationFilters.
I've posted this question before but being new I wanted to learn how to write a "proper" question (in case you've tried to help me and I didn't get back immediately, wanted to get it mostly right if not all hopefully I've got the hang of it)
This is just registration/login code whose purpose is self explanatory, the error am getting (could not load file or assembly 'microsoft aspnetcore razor runtime 3.1 1) happens in a service registration class in the AddControllers() method, I've tried adding the package specified in the error but it didn't work, I tried a few other similar packages but the result has been the same, when I build I get no errors or warnings but when the app runs I get the error, hope this is enough data to work with.
//controller class Login and registration
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class IdentifyMe : Controller
{
private readonly IIdentify _identify;
public IdentifyMe(IIdentify identifying)
{
_identify = identifying;
}
[HttpGet(Api_Routes.Identity.Register)]
public async Task<IActionResult> Register(UserRegistration register)
{
if (!ModelState.IsValid)
{
return BadRequest(new Unauthenticated
{
Errors=ModelState.Values.SelectMany(x=>x.Errors.Select(xx=>xx.ErrorMessage))
});
}
var authresponce = await _identify.RegisterAsync(register.Email, register.Password, register.User_Name);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
[HttpGet(Api_Routes.Identity.Login)]
public async Task<IActionResult> LoginAsync(User_login login)
{
var authresponce = await _identify.LoginAsync(login.Password, login.email);
if (!authresponce.Success)
{
return BadRequest(new Unauthenticated
{
Errors = authresponce.Errors
});
}
return Ok(new Authenticated
{
Token = authresponce.Token
});
}
}
// service registration
public class Dbinstaller : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
var jwt = new JWTsettings();
configuration.Bind(nameof(jwt), jwt);
services.AddSingleton(jwt);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.SaveToken = true;
var TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwt.Secret)),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
});
services.AddSwaggerGen(x =>
{
x.SwaggerDoc("v1", new OpenApiInfo { Title = "TXC API", Version = "v1" });
var security = new Dictionary<string, IEnumerable<string>>
{
{"Bearer", new string[0]}
};
x.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description="JWT Authorization Header using the bearer scheme",
Name="Authorization",
In=ParameterLocation.Header,
Type=SecuritySchemeType.ApiKey
});
x.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{new OpenApiSecurityScheme{Reference=new OpenApiReference
{
Id="Bearer",
Type=ReferenceType.SecurityScheme
}
},new List<string>() }
});
});
services.AddDbContext<DataContext>(opt =>
opt.UseSqlServer(configuration.GetConnectionString("TXC Connection")));
services.AddIdentityCore<IdentityUser>();
}
}
//service registration, specified error occurs in Add controllers()
public class In_App_Componentes : IInstaller
{
public void Cleanner(IConfiguration configuration, IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(p => p.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IX_Change, The_center>();
services.AddScoped<IIdentify, Identify>();
}
}
//service implementation class
public class Identify : IIdentify
{
private readonly UserManager<IdentityUser> _manager;
private readonly JWTsettings jwtset;
public Identify(UserManager<IdentityUser> userManager, JWTsettings jW)
{
_manager = userManager;
jwtset = jW;
}
public async Task<Authentication_result> RegisterAsync(string email, string password, string Username)
{
var exists = _manager.FindByEmailAsync(email);
if (exists != null)
{
return new Authentication_result
{
Errors = new[] { "User with this email already exists" }
};
}
var newPerson = new IdentityUser
{
Email = email,
UserName = Username
};
var Creation = await _manager.CreateAsync(newPerson, password);
if (!Creation.Succeeded)
{
return new Authentication_result
{
Errors = new[] { "Invalid user!" }
};
}
return Generate_Authentication_Result(newPerson);
}
public async Task<Authentication_result> LoginAsync(string email, string Password)
{
var user = await _manager.FindByEmailAsync(email);
if (user == null)
{
return new Authentication_result
{
Errors = new[] { "User does not exists" }
};
}
var validate_password = await _manager.CheckPasswordAsync(user, Password);
if (!validate_password)
{
return new Authentication_result
{
Errors = new[] { "" }
};
}
return Generate_Authentication_Result(user);
}
private Authentication_result Generate_Authentication_Result(IdentityUser newPerson)
{
var Tokenhandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(jwtset.Secret);
var TokenDescripter = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(JwtRegisteredClaimNames.Sub, newPerson.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Email, newPerson.Email),
new Claim("id",newPerson.Id)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = Tokenhandler.CreateToken(TokenDescripter);
return new Authentication_result
{
Success = true,
Token = Tokenhandler.WriteToken(token)
};
}
}
Ciao, I tried to search Microsoft.AspNetCore.Razor.Runtime v. 3.1.1 on NuGet but I found only 2.2.0 . I didn't found any reference to 3.1.1 . Anyway, downgrading Microsoft.AspNetCore.Identity.UI to 3.1.0 should solve your problem as mentioned here
How can I implement LDAP authentication in ASP.NET Core?
I want to create a ldap authentication, but I'm not sure about my code - any help please.
I created an ASP.NET Core 2.0 application, and when I try get response, an error happens and I don't know how I can resolve this problem.
My code:
public interface IAuthenticationService
{
bool ValidateUser(string username, string password);
}
public class LdapAuthenticationService : IAuthenticationService
{
public static bool ValidateUser(string username, string password)
{
Dictionary<string, object> properties;
string _path = string.Format("LDAP://{0}", "ADSLOCAL");
string _filterAttribute;
DirectoryEntry entry = new DirectoryEntry(_path, username, password);
properties = new Dictionary<string, object>();
try
{
//Bind to the native AdsObject to force authentication.
object obj = entry.NativeObject;
if (obj != null)
{
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("givenName");
search.PropertiesToLoad.Add("sn");
SearchResult result = search.FindOne();
if (result == null)
{
return false;
}
else
{
if (result.Properties["sn"].Count != 0)
properties.Add("FirstName", result.Properties["sn"][0]);
if (result.Properties["givenName"].Count != 0)
properties.Add("LastName", result.Properties["givenName"][0]);
}
// Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
else
{
return false;
}
}
catch (Exception ex)
{
throw new Exception("err:" + ex.Message);
}
return true;
}
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<IAuthenticationService,LdapAuthenticationService>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
services.AddCors();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Use(async (ctx,next)=>
{
await next();
if (ctx.Response.StatusCode == 204)
{
ctx.Response.ContentLength = 0;
}
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(builder =>
builder.WithOrigins(Configuration["ApplicationSettings:Client_URL"].ToString())
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseAuthentication();
app.UseMvc();
}
}
}
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login(LoginModel model)
{
bool result = LdapAuthenticationService.ValidateUser(model.UserName, model.Password);
if (result)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.UserName),
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties {};
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity), authProperties);
}
return Ok();
}
Change the Filter and try it
SearchResultCollection results;
DirectorySearcher ds = null;
DirectoryEntry de = new DirectoryEntry("LDAP://domain");
ds = new DirectorySearcher(de);
ds.Filter = "(&(objectCategory=User)(objectClass=person)(name=" + userName + "))";
results = ds.FindAll();
foreach (SearchResult sr in results)
{
// Using the index zero (0) is required!
Console.WriteLine(sr.Properties["name"][0].ToString());
}
I have created a endpoint in asp.net webapi that accepts two parameters. I am using postman to test it but the endpoint is not hitting. I have other methods that hit which dont have parameters passed in. Not sure why this is giving a problem.
I tried calling the endpoint using the following url in postman
http://localhost:56888/api/terms/reviewlegalfundclass/11166/2
I also changed the attribute route to
[Route("api/terms/{fundclassId}/{reviewtypeId}")]
What I have seen works is this combination
[Route("api/terms/reviewlegalfundclass/{id}")]
http://localhost:56888/api/terms/reviewlegalfundclass/11166 - postman call
Here is how the routing is configured in the webapi.config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "WebApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
config.Formatters.Add(new BsonMediaTypeFormatter());
}
}
Controller
[System.Web.Http.HttpPut]
[SkipTokenAuthorization]
[Route("api/terms/reviewlegalfundclass/{fundClassId}/{reviewTypeId}")]
public string ReviewLegalFundClass(int fundClassId, int reviewTypeId)
{
var svc = Resolve<IUserIdentityService>();
var user = svc.GetCurrentUser();
try
{
var fcService = GetService<LEGAL_FUND_CLASS>();
var fundClass = fcService.Get(fundClassId);
var response = "N/A";
var users = GetViewService<V_PERMAL_USER_ALL>().GetAll();
if (reviewTypeId == (int)LegalFundClassReviewType.Fees)
{
fundClass.FEES_LAST_REVIEWED_BY_ID = user.ID;
fundClass.FEES_LAST_REVIEWED_DATE = DateTime.Now;
var feeUsr = users.FirstOrDefault(x => x.ID == fundClass.FEES_LAST_REVIEWED_BY_ID);
response = $"Last Reviewed: {feeUsr?.LOGIN_NAME} {fundClass.FEES_LAST_REVIEWED_DATE.Value.ToString(PermalConstants.DateFormat2)}";
}
else if (reviewTypeId == (int)LegalFundClassReviewType.Terms)
{
fundClass.TERMS_LAST_REVIEWED_BY_ID = user.ID;
fundClass.TERMS_LAST_REVIEWED_DATE = DateTime.Now;
var termsUsr = users.FirstOrDefault(x => x.ID == fundClass.TERMS_LAST_REVIEWED_BY_ID);
response = $"Last Reviewed: {termsUsr?.LOGIN_NAME} {fundClass.TERMS_LAST_REVIEWED_DATE.Value.ToString(PermalConstants.DateFormat2)}";
}
else throw new Exception("Invalid fund class review type");
fcService.Update(fundClass);
return response;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
Ok, so this one has been highly frustrating and I cannot seem to figure out how to fix it, hoping someone can point me in the right direction.
So if I may give a run down of the architecture:
Solution
IdentityServer Layer
WebApi Layer
MVC layer (as the client)
The Issue
When trying to access the user that has been authenticated by the IdentityServer it always returns false
if (this.User.Identity.IsAuthenticated)
{
var identity = this.User.Identity as ClaimsIdentity;
foreach (var claim in identity.Claims)
{
Debug.Write(claim.Type + " " + claim.Value);
}
}
I've spent many hours trying to figure why I do not have access to the user.
IdentityServer
public void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
var idServerServiceFactory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get())
.UseInMemoryUsers(Users.Get());
var options = new IdentityServerOptions
{
Factory = idServerServiceFactory,
SiteName = "Proderty Security Token Service",
IssuerUri = "https://localhost:44300/identity",
PublicOrigin = "https://localhost:44300/",
SigningCertificate = LoadCertificate()
};
idsrvApp.UseIdentityServer(options);
});
}
public static IEnumerable<Client> Get()
{
return new[]
{
new Client()
{
ClientId = "prodertyclientcredentials",
ClientName = "Proderty (Client Credentials)",
Flow = Flows.ClientCredentials,
ClientSecrets = new List<Secret>()
{
new Secret("Some Secret here".Sha256())
},
AllowAccessToAllScopes = true
},
new Client()
{
ClientId = "prodertyauthcode",
ClientName = "Proderty (Authorization Code)",
Flow = Flows.AuthorizationCode,
RedirectUris = new List<string>()
{
"http://localhost:10765/prodertycallback"
},
ClientSecrets = new List<Secret>()
{
new Secret("Some Secret here".Sha256())
},
AllowAccessToAllScopes = true
},
new Client()
{
ClientId = "prodertyhybrid",
ClientName = "Proderty (Hybrid)",
Flow = Flows.Hybrid,
RedirectUris = new List<string>()
{
"http://localhost:10765"
},
AllowAccessToAllScopes = true
}
};
}
public static IEnumerable<Scope> Get()
{
return new List<Scope>()
{
new Scope
{
Name = "prodertymanagment",
DisplayName = "Proderty Management",
Description = "Allow the application to manage proderty on your behalf.",
Type = ScopeType.Resource
},
StandardScopes.OpenId,
StandardScopes.Profile
};
}
return new List<InMemoryUser>()
{
new InMemoryUser()
{
Username = "Terry",
Password = "Bob",
Subject = "b053d546-6ca8-b95c-77e94d705ddf",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "Terry"),
new Claim(Constants.ClaimTypes.FamilyName, "Wingfield")
}
},
new InMemoryUser()
{
Username = "John",
Password = "Bob",
Subject = "bb61e881-3a49-8b62-c13dbe102018",
Claims = new[]
{
new Claim(Constants.ClaimTypes.GivenName, "John"),
new Claim(Constants.ClaimTypes.FamilyName, "Borris")
}
}
};
As you can see the IdentityServer seems positively intact. Once called I expect that I should be access the user on the MVC client.
WebApi
public void Configuration(IAppBuilder app)
{
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://localhost:44300/identity",
RequiredScopes = new[] { "prodertymanagment" }
});
}
[Authorize]
public class TestController : ApiController
{
// GET api/<controller>
public string GetOne()
{
return "If you can see this, then we must be authorized! - GETONE - Coming From Test Controller";
}}
}
MVCClient
public static HttpClient GetClient()
{
var client = new HttpClient();
var accessToken = Token.RequestAccessTokenAuthorizationCode();
if (accessToken != null)
{
client.SetBearerToken(accessToken);
}
client.BaseAddress = new Uri("https://localhost:44301/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
public static string RequestAccessTokenAuthorizationCode()
{
var cookie = HttpContext.Current.Request.Cookies.Get("ProdertyCookie");
if (cookie != null && cookie["access_token"] != null)
{
return cookie["access_token"];
}
var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
"https://localhost:44300/identity/connect/authorize"
);
var state = HttpContext.Current.Request.Url.OriginalString;
var url = authorizeRequest.CreateAuthorizeUrl(
"prodertyauthcode",
"code",
"prodertymanagment",
"http://localhost:10765/prodertycallback",
state);
HttpContext.Current.Response.Redirect(url);
return null;
}
// GET: ProdertyCallback
public async Task<ActionResult> Index()
{
var authCode = Request.QueryString["code"];
var client = new TokenClient(
"https://localhost:44300/identity/connect/token",
"prodertyauthcode",
"Some Secret here"
);
var tokenResponse = await client.RequestAuthorizationCodeAsync(
authCode,
"http://localhost:10765/prodertycallback"
);
Response.Cookies["ProdertyCookie"]["access_token"] = tokenResponse.AccessToken;
return Redirect(Request.QueryString["state"]);
}
With the code above everything works, If not logged in I'm pushed to the IdentityServer login page, My callback fires and then returned to the originating page. I also have a access token.
Now that I'm authorized to access the page, I expect to have access to the user but I don't which is highly frustrating.
I'm very aware that it's the app that's being authenticated but I'm sure the purpose was to access the user that authenticated the app (As a user of the client system too).
public async Task<ActionResult> Index()
{
if (this.User.Identity.IsAuthenticated)
{
var identity = this.User.Identity as ClaimsIdentity;
foreach (var claim in identity.Claims)
{
Debug.Write(claim.Type + " " + claim.Value);
}
}
var httpClient = ProdertyHttpClient.GetClient();
var test = await httpClient.GetAsync("api/test/getone").ConfigureAwait(false);
if (test.IsSuccessStatusCode)
{
var stringContent = await test.Content.ReadAsStringAsync().ConfigureAwait(false);
return Content(stringContent);
}
else
{
return Content(ExceptionHelper.GetExceptionFromResponse(test).ToString());
}
}
The only thing that springs to mind is using the Authorize Attribute which attempts to use the core asp.net identity model which I have not install because I'll have a custom data store.
Any help greatly appreciated!