How to execute custom code after successful authorization? - identityserver4

Client app is redirecting to IdentityServer4 instance - "/connect/authorize/callback"?client_id=...".
If the user is not logged in(has no cookie) he is redirected to the Login page and on postback I can execute random logic(subscribe the user to this client - add claim to the user's claims collection with the client_id).
If the user has valid cookie there is no login screen and IdentityServer4 redirects him back to the Client app with the requested tokens in the query string (the consent is turned off).
How can I execute custom code when this event occurs?

A "login success" event is available from the Built-In Events. Unfortunately, Identity Server events are logging-oriented, so you must implement IEventSink to capture events, and you probably can't do things like show UI without interrupting the rest of the OIDC flow. This also means you can't log events unless you manually handle it in your IEventSink implementation (see the DefaultEventSink, it just serializes the event to whatever logger is registered for injection). The event is raised by the TokenRequestValidator.
First, enable "success" events in the configuration...
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
});
...then look for the UserLoginSuccessEvent.
public class LoginEventSink : IEventSink
{
public Task PersistAsync(Event evt)
{
if(evt.Id.Equals(EventIds.UserLoginSuccess))
{
// do stuff
}
}
}
One of the overloads tells you whether the login was interactive by setting Event.Endpoint to UI but I don't see a way to get that from IEventSink, not really sure how it's used. You might be able to check the OIDC "Prompt" mode for None if you need that.
Depending on your setup and requirements, you could also capture an event on the client side when the challenge returns, but it sounds like you want to keep this in Identity Server.

Related

Identity Server 4: Redirect or Show Error Message in OnTokenValidated

In my web application, after user authenticates w/ interactive login page, I would like to do some additional validation in OnTokenValidated after the user is authenticated, and if it fails then i want to show a nice page with an appropriate message.
How can I do this? I tried:
private Task HandleOpenIdConnectOnTokenValidated(TokenValidatedContext context)
{
if (some condition fails...)
{
context.HttpContext.Response.Redirect("/home/notregistered");
return Task.FromResult(-1);
}
}
return Task.CompletedTask;
}
Other option is to throw an exception, but it shows an unpleasant error. If this is best/only option, how can I specify a custom error page and show something custom based on the exception thrown?
thanks
I would implement the additional authorization requirements using the built in authorization mechanisms.
Perhaps using IAuthorizationRequirement, as described here. It allows you to create very flexible authorization rules.

Should I be testing for an AdalException inside an ActionFilter?

