post_logout_redirect_uri ASP NET Core 2.2 AzureAD Razor Class Library RCL - razor

We have tried using the sample
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/
Walked through the sample and all works.
We can't get it to redirect after logout process. Also, it seems the account controller is not there but it is called in _layout.chtml this must be something new.

Yes, it does redirect to the application - what I'd like it to do is redirect to a different page.
You can redirect user to another page after sign-out by setting the OnSignedOutCallbackRedirect event :
In Startup.cs add using System.Threading.Tasks;
Config your new redirect url in OnSignedOutCallbackRedirect event :
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
options.Events.OnSignedOutCallbackRedirect = (context) =>
{
context.Response.Redirect("/Home/About");
context.HandleResponse();
return Task.CompletedTask;
};
});

The account controller code is built into the framework now. You can see it in Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureAD.Controllers.Internal (see https://github.com/aspnet/AADIntegration/blob/0efa96de73e3235fbfc55cfe51d9547a693010cc/src/Microsoft.AspNetCore.Authentication.AzureAD.UI/Areas/AzureAD/Controllers/AccountController.cs):
namespace Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureAD.Controllers.Internal
{
[AllowAnonymous]
[Area("AzureAD")]
[NonController]
[Route("[area]/[controller]/[action]")]
internal class AccountController : Controller
{
public IOptionsMonitor<AzureADOptions> Options
{
get;
}
public AccountController(IOptionsMonitor<AzureADOptions> options)
{
this.Options = options;
}
[HttpGet("{scheme?}")]
public IActionResult SignIn([FromRoute] string scheme)
{
scheme = scheme ?? AzureADDefaults.AuthenticationScheme;
string str = base.Url.Content("~/");
return this.Challenge(new AuthenticationProperties()
{
RedirectUri = str
}, new String[] { scheme });
}
[HttpGet("{scheme?}")]
public IActionResult SignOut([FromRoute] string scheme)
{
scheme = scheme ?? AzureADDefaults.AuthenticationScheme;
AzureADOptions azureADOption = this.Options.Get(scheme);
string str = base.Url.Page("/Account/SignedOut", null, null, base.Request.Scheme);
return this.SignOut(new AuthenticationProperties()
{
RedirectUri = str
}, new String[] { azureADOption.CookieSchemeName, azureADOption.OpenIdConnectSchemeName });
}
}
}
Unfortunately, I have not be able to force a redirect after logout. Instead, I see a page that says "You have successfully signed out." I'd like to know how to redirect the user back to the Index page.

I had to override the signedOut page manually by adding this to a controller:
[AllowAnonymous]
[HttpGet]
[Route("/MicrosoftIdentity/Account/SignedOut")]
public IActionResult SignedOut()
{
return Redirect(<MyRealSignedOutRedirectUri>);
}

Related

How to add "allow-downloads" to the sandbox attributes list

I am trying to get files from my MVC project (asp.net core 3.1)
I created a link
<a asp-action="#nameof(HomeController.Download)" asp-controller="#HomeController.Name" asp-route-fileName="fileName.doc" download>FileName</a>
I created a controller
public async Task<ActionResult> Download(string fileName) {
var path = Path.Combine(_hostingEnvironment.WebRootPath, fileName);
if (!System.IO.File.Exists(path)) {
return NotFound();
}
var fileBytes = await System.IO.File.ReadAllBytesAsync(path);
var response = new FileContentResult(fileBytes, "application/vnd.openxmlformats-officedocument.wordprocessingml.document") {
FileDownloadName = fileName
};
return response;
}
In Chrome i get the warning
Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details.
Following the link, how can i add:
add "allow-downloads" to the sandbox attributes list to opt in
The file is downloaded if i click the button from Microsoft Edge
This is what I had to do for my project, hopefully it will point you to the right direction.
In your Startup.cs
services.AddMvc(options =>{
options.Filters.Add(new MyActionFilterAttribute());
}
Then in MyActionFilterAttribute.cs
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Headers.Add("Content-Security-Policy", "sandbox allow-downloads; " )
}
}

HttpPost with JSON parameter is not working in ASP.NET Core 3

So, I migrated my RestAPI project to ASP.NET Core 3.0 from ASP.NET Core 2.1 and the HttpPost function that previously worked stopped working.
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody]Application login)
{
_logger.LogInfo("Starting Login Process...");
IActionResult response = Unauthorized();
var user = AuthenticateUser(login);
if (user != null)
{
_logger.LogInfo("User is Authenticated");
var tokenString = GenerateJSONWebToken(user);
_logger.LogInfo("Adding token to cache");
AddToCache(login.AppName, tokenString);
response = Ok(new { token = tokenString });
_logger.LogInfo("Response received successfully");
}
return response;
}
Now, the login object has null values for each property. I read here, that
By default, when you call AddMvc() in Startup.cs, a JSON formatter, JsonInputFormatter, is automatically configured, but you can add additional formatters if you need to, for example to bind XML to an object.
Since AddMvc was removed in aspnetcore 3.0, now I feel this is why I am unable to get my JSON object anymore. My Startup class Configure function looks like this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseRouting();
//app.UseAuthorization();
//app.UseMvc(options
// /*routes => {
// routes.MapRoute("default", "{controller=Values}/{action}/{id?}");
//}*/);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
The request I am sending through postman (raw and JSON options are selected)
{
"AppName":"XAMS",
"licenseKey": "XAMSLicenseKey"
}
UPDATES
Postman Header: Content-Type:application/json
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//_logger.LogInformation("Starting Log..."); //shows in output window
services.AddSingleton<ILoggerManager, LoggerManager>();
services.AddMemoryCache();
services.AddDbContext<GEContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
services.AddRazorPages();
//Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "https://localhost:44387/";
options.Audience = "JWT:Issuer";
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("GuidelineReader", p => {
p.RequireClaim("[url]", "GuidelineReader");
});
});
//
}
Application.cs
public class Application
{
public string AppName;
public string licenseKey;
}
With you updated code, I think the reason is you didn't create setter for your properties.
To fix the issue, change your Application model as below:
public class Application
{
public string AppName {get;set;}
public string licenseKey {get;set;}
}

Adding aspnet-api-versioning prevents UrlHelper from generating Controller API routes within a Razor Pages request

I can create a file->new aspnetcore API project and use the IUrlHelper to generate a route by name without any issues.
[Route("api/[controller]")]
public class ValuesController : Controller
{
public const string GetValues = "GetValues";
public const string GetValuesById = "GetValuesById";
public static string[] Values = new[] { "value1", "value2", "value3", };
// GET api/values
[HttpGet(Name = GetValues)]
public IEnumerable<object> Get()
{
var result = new List<object>();
for(int index = 0; index < Values.Length - 1; index++)
{
string routeForElement = this.Url.RouteUrl(GetValuesById, new { Id = index });
result.Add(new { Value = Values[index], Route = routeForElement });
}
return result;
}
// GET api/values/5
[HttpGet("{id}", Name = GetValuesById)]
public string Get(int id)
{
if (id > (Values.Length - 1))
{
return "Invalid Id";
}
return Values[id];
}
}
When the response is sent back, I correctly have the routes that I've created:
[
{
"value": "value1",
"route": "/api/v1/Values/0"
},
{
"value": "value2",
"route": "/api/v1/Values/1"
},
{
"value": "value3",
"route": "/api/v1/Values/2"
}
]
I can then use the Visual Studio Scaffolding to create a Razor Page and continue to generate the same route without any issues within my Razor Page:
Model
public class IndexModel : PageModel
{
public List<string> Routes { get; set; } = new List<string>();
public void OnGet()
{
for (int index = 0; index < ValuesController.Values.Length; index++)
{
string routeForElement = this.Url.RouteUrl(ValuesController.GetValuesById, new { Id = index });
Routes.Add(routeForElement);
}
}
}
Page
#page
#model UrlHelperWithPages.Pages.IndexModel
#foreach(string route in Model.Routes)
{
<h4>#route</h4>
}
This renders the routes without issue.
If I add the aspnet-api-versioning nuget package and configure it's services:
services.AddApiVersioning();
My API controller continues to work with the following modification. Any request that is destined for this controller has the routes generated correctly.
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ValuesController : Controller
However the Razor Pages stops working when we try to generate a route from within a Razor Pages request. The RouteUrl method now returns null. I've tried updating the route data provided to the RouteUrl method so that I pass a hard-coded version (for testing) and it doesn't work either.
new { version = 1, Id = index }
Is there any configuration that needs to happen on the api versioning package to support pages? We have razor pages that we want to generate API routes for rendering in the page, but it doesn't seem to work.

How to send JSON data from ASP.NET MVC 5 back to client side Angular (i.e. during authentication)

i have to redirect to a different view if authenticated and I need to send JSON data containing the userid and username to be used by AngularJS.
The Redirect works but I am not sure how to send the authenticated user information as JSON data to the client-side.
Can some one help me?
Here are some code that I have in MVC side...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using TVS.Core.Interfaces;
using TVS.Domain;
using TVS.UI.Models;
namespace TVS.UI.Controllers
{
public class homeController : Controller
{
private readonly IUser _IUser;
public homeController(IUser IUser)
{
_IUser = IUser;
}
public ActionResult RedirectAngular()
{
return View();
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserModel model)
{
if (ModelState.IsValid)
{
if (_IUser.IsValidUser(model.Login,model.Password))
{
FormsAuthentication.SetAuthCookie(model.Login, false);
return RedirectToAction("RedirectAngular", "home");
}
else
{
// ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
//}
}
return View();
}
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "home");
}
}
}
You could just put it in a model or ViewBag and render the view if the logon form is actually a page and whatever page you redirect to when authenticated is another. Then just use the model / ViewBag to populate a page / app Setting javascript global or push them into angular value or constant like so:
//Controller Action
public ActionResult Logon(string username, string password) {
//authentication successful...
//using ViewBag, but model will do as well..
ViewBag.UserId = 12; //for example...
ViewBag.UserName = "John Doe";
return View();
}
#* View
somewhere near end of <body> tag...
*#
<script src=".../angular.js">
<script>
//or load this from a file...
angular.module('moduleName', []);
</script>
<script>
//this need to be in page!!
angular.moduel('moduleName')
.value('appSettings', {
userId : #ViewBag.UserId,
userName: '#ViewBag.UserName'
});
</script>
<script>
//this can be loaded from file...
angular.controller('controllerName', ctrlFn);
ctrlFn.$inject = ['$log', 'appSettings'];
function ctrlFn($log, appSettings) {
//do something with appSetting here...
$log.info(appSettings.userId, appSettings.userName);
}
</script>
If this is not what you meant, and you are pushing the form info as Ajax and your Angular is actually a SPA then you need to use AJAX to marshal data between server and client side...
Just return a JsonResult like so...
[HttpPost]
public ActionResult Logon(string username, string password)
{
//authenticate here... assume succesful...
var user = new { userId = 12, userName = "John Doe" }
return Json(user, "application/json", Encoding.UTF8);
}
in your angular, just handle whatever getting returned back like so:
<script>
angular.module('moduleName')
.factory('loginSvc', svcFn);
svcFn.$inject = ['$http'];
function svcFn($http) {
var svc = {
login: login
};
return svc;
function login(username, password) {
return $http.post(
'/api/login',
angular.toJson({username: username, password: password});
}
}
angular.module('moduleName')
.controller('controllerName', ctrlFn);
ctrlFn.$inject = ['$log', 'loginSvc'];
function ctrlFn($log, loginSvc) {
var self = this;
self.username = "";
self.password = "";
self.login = login;
function login() {
loginSvc.login(username, password)
.then(handleLoginSuccess)
.catch(handleLoginFailure);
}
function handleLoginSuccess(userInfo) {
$log.info(userInfo); //this should contain logged in user id and username
}
//some other functions here...
}
</script>

ASP.NET Web API always returns 401 unauthorized error

I am using Custom authorization on asp.net web api.I have followed the following link
http://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han
I use the attribute name in my controller like this
[mycustomattribute]
public class userController : apicontroller
{
}
but it always shows 401 unauthorized exception inspite of authentication status being authorized. I have followed exactly wat it is in the link for creating custom authorize attribute.
my custom authorize class
public class tokenAuthorize : AuthorizeAttribute
{
DBEntity _objScrumDBEntities = new DBEntity ();
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (actionContext.Request.Headers.GetValues("authenticationToken") != null)
{
// get value from header
string authenticationTokenValue = Convert.ToString(actionContext.Request.Headers.GetValues("authenticationToken").FirstOrDefault());
ObjectParameter m_tokenParam = new ObjectParameter("status", typeof(string));
_objScrumDBEntities.validateToken(authenticationTokenValue, m_tokenParam);
string status = Convert.IsDBNull(m_tokenParam.Value) ? null : (string)m_tokenParam.Value;
if (status == "false")
{
HttpContext.Current.Response.AddHeader("authenticationToken", authenticationTokenValue);
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "NotAuthorized");
// actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
return;
}
else
{
HttpContext.Current.Response.AddHeader("authenticationToken", authenticationTokenValue);
HttpContext.Current.Response.AddHeader("AuthenticationStatus", "Authorized");
return;
}
//return;
}
//actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.ExpectationFailed);
//else
// actionContext.Response.ReasonPhrase = "Please provide valid inputs";
}
}
and my controller
[tokenAuthorize]
public class myController : ApiController
{
public IEnumerable<organization> Get()
{
return _objOrgRepository.GetAll();
}
It seems that System.Web.Security.Roles.GetRolesForUser(Username) does not get automatically hooked up when you have a custom AuthorizeAttribute and a custom RoleProvider.
So, in your custom AuthorizeAttribute you need to retrieve the list of roles from your data source and then compare them against the roles passed in as parameters to the AuthorizeAttribute.Try the below code
public class myController : ApiController
{
[RequestKeyAuthorizeAttribute(Roles="Admin,Bob,Administrator,Clue")]
public HttpResponseMessage Get()
{
return Request.CreateResponse(HttpStatusCode.OK, "RequestKeyAuthorizeTestController");
}