How to see the redirect status from an STS / IdP - asp.net-mvc-3

I have searched (probed ,even) for an answer to this but haven't come up with anything useful so far. I'm pretty new to ADFS, STS's in general and WIF so please excuse any obvious ignorance or inappropriate use of terminology. ;)
I'm currently integrating a custom MVC3 app with an external IdP, via ADFS. The ADFS to IdP setup is all done and working.
Some parts of the site are accessible to anon users - in the web.config authentication mode has been set to none. The other parts are protected by having their controllers/action methods decorated by a custom System.Web.Mvc.AuthorizeAttribute.
All the usual modifications to the web.config for using the WsFederationAuthenticationModule have been made and it works 95%; the user can browse to the anon accessible parts of the site. When they try and hit the protected parts, the authorize attribute checks if they have some custom information from our IdP in the IClaimsPrincipals associated with the HttpContext.Current.User and then sets the ActionResult to 401 if not; The WsFederationAuthenticationModule kicks in and redirects them to the IdP's login page. When they enter their details, they're then successfully redirected with some FedAuth cookies and the authorization then passes.
The problem starts when they get to the IdP's login page. This particular IdP has a link to return you directly to our site (to the same page the original request was made to), with this SAML response embedded somewhere (this is according to their documentation)
urn:oasis:names: tc:SAML:2.0:status: AuthnFailed
At this point, they are now "Unauthorized" and all the user will see (at least in dev) is a 401 page. You have to kill the session or otherwise get rid of that cookie to start again.
What I need to do is intercept that redirect request from the IdP, and essentially check for that particular SAML status, because the user should then be redirected to one of the unauthorized areas as if nothing has happened. I've tried something like this in the global.asax:
protected void Application_Start()
{
// mvc stuff here....
// add handler to intercept handling creation of security tokens by WsFederationAuthnticationModule
FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
}
void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
FederatedAuthentication
.WSFederationAuthenticationModule
.SessionSecurityTokenCreated += WSFederationAuthenticationModule_SecuityTokenCreated;
}
public void WSFederationAuthenticationModule_SecuityTokenCreated (Object sender, SessionSecurityTokenCreatedEventArgs args)
{
var token = args.SessionToken;
// do something with the session token here e.g. check for SAML status
}
.. but I cant see anything useful on that token; nothing to indicate a specific response status. The fact that there is a FedAuth cookie at all but no custom info from the Idp is a dead give away that the user has been there but somehow failed to authenticate, but in principle I want to be able to see that status. I might have to deal with timeouts at the IdP as well....
Maybe I'm doing this all wrong, or just plain old don't understand, but can somehow fill me in on how to determine those response statuses?
Phew. Thank you! :D