In some Azure Samples on GitHub like this one we have an example of using ADAL to access a protected Web API resource. The effort is protected by a try/catch looking for an AdalException
I'll summarize the code as thus:
try
{
//pseudo code... configure a client to use an access token..
var token = ADAL.AcquireTokenAsync...
var httpClient = new HttpClient(baseUri, token);
// use the token for querying some protected API
var result = //await HttpClient to return data...
return View(result);
}
catch (AdalException)
{
// do something important with the exception,
// e.g. return an error View w/login link
}
So, as I start to flesh out my MVC controllers to use ADAL-access_token'd requests, do I really want all this try/catch business in every controller?
Does it make sense to create an ActionFilter? This snippet was inspired by code I saw at this Azure Sample
public class AdalErrorAttribute : FilterAttribute, IExceptionFilter
{
void IExceptionFilter.OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is AdalException)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
filterContext.RequestContext.HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
}
My Context:
I'm taking a rather homogenous set of scaffolded MVC controllers that were EntityFramework-centric at the time they were generated.. but now need to be re-tooled to access my Web API (via my new AutoRest client)
#vibronet made a good point - don't do that for your WebAPI endpoints. They're called with bearer auth, and shouldn't be automatically redirected to a sign-in process. Give back a 401 indicating the credentials are invalid and let it go.
But for an MVC app the user is using interactively, this is a reasonable idea to follow, subject to a couple of constraints.
Make sure your filter is very tight about what it matches. Ie. make sure it's only handling exceptions you can reasonably-certainly draw to an authentication issue (vs. being an access-denied issue). Perhaps a custom exception that gets thrown by your wrapping logic, or some extra conditions beyond it being an ADALException to make sure it's an issue solveable by logging in again.
Unless you really want every request have this handling, look at attaching it at the controller or action layer instead of globally.
Look out for potential "loop" issues, where you get an error, tell the user to log in again, get the error, make them log in again, etc. Maybe set some data in the session when triggering the login, or in the login process, or something like that. Better to give an error message than get the user's browser stuck in an infinite-redirect loop. (of course, this applies to the manually-handle-the-exception case too)
Hope that helps.
There are two issues with the approach.
the sample code you mention and the filter implementation are for web APPs, not web API. That's an important difference. Web APPs can rely on the browser to interact with the user for authentication; web APIs don't know what UX the client has (or even if there's any UX at all) hence the error behavior is radically different
a controller might call multiple APIs, requiring multiple token requests for different resources- which can fail or succeed independently, and connect to different providers. Different controllers might call different resources. The reaction to the error might vary. A filter such as the one you prototyped would only help with a very narrow set of cases.
Hence I would say that unless your controllers are all expected to perform very homogeneous logic, a filter such as the above would not give you the flexibility you need.

Session Override implementation

I am using Spring Boot, AngularJS and REST for my web application. I am also using JWT/oAuth token for user authentication and authorization, implying that the session is stateless.
We have a requirement wherein a duplicate session should not be allowed for a user.
I have implemented a way by which if the user logs in from any other browser of the same machine or any other machine, then a session override confirmation message will be shown.
If the session is overridden, then the first session will terminate whenever user tries to perform any activity.
For this the last token generation time for that user is maintained in the server memory.
If the incoming token generation time matches the time in memory, then access is granted, else session expired error is thrown.
When a user performs a successful logout, then the entry for that user is flushed out from the memory and the new authentication request doesn't throw the session override error.
Now what is happening is that whenever a user is not doing a successful logout and is simply closing the browser window, then the server value is not cleared.
And when user tries to log in again, then session override message is thrown, even though, (from user's perspective), there is no duplicate session.
My question is: Is there any way by which we can avoid showing the session override message when the above scenario happens but at the same time show override message if the user is really trying a duplicate session?
Are there any applications/designs available to handle such cases?
Any suggestion/idea is welcome.
I have similar case when i need to detect if user is logged out manually or by session timeout (usually they forgot to logout by just closing the browser).
What i do is utilizing the spring ApplicationListener
public class SessionAuditListener implements ApplicationListener<ApplicationEvent> {
#Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
if (applicationEvent instanceof HttpSessionDestroyedEvent) {
// do whatever it needs here
HttpSession httpSession = ((HttpSessionDestroyedEvent) applicationEvent).getSession();
}
}
}

How should i recognize if user is logged in or not in angular/node application?

A scenario:
A user tries to login into the application, the credentials than passed to node to check if he exists, if so, some auth credential is placed to true, and the account of the user is than returned to the site, and with angular i have a service that does the following:
angular.module(myapp).service('accountService',function($http){
var account= null;
return {
authUser = function(credentials){
....
};
setaccount = function(accountObj){
account = accountObj;
...
}
}
Now it's ok if no refresh is going on, but when a refresh does happen, what is the best way to determine if a user is still logged in or not?
For example: i took a look into mean stack to see their approch, and what they do is like make the userObject exposed so i can do window.loggedinUser (That comes from a session storage of some kind?)
There are other examples i assume that deals with the run method doing something, but my question is this:
every time a user clicks refresh should the program do something in the run to determine if the user is still logged in or not (meaning dealing with server request in every refresh)?
What is the better solution to do so, a simple example will be appreciated....
For most AngularJS applications the user credentials are fetched from the server via an asynchronous HTTP request. This creates a timing problem of when AngularJS starts up and when the application knows if there is a user session.
You'll have to set the user flag to false at startup, and when the asynchronous call finishes you'll have to change it's state (if the user is logged in).
This becomes a problem if the current route is restricted to signed in users only.
The best recommended approach is to use route promises. Your accountService should provide a method that will return a promise that fetches the current user's session. If it already has it, then great use that one, otherwise it has to perform a HTTP request to get. Either way, the result should always be a promise.
This is how you should make the user session availible in your application. Via a promise on the route.

FOSUserBundle + verify user related properties after valid login

I have implemented the FOSUserbundle on my application, I have manage to get chained userproviders to work as well as overriding some of the default controllers.
I have one issue though, I need to find a way to intercept the login process.
My userobject is linked to a client entity, Now I need to deny access to the Userobject even though it's valid because the client entity is disabled.
What I have attempted.
1)I tried extending the SecurityController to no avail, it seems to redirect before executing my code.
2) I tried to set _target_path on my login form to a controller that does the check of the client entity status, This seems to work but after I logout the user using,
$this->get('request')->getSession()->invalidate() ;
The user is redirected back to the login page but I cannot get a flash message to appear on twig template stating that the client is not active.
Any help or ideas will be much appreciated.
You might want to attach a handler to the login and logout process: http://www.reecefowell.com/2011/10/26/redirecting-on-loginlogout-in-symfony2-using-loginhandlers/

Resources