Blazor Server: Read Response information in Callback - oauth-2.0

I am working on a Hobby application in Blazor Server. The first stage in this application involves directing the user to an external site to have them authorize my app (OAuth2) to access their information from the external site. Part of this process involves a return authorization code. I have a Webforms version of this application where the Start page redirects the user to a URL and then a callback page completes the task of capturing a token. I cannot figure out how to capture the "Code" from the response in the Callback from the external server. Any help?
Start Page:
public void Btn_UM_CreateNew_click(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(TestNew));
}
private async Task TestNew()
{
using (var eveAuth = new EveAuth())
{
var url = await eveAuth.EveAuthGet();
Response.Redirect(url);
}
}
Callback page (where I extract the "Code" value:
protected async void Page_Load(object sender, EventArgs e)
{
var resp = Request["code"];
using (EveAuth eveCallback = new EveAuth())
{
await eveCallback.EveTokenGet(resp);
}

Is the "Code" passed to the Callback page via a query string?
If so you can refer to the below article
https://chrissainty.com/working-with-query-strings-in-blazor/

Related

Spring Security 4 intercept anonymous user AccessDeniedException

My website allows unauthenticated (anonymous) users to perform a search of content which requires authentication to view. Currently, when they click on a link to view the details of one of the content items on the search results page Spring Security correctly identifies the user as unauthenticated and displays the login page. However, I would like to intervene and instead display a page to encourage the anonymous user to sign up for the website. I have traced what is happening in the filter chain but it's not clear to me whether I should extend an existing filter or handler or create a custom filter or handler. If it's the later I'm not sure where it should go.
When I run this through debug I can see the following happening:
ExceptionTranslationFilter.doFilter executes FilterSecurityInterceptor which determines that the detail page requires authentication (returns a -1 vote and throws an AccessDeniedException)
ExceptionTranslationFilter catches the exception, determines the user is anonymous and calls the authenticationEntryPoint, in this case LoginUrlAuthenticationEntryPoint
LoginUrlAuthenticationEntryPoint invokes the DefaultRedirectStrategy which redirects to the login page
So, basically I need to override the redirection to the login page for this one use case. My best guess is to create a custom filter that checks for the combination of an anonymous user accessing this specific detail page and forces a redirect to the join up page, inserting the filter in the chain after ExceptionTranslationFilter. Or is this total overkill for handling a single page redirect and there's an easier way to accomplish this?
For anyone interested, here's the code for the custom auth entry point, borrowing from LoginUrlAuthenticationEntryPoint and ExceptionTranslationFilter.
public class CustomAuthLoginEntryPoint extends LoginUrlAuthenticationEntryPoint {
private final Logger logger = LoggerFactory.getLogger(getClass());
private PortResolver portResolver = new PortResolverImpl();
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private RequestCache requestCache = new HttpSessionRequestCache();
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private String joinPageUrl;
public CustomAuthLoginEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
logger.debug("commence");
String redirectUrl = null;
if (!StringUtils.isBlank(joinPageUrl)) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || trustResolver.isAnonymous(auth)) {
SavedRequest savedRequest = requestCache.getRequest(request, response);
redirectUrl = savedRequest.getRedirectUrl();
if (redirectUrl.indexOf("viewDetail") > 0) {
String joinPageUrl = buildRedirectUrlToJoinPage(request);
logger.debug("Redirecting to '" + joinPageUrl + "'");
redirectStrategy.sendRedirect(request, response, joinPageUrl);
return;
}
}
}
super.commence(request, response, authException);
}
protected String buildRedirectUrlToJoinPage(HttpServletRequest request) {
int serverPort = portResolver.getServerPort(request);
String scheme = request.getScheme();
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme(scheme);
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(serverPort);
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setPathInfo(joinPageUrl);
return urlBuilder.getUrl();
}
public void setJoinPage(String joinPageUrl) {
this.joinPageUrl = joinPageUrl;
}
}
I added this to my WebSecurityConfigurerAdapter:
#Bean
public CustomAuthLoginEntryPoint customAuthLoginEntryPoint() {
CustomAuthLoginEntryPoint entryPoint = new CustomAuthLoginEntryPoint("/user/login");
entryPoint.setJoinPage("/user/join");
return entryPoint;
}
and the http configure:
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.authenticationEntryPoint(customAuthLoginEntryPoint())

Changes to cookie domain for outgoing responses ignored for ServiceStack requests

I have a multi-tenant website (e.g. several different sites, each with it's own domain, all in the same project, separated using MVC areas), where the authentication cookie has the domain manually set when the user logs in so that it is available to all subdomains (but not the various other sites in the project, this is not an SSO).
So a user logins a x.foo.com, the cookie domain is set for foo.com, so that it also works at y.foo.com and z.foo.com. However, because of the other domains being served from the same project the auth cookie domain cannot be set in the web.config in the usual manner, instead it is set manually when the user logins in like so:
public HttpCookie GetAuthenticationCookie(string username)
{
var cookieDomain = UrlHelper.GetTopAndSecondLevelDomain();
var authenticationCookie = FormsAuthentication.GetAuthCookie(username, false);
authenticationCookie.Domain = cookieDomain;
return authenticationCookie;
}
This works fine, but of course can cause a problem when the cookie is automatically refreshed for sliding expiration. So we have an HTTP module which is hooked into the PostRequestHandlerExecute event of our MVC app to look for auth cookies that were set into the response during the request, and overriding the domain:
public class AuthenticationCookieDomainInterceptorModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += UpdateAuthenticationCookieExpiry;
}
private void UpdateAuthenticationCookieExpiry(object sender, EventArgs e)
{
var app = (HttpApplication) sender;
var cookieDomain = UrlHelper.GetTopAndSecondLevelDomain();
var authenticationCookie = GetCookieFromResponse(app.Context.Response, FormsAuthentication.FormsCookieName);
if (authenticationCookie != null)
{
if (authenticationCookie.Domain == null || !string.Equals(authenticationCookie.Domain, cookieDomain, StringComparison.InvariantCultureIgnoreCase))
{
authenticationCookie.Domain = cookieDomain;
}
}
}
private HttpCookie GetCookieFromResponse(HttpResponse response, string cookieName)
{
var cookies = response.Cookies;
for (var i = 0; i < cookies.Count; i++) {
if (cookies[i].Name == cookieName)
{
return cookies[i];
}
}
return null;
}
}
This is also works fine unless the request is to our ServiceStack front end which we use to handle our AJAX requests. In that case the module fires as normal, picks up the cookie if its been set, changes the domain as it should, but when the response is sent back to the client the changes to the cookie are ignored.
Is there any reason why the cookie changes wouldn't be saved to the response in this scenario? My guess would be something to do with the fact ServiceStack uses an HttpHandler to hook in the request cycle in the first place, so we are not going through the normal MVC request life-cycle.

SignalR with sliding expiration with Forms Authentication

I got a problem when using SignalR with sliding expiration with Forms Authentication.
Because of the SignalR keep polling from server, user auth never expired...
I've researched a lot... and found an interesting article from http://www.asp.net/signalr/overview/security/introduction-to-security#reconcile
They said:
the user's authentication status may change if your site uses sliding expiration with Forms Authentication, and there is no activity to keep the authentication cookie valid. In that case, the user will be logged out and the user name will no longer match the user name in the connection token.
I need to have user's auth expired if he idle for 20 minutes, but I can't..
any ideas?
Thanks a lot!
I ran into this same issue. I found one method posted on GitHub issues that utilizes an HttpModule to remove the auth cookie at the end of the pipeline. This worked great in my scenario.
https://github.com/SignalR/SignalR/issues/2907
public class SignalRFormsAuthenticationCleanerModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
private bool ShouldCleanResponse(string path)
{
path = path.ToLower();
var urlsToClean = new string[] { "/signalr/", "<and any others you require>" };
// Check for a Url match
foreach (var url in urlsToClean)
{
var result = path.IndexOf(url, StringComparison.OrdinalIgnoreCase) > -1;
if (result)
return true;
}
return false;
}
protected void OnPreSendRequestHeaders(object sender, EventArgs e)
{
var httpContext = ((HttpApplication)sender).Context;
if (ShouldCleanResponse(httpContext.Request.Path))
{
// Remove Auth Cookie from response
httpContext.Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
return;
}
}
}

Mini profiler not displaying ajax request information?

I'm using the mini-profiler on my asp.net MVC 3 application. I've implemented the profiler using the mvc nuget package. Everything works ok for standard page requests i get profile information sql everything.
However ajax requests don't seem to display initially. Just to let me confirm the request are completed without error. I've debugged this and they complete they also return http 200 responses in fiddler.
There is no request by the mini profiler that accompanies the ajax request. When i then navigate to another page i.e. a standard page request all the ajax request that were made on the last page are now displayed.
This is my mini profiler configuration page in App_Start
public static class MiniProfilerPackage
{
public static void PreStart()
{
//Setup sql formatter
MiniProfiler.Settings.SqlFormatter = new OracleFormatter();
//Make sure the MiniProfiler handles BeginRequest and EndRequest
DynamicModuleUtility.RegisterModule(typeof(MiniProfilerStartupModule));
//Setup profiler for Controllers via a Global ActionFilter
GlobalFilters.Filters.Add(new ProfilingActionFilter());
//Settings
MiniProfiler.Settings.PopupShowTimeWithChildren = true;
MiniProfiler.Settings.PopupShowTrivial = false;
//Ignore glimpse details in miniprofiler
var ignored = MiniProfiler.Settings.IgnoredPaths.ToList();
ignored.Add("Glimpse.axd");
MiniProfiler.Settings.IgnoredPaths = ignored.ToArray();
}
public static void PostStart()
{
// Intercept ViewEngines to profile all partial views and regular views.
// If you prefer to insert your profiling blocks manually you can comment this out
var copy = ViewEngines.Engines.ToList();
ViewEngines.Engines.Clear();
foreach (var item in copy)
{
ViewEngines.Engines.Add(new ProfilingViewEngine(item));
}
}
}
public class MiniProfilerStartupModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += (sender, e) =>
{
var request = ((HttpApplication)sender).Request;
MiniProfiler.Start();
};
//Profiling abadened if user is not in the admin role
context.PostAuthorizeRequest += (sender, e) =>
{
if (!context.User.IsInRole("Admin"))
MiniProfiler.Stop(discardResults: true);
};
context.EndRequest += (sender, e) =>
{
MiniProfiler.Stop();
};
}
public void Dispose() { }
}
Is there some configuration that is required or potential issues I should be aware of?
In the end i figured out that the Miniprofiler JavaScript block that is added to the page must be below the pages jquery reference.
My JavaScript reference are all added at the end of the page for best practice performance reasons.
But I'd left the #MvcMiniProfiler.MiniProfiler.RenderIncludes() in the page header. Moving it below the jquery script ref at the bottom of the page fixed my problem.
Just found out that the same situation repeat if
$.ajaxSetup({
global: false
});

SharePoint Publishing site HTTPModule

I have written an HTTPModule for the redirection purpose and installed in GAC and referenced in root web.config file. It is working for Team sites very well.
I am using PreRequestHandlerExecute to see the request is page or not and calling
public void Init(HttpApplication context)
{
this.app = context;
this.app.PreRequestHandlerExecute += new EventHandler(Application_PreRequestHandlerExecute);
}
void Application_PreRequestHandlerExecute(object source, EventArgs e)
{
Page page = HttpContext.Current.CurrentHandler as Page;
if (page != null)
{
page.PreInit += new EventHandler(Perform_Redirection);
}
}
and in the Perform_Redirection method I am doing the redirection stuff.
void Perform_Redirection(object source, EventArgs e)
{
//logic goes here for redirection
}
The above code working fine for Teamsites but not for Publishing sites. The Page.PreInit is not firing for publishing sites.
Please help me to solve this problem!
I am using PreRequestHandlerExecute, because I need session object and other details otherwise I would have used BeginRequest.
I solved it by moving the redirection code into the PreRequestHandlerExecute event handler

Resources