Ok, so I'm going to answer my own question.
The answer to whether I can get that custom status from my IdP is a no, at the moment. :(
But this is only because ADFS is not setup to capture it and pass it on. Apparently you need to do some custom coding for capturing information from the back channel that is opened between ADFS and the IdP.... well beyond the current scope of work.
As a work around for the moment:
If a request is made to the site and there is NO SAML token, its a new request by a user who has made no auth attempt at the Idp
If there is a SAML token but no ID from the IdP in the token (which is only present when they auth properly), then the user failed Auth for some reason
If there is a SAML token with the ID present, the user auth'd properly
Not great but acceptable. BTW, all credit goes to YMC in this SO post for the following code which lets you check for SAML tokens:
void WSFederationAuthenticationModule_SecurityTokenReceived(object sender, SecurityTokenReceivedEventArgs e)
{
var message = SignInResponseMessage.CreateFromFormPost(Request) as SignInResponseMessage;
var rstr = new WSFederationSerializer()
.CreateResponse(message,
new WSTrustSerializationContext(
SecurityTokenHandlerCollectionManager.CreateDefaultSecurityTokenHandlerCollectionManager()));
}
Pce!

Related

How to access JWT in Quarkus when proactive auth is disabled?

I need to access a jwt sent in authorization header. I also have to disable the proactive auth check. How can I get the token?
I have this class which used to work when proactive auth was enabled:
#RequestScoped
public class AccessRights {
#Inject
JsonWebToken accessToken;
public boolean validateAccess() {
if(accessToken.getRawToken() == null)
throw new AccessDeniedException("Invalid Access Token");
JsonObject claim = accessToken.getClaim("claim");
// ... do some validation ...
}
}
When I set quarkus.http.auth.proactive=false it stops working. The accessToken is always null. Now that is expected since its documented here https://quarkus.io/guides/security-jwt#proactive-authentication and here https://quarkus.io/guides/security-built-in-authentication#proactive-authentication. I tried to use the alternative way suggested in the docs, but I cannot get it to run.
Problem: I don't know how to get a JsonWebToken from the suggested workaround
Problem: Even when I try to use securityIdentity instead, it is always null. Plus I don't know yet how to actually use it for validation.
#RequestScoped
public class AccessRights {
#Inject
CurrentIdentityAssociation currentIdentityAssociation;
public boolean validateAccess() {
SecurityIdentity identity = identities.getDeferredIdentity().await().indefinitely();
// identity is always null
}
}
EDIT:
To add more context: I have a graphQL API and the initial problem was, that if I receive an expired token, the framework will respond with a 401 unauthorized error. However as is best practice in graphQL, I need to respond with a 200 with json body that describes the error. The proactive auth check prevents the graphql error handler to kick in. That is why I disabled it, effectively making things worse.
Here is how the whole auth process in my application works:
The API receives a request that should contain a bearer token (JWT) in the authorization header. The token is acquired somewhere else. My application needs to validate the issuer and expiration time. This part used to be handled by the pro-active auth check. After that my custom validateAccess() method will extract some roles from the token. If those roles are not present, I will call an external api, providing the raw token, to get back some more detailed access rights.
I believe you missed something important here.
Docs says that :
By default, Quarkus does what we call proactive authentication. This means that if an incoming request has a credential then that request will always be authenticated (even if the target page does not require authentication).
Which implicitly means : if you disable proactive auth, you need to require auth to be done before accessing resource.
That may be part of your problem, as you disabled proactive auth, you'll have to explicitly restrict access to resources to authenticated clients. Otherwise, no auth is performed and thus SecurityIdentity is null.
If you already tried that, please add more code and context. I'll gladly edit my response.
EDIT 1 :
I see 2 distinct problems in informations you added :
You got to validate token (if proactive auth is disabled)
You got to get response code 200 with error details instead of 401
But as you clearly stated, token validation works out of the box with proactive enabled.
So, I would suggest you to let proactive auth do its job. Then, I would add an ExceptionMapper.
This way, you can write custom code for Exception unfolding, and you can respond what you want for every situation.
See ExceptionMappers documentation for more

Spring Social losing auth token on redirect

I'm trying to use Spring Social Facebook login along side form login, more or less following the guide here: http://www.baeldung.com/get-user-in-spring-security, only using header-based session management rather than cookies. Right now the login is successful. Facebook sends a 302 to my server at /api/signin/facebook, and my server sends a 302 to the post-sign-in url I've set on my ProviderSignInController along with the x-auth-token header. The issue is that when following the last redirect my browser throws away the auth token.
I think I want to just add the auth token as a query param on the final redirect uri, but I don't know how to intercept the final response. I've called setSignInInterceptors on my ProviderSignInController but that seems to be ignored after the first sign in. How can I keep my session information when it's not a cookie?
Just added the token as a query parameter and returned it from my custom SignInAdapter.signIn method. I feel like there's probably a better solution but I needed something.

How can I handle the most basic user auth in Spring Boot?

I've implemented really basic user authentication before - generate a session cookie when logging in, and when the user loads a page with authentication just check the cookie.
However, the complexity of Spring Security / Apache Shiro is really confusing me.
All I really want is:
be able to have a user log in once and see their username on every page they visit (like on Stackoverflow)
have this login persist for a reasonable length (cookie expiry time or something like that)
It looks like I have the option of using EhCache or implementing my own subclass of... something... to use something like postgres.
I seem to have gotten the first thing I want working in Apache Shiro:
Subject sub = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(remember);
try {
sub.login(token);
// ...
But I'm super stuck on how to get this session to persist between restarts of the spring webserver. Like, I know Stackoverflow highly recommends a code example but I literally don't even know where to start. Right now my "progress" on trying to figure out how to persist sessions between restarts (bolded to clarify what I'm asking) is literally the single line
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
// ...
dwsm.setCacheManager(new EhCacheManager());
and I don't even know what that actually does.
I would really appreciate any kind of guidance here since I really don't even know where to begin and the documentation I've been able to find is unfortunately not super thorough.
thats one of the problems with just sessions. That they are not persistant over restarts unless you save your sessions.
Now days people usually use a token based approach like oauth2.
The flow in a token based authentication system is:
User sends his/hers credentials to an authorizationserver (oauth2).
If credentials were correct they get a token back
The client (webpage) makes a request to the backend to get the user object and supplies the token in this call as an "Authorization"-header.
The backend takes the token and sends it to the authorizationserver (oauth2) to check its validity.
if the token is valid the backend server fetches the user object and sends this to the client.
The client gets the username from the user object and stores this in its state (react, redux if such an app). In every call to the backend the client must supply the token it got from the first login so that the server always knows that the caller is whom he/she claims to be

Asp Web API. JWT Authentication vs username / password Authentication

Sorry for such novice question.
I am fairly new to web security.
Can someone please explain to me, why do we need JWT token authentication for web api (REST) when I could include { username | email } / password for every single API request?
Mostly, it's a separation of concerns thing. JWTs are a way to authorize a request, whereas username/password is a way to authenticate. The key difference is that authentication is something you should ideally only have to do once, and it should be done by a dedicated endpoint responsible for that. For every other request, you're simply confirming the authorization you received from that initial authentication.
If you were to send username and password with every request, every endpoint then would have to handle authentication logic, which would be a nightmare. Using a JWT, the endpoint can simply verify that it's valid and move on to what it's actually responsible for.
JWTs are just one method of authorization. In a traditional website-style application, this would be handled by a cookie. This then enables the user to login once, and then proceed to browse protected areas of the site without having to login again. The equivalent of what you're suggesting would be essentially like forcing the user to login again everytime they clicked a link, just to view that next page.

SAMLException: InResponseToField of the Response doesn't correspond to sent message

We are working on an application, which is protected with spring security saml.
Authentication works fine, but there is one problem with the following workflow in production environment.
user requests the unprotected address www.server.com
response is a html page with an inline script that changes window.location.href to the saml protected page (service provider) www.server.com/app/action?param1=value1&param2=value2
spring saml detects that authentication is needed and redirects the user to the login form (identity provider) on www.login-server.com
at this point the login form is the first page that is displayed to the user
user adds this login page as bookmark (including saml related url params for this http session) www.login-server.com/adfs/ls/?SAMLRequest=xxx&SigAlg=xxx&Signature=arGdsZwJtHzTDjQP1oYqbjNO
user works with the application...
at the next day the user opens this bookmark and login
IdP redirects to the SP but the belonging http session has already expired
Now we get the following exception in our application:
org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message arGdsZwJtHzTDjQP1oYqbjNO
Any ideas how to handle this workflow so the user can use the application after successful login?
Thanks for your answers!
We have solved our issue with following changes to the spring saml configuration:
In bean with id successRedirectHandler (org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler) we set the defaultTargetUrl to the init-Action of our application (including all request parameters). This url will be automatically used in case of IdP initiated SSO.
In Bean with id contextProvider (org.springframework.security.saml.context.SAMLContextProviderLB) we set storageFactory to org.springframework.security.saml.storage.EmptyStorageFactory. This disables the check of the InResponseToField.
When you applicate generated an AuthnRequest, the request has an ID which your application somehow keeps. The corresponding response from IdP must have InResponseTo attribute set to that same ID value so that your application can verify that the response is meant to be for the request it sent.
However, when your user bookmarked the adfs link that contains request (www.login-server.com/adfs/ls/?SAMLRequest=xxx...), your application had totally forgotten about that request. In other word, it no longer kept the request ID somewhere and couldn't verify response.
The solution is to tell your users not to bookmark the www.login-server.com/adfs/ls/?SAMLRequest=xxx... link. Instead, they must bookmark a link in your application where it can generate a new request and send to ADFS.

Resources