Session Authentication not working in Play when using Silhouette - playframework-2.6

I am using Silhouette security library. My Play server seem to send empty Session information in response. What am I doing wrong?
Following is the print on Play's console just before sending response.
Session(Map(authenticator -> 1-jtwBvA+LsLKE2rnkT/nMH1aQF9xc1twhECrma9mj3NUhUdVDmh/4wxQ2MxDOjcxkvEMTi1k63Dg5ezl+9FzDE3miaM5DbOrhyqAyGu4+30mHHV3QdPKA3IQQx5UdL1Hu85fZRI4f3Ef+q6xAgboDps0uBob5ojzo5Oqy8FNsoexn7Wr9iRyTr5xrMrLvl9GNQa+rA3q8qvW84sJaSei2iydrP2OjUbnnzo+zgrHLB3Bn7KJxOcFH4h9CikZNk/FHbtDm4uxzcK3paK1CuuIWLE8yvcYdavJ+4ejV5IaJ8QesJQRFgBktD9L/A2bc03eaA8wm)))
But in the the browser window, I notice that the value is empty.
Set-Cookie: PLAY_SESSION=; Max-Age=-86400;
Note that my browser earlier already had a PLAY_SESSION cookie from previous test runs. However, I would expect that the client application (Angular) would override old cookies with new cookies. Am I correct?
Following is the code snippet which creates, initialised and embed session information
val AuthenticatorFuture: Future[SessionAuthenticator] = silhouette.env.authenticatorService.create(loginInfo) //create authenticator
AuthenticatorFuture.flatMap(authenticator => { //got the authenticator
val securityTokenFuture: Future[Session] = silhouette.env.authenticatorService.init(authenticator) //init authenticator
securityTokenFuture.flatMap(securityToken=> {
println("adding security token: ",securityToken)
val result:Future[AuthenticatorResult] = silhouette.env.authenticatorService.embed(securityToken, Ok(Json.toJson(JsonResultSuccess("found user"))))
result
The Environment is defined as
trait SessionEnv extends Env {
type I = User
type A = SessionAuthenticator
}
As is passed to my controller as
silhouette: Silhouette[SessionEnv]
I created is at compile time as follows
val configSession = SessionAuthenticatorSettings()
val sessionAuthenticatorService = new SessionAuthenticatorService(configSession,fingerprintGenerator,authenticatorEncoder,new DefaultSessionCookieBaker(),clock)
val sessionEnv = com.mohiva.play.silhouette.api.Environment[SessionEnv](userIdentityService,sessionAuthenticatorService,Seq(),EventBus())

The issue is probably expected behavior of Play Framework as Silhouette doesn't modify the session cookie. I noticed that the browser already had a previous expired cookie and it sends it in the signin request. When Silhouette authenticator sees the expired cookie, it sends an empty value back. I think this is to make the browser discard the previous cookie.

Related

Passing extra query/form parameters through spring social

I'm building a Single Page Application using Spring Social and Spring Security generated by JHipster.
I'm trying to capture the original query parameters after a user has been authenticated by some social authentication provider.
Example:
calling /signin/someprovider?show=someEntityId and after a successful authentication redirects the user to /signup/ , I need a way to fetch 'someEntityID'.
I assume different http calls make it difficult to pass/store the parameters around.
Is there some Spring built-in functionality I can use/reuse or how does one solve this problem?
UPDATE
The thread of requests looks like this:
(1) browser-> http://localhost:9060/signin/authenticationProvider?show=**someEntityId**
<- redirect to https://authenticationProvider... &state=SomeState
(2) browser -> https://authenticationProvider
<- redirect to http://localhost:9060/signin/google?state=SomeState&code=SomeCode
(3) browser-> http://localhost:9060/signin/authenticationProvider?state=SomeState&code=SomeCode
<- redirect to http://localhost:9060/social/signup
(4) browser -> http://localhost:9060/social/signup
This ends up in
#GetMapping("/signup")
public RedirectView signUp(WebRequest webRequest, #CookieValue(name = "NG_TRANSLATE_LANG_KEY", required = false, defaultValue = Constants.DEFAULT_LANGUAGE) String langKey) {
try {
Connection<?> connection = providerSignInUtils.getConnectionFromSession(webRequest);
socialService.createSocialUser(connection, langKey.replace("\"", ""));
At this point it want to call a function with the original parameter someEntityId.
According to google oauth2 redirect_uri with several parameters the ?show=someEntityId parameter should be encoded in the state parameter of the Oauth2 request in order to survive
from (1) to (3). In (3) the state parameter has to be added to the redirect uri, such that the original parameter can be decoded in (4).
It looks like a lot of work, or am I missing something? It would be nice if there would be a way to have a session variable in which I could store the parameters at (1) and fetch them again when in (4).
Since version 1.1.3 Spring Social creates the state parameter on its own and uses it as a CSRF token, see https://pivotal.io/security/cve-2015-5258 - therefore you can (and should not) encode additional parameters in the state parameter.
Instead if the provider sign is enabled with a ProviderSignInController, a ProviderSignInInterceptor can be used to store such parameters intermediately in the session (in preSignIn(...) and postSignIn(...)).
I guess there is a similar approach if a SocialAuthenticationFilter is used.

AuthFlow.CreateCredentialsFromVerifierCode throws error only on load balancer

I am using Tweetinvi for posting images to Twitter.
From our App servers its working fine to post to Twitter.
But, When tried from our load balancer getting this error -
Error:The credentials are required as the URL does not contain the
credentials identifier.
Stack Trace: at Tweetinvi.AuthFlow.CreateCredentialsFromVerifierCode(String
verifierCode, String authorizationId, IAuthenticationContext
authContext)
My code snippet is like this -
var verifierCode = Request.Params.Get("oauth_verifier");
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
I see these parameters(oauth_verifier, authorization_id,..) being passed to the callback page. But still seeing the above error in the call back page.
Note: this issue is only when I try posting to Twitter on our loadbalancer (using the individual servers working fine).
Should I use a different overloaded function?
So the problem comes from the fact that you are actually using a load balancer. But let me explain how the authentication works and how you can solve your problem.
var appCredentials = new ConsumerCredentials("", "");
var authContext = AuthFlow.InitAuthentication(appCredentials, "");
When you call AuthFlow.InitAuthentication, it returns an IAuthenticationContext. This context contains all the information required to process the callback from Twitter.
But in addition to this, Tweetinvi adds a parameter authorization_id to the callback so that it can map the callback request to an actual IAuthenticationContext.
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
When you call AuthFlow.CreateCredentialsFromVerifierCode with an authorization_id as a parameter it will look into the local dictionary and try to get the IAuthenticationContext.
Because you are using a load balancer, the server executing the AuthFlow.InitAuthentication can be different from the server your receiving the callback request.
Because your callback arrives at a different server, it actually result in the AuthenticationContext being null.
This is what I tried to explain in the documentation.
How to solve this?
What you need to do is to store the IAuthenticationContext information required for the CreateCredentialsFromVerifierCode to continue its work when it receives the callback. I would suggest you store this in your database.
When you receive your callback you will have to get back these information from your db. To do that I would suggest that when you initally call the `` you add to the callback url a parameter with the value storing the authentication id in your database (e.g. my_auth_db_id=42).
var authContext = AuthFlow.InitAuthentication(appCredentials, "http://mywebsite.com?my_auth_db_id=42");
When your callback arrives you will be able to do :
var myDBAuthId = Request.Params.Get("my_auth_db_id");
With this value you can now create a new token with the required information (stored in the db).
var token = new AuthenticationToken()
{
AuthorizationKey = "<from_db>",
AuthorizationSecret = "<from_db>",
ConsumerCredentials = creds
};
Now you are ready to complete the operation:
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, token );
I realize this is a big post, but I wanted to explain how it works.
Please let me know if anything does not makes sense.

Google Oauth - TokenVerifier How to USE?

I'm trying to use Google OAuth with Sign in & Sign Up for my Web Server Application.
This is the page : https://developers.google.com/identity/sign-in/web/backend-auth that I have referenced, but I am stuck in using the Google Client API, the TokenVerifier that is mentioned below in the document. I tried to find some examples, but I couldn't find one, as I am not sure how to handle the parameters in the methods that the sample shows.
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
...
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Arrays.asList(CLIENT_ID))
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
if (payload.getHostedDomain().equals(APPS_DOMAIN_NAME)
// If multiple clients access the backend server:
&& Arrays.asList(ANDROID_CLIENT_ID, IOS_CLIENT_ID).contains(payload.getAuthorizedParty())) {
System.out.println("User ID: " + payload.getSubject());
} else {
System.out.println("Invalid ID token.");
}
} else {
System.out.println("Invalid ID token.");
}
For example, I know what these CLIENT_ID, ANDROID_CLIENT_ID, IOS_CLIENT_ID parameters mean in the sample code(in the reference page), but the server only receives id_token, which is basically a String Text. (That was made by the google api in the client-side, the javascript)
So, I do not have these parameter values passed to the server from the client. I know that google shows another way: the tokeninfo endpoint, but they mentioned that it is for only 100user/month only. (If I translated it correctly) However, for the tokeninfo endpoint way, they return the JSON file of containing client ids, which I think that would be the values for the parameters that I mentioned before, but I do not want to use the token info endpoint method.
So, my question is, how do I get the right parameter values for the sample code that is showed in the google document page? I only receive id_token value from the client.
ANDROID_CLIENT_ID or IOS_CLIENT_ID should be hard coded (in a config file) in your server's code.
Essentially your server is getting an id_token in a request and you need to make sure if it is meant for your app or server by checking the audience in there and making sure it matches one of the values you are expecting.

Why is the Etrade API returning a missing parameters error?

I have successively obtained a request token, and am now using it in conjunction with my consumer key to create the following request
https://us.etrade.com/e/etws/authorize?key=2fc*******c323d6&token=IIrs6BsIrGQ********duC60GAmLq8
where the asterisks have been substituted for my consumer key and request token. I give this as an argument to getAuthorizeURL This returns an ETWSException and output in the terminal reading
ERROR OAuthClientImpl - Mandatory parameters missing
I have the two required arguments for the getAuthorizeURL method, and I am sure they are formatted correctly. Can anyone tell me what is going wrong here?
Also, if it helps to know, calling the getAuthorizeURL causes my default browser to open and brings me to the address that I entered above, but it returns a 404 error.
If you're using the sample code from the Docs.. they are missing 1 piece.
(java)
client = OAuthClientImpl.getInstance(); // Instantiate IOAUthClient
request = new ClientRequest(); // Instantiate ClientRequest
request.setEnv(Environment.SANDBOX); // Use sandbox environment
request.setConsumerKey(oauth_consumer_key); //Set consumer key
request.setConsumerSecret(oauth_consumer_secret); // Set consumer secret
token= client.getRequestToken(request); // Get request-token object
oauth_request_token = token.getToken(); // Get token string
oauth_request_token_secret = token.getSecret(); // Get token secret
request.setToken(oauth_request_token);
request.setTokenSecret(oauth_request_token_secret);
String authorizeURL = null;
authorizeURL = client.getAuthorizeUrl(request);
URI uri = new URI(authorizeURL);
Desktop desktop = Desktop.getDesktop();
desktop.browse(uri);
The Documentation sample forgot to mention, you'll need to set the Token Key/Secret on the Request object, before you make the call the get AuthorizeUri.
request.setToken(oauth_request_token);
request.setTokenSecret(oauth_request_token_secret);

Message: ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context

We're getting the exact same error as in this thread ... in our production environment.
[WIF Security Token Caching
Does anybody have a fix to this error ?
Message: ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.
Here is some info about our setup:
• We‘re using built-in Windows Identity Framework with .NET Framework 4.5.1
• The problem is almost always associated with changing from RelyingParty#X over to RelyingParty#Y ( e.g. the moment user clicks the RP#Y he‘s SIGNED OUT without asking for it ) – when he logs in again after this event, he‘s taken right to the page he was asking for, inside RP#Y
• We‘re using e.SessionToken.IsReferenceMode = true; // Cache on server, to get a smaller cookie
• By using IsReferenceMode = true, our FedAuth cookie stores a „pointer“ to the actual Token which is stored inside our database
• We‘re using our own DatabaseSecurityTokenCache which is overriding the functions in SessionSecurityTokenCache. By using the the DatabaseSecurityTokenCache alongside the IsSessionMode = true, we‘re server-farm-friendly ( but we‘re also guaranteed to be within the same server through all our login-session ) so if the application pool for some reason dies, we‘re able to get the token from database through the DatabaseSecurityTokenCache. I‘ve verified this by completely killing IIS in the middle of a session ( with „net stop WAS“ and the restart it again with „net start W3SVC“ and we‘re still able to get the Token from the DatabaseSecurityTokenCache ). I‘ve also tried doing the same by simply using the out-of-the-box SessionSecurityTokenCache and that will fail respectivly ( as expected )
• Default token lifetime is 20 minutes ( but the user can change it to 40 or 60 minutes if he wants to ) – that will only be effective the next time the user logs in ( and 90% of our user are just using the default 20 minutes lifetime )
• We‘re using a certificate (same on all servers) to encrypt the FedAuth cookie, NOT a machine-key ( which would be catastrophic if using server-farm, with different machine-keys )
• so all the servers can decrypt cookies, which were encrypted from another server.
• We have a javascript with a countdown in our RelyingParty4 and RelyingParty5 ( two different relying parties ) which is used as a „timeout script“ in case the user leaves his computer unattended ... he will be signed out when the token is about to expire – (minus) 30 seconds ( e.g. 20 minutes – 30 sec = 19,5 minutes ) with idle time. This is protect our very sensitive banking information, so when the user comes back to his machine he will need to login again. e.g. We‘re also using sliding sessions ([http://www.cloudidentity.com/blog/2013/05/08/sliding-sessions-for-wif-4-5/]) and when we slide, the timing in the javascript of the client is also updated as well, to match the length of the token minus 30 seconds. These 30 seconds are used to make sure that the session is still alive when signing out, so it‘s a little bit shorter than the lifetime of the token/session. We currently sliding if this condition is met: total lifetime / 2 .... e.g. 20 / 2
• We‘re only sliding if there‘s any activity going on with the user ( i.e. he‘s moving around, doing some work ). We're sliding in minute10+ (if token lifetime is 20minuts) as the example above shows
• We‘ve debugged the problem multiple times and this is the WIF error we‘re getting: Exception: System.IdentityModel.Tokens.SecurityTokenException Message: ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context. Source: Microsoft.IdentityModel at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver) at Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken) at Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
• We‘ve been able to re-produce the bug by using an old FedAuth cookie and this plugin: ( attention! We‘re not sure if this is the same thing that‘s happening on PROD, but at least it gives the same error in our Logging system ) This is good, but I think you should add the steps on how we‘re able to modify the content of the FedAuth cookie, to bring this problem to life, locally.- You can use this:
It‘s simple by taking the Value of the FedAuth cookie from some previous sessions ( on the same machine! not from another machine, that won‘t work ) And pasting it into the Value of the FedAuth cookie and refreshing the page.-
Plugin used to modify the cookie, in Chrome is called „Edit This Cookie“:
- If we change the content of this cookie to a value from a previous session, and hit the refresh ( CTRL + R in Chrome ) we get the infamous TokenSecurityException ID4243 and the RP calls for a immidiate FederatedSignout because we're unable to recover from this situation.
Signing out....
I should also probably mention that we took's Microsoft MSDN's article marked "Important" on IsReferenceMode seriously and added it also to our
SessionAuthenticationModule_SessionSecurityTokenCreated event:
e.SessionToken.IsReferenceMode = true;
taken from MSDN:
Important!
To operate in reference mode, Microsoft recommends providing a handler for the WSFederationAuthenticationModule.SessionSecurityTokenCreated event in the global.asax.cs file and setting the SessionSecurityToken.IsReferenceMode property on the token passed in the SessionSecurityTokenCreatedEventArgs.SessionToken property. This will ensure that the session token operates in reference mode for every request and is favored over merely setting the SessionAuthenticationModule.IsReferenceMode property on the Session Authentication Module.
Below is our whole SessionAuthenticationModule_SessionSecurityTokenReceived,
please examine the comments I put into it ... it explains what everything does:
void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
if (e.SessionToken.ClaimsPrincipal != null)
{
DateTime now = DateTime.UtcNow;
DateTime validTo = e.SessionToken.ValidTo;
DateTime validFrom = e.SessionToken.ValidFrom;
TimeSpan lifespan = new TimeSpan(validTo.Ticks - validFrom.Ticks);
double keyEffectiveLifespan = new TimeSpan(e.SessionToken.KeyExpirationTime.Ticks - e.SessionToken.KeyEffectiveTime.Ticks).TotalMinutes;
double halfSpan = lifespan.TotalMinutes / 2;
if (validFrom.AddMinutes(halfSpan) < now && now < validTo)
{
SessionAuthenticationModule sam = sender as SessionAuthenticationModule;
// This will ensure a re-issue of the token, with an extended lifetime, ie "slide". Id deletes the current token from our databasetoken cache (with overriden Remove of the SessionSecurityTokenCache ) and writes a new one into the cache with the overriden AddOrUpdate of the SessionSecurityTokenCache.
// it will also write the token back into the cookie ( just the pointer to the cookie, because it's stored in database-cache ) because the IsReferenceMode = True is set
e.ReissueCookie = true; // Will force the DatabaseSecurityTokenCache'ið to clean up the cache with this, handler.Configuration.Caches.SessionSecurityTokenCache.Remove(key); internally in WIF's SessioAuthenticationModule
e.SessionToken = sam.CreateSessionSecurityToken(
e.SessionToken.ClaimsPrincipal,
e.SessionToken.Context,
now,
now.AddMinutes(lifespan.TotalMinutes),
false); // Make persistent, þannig að kakan lifir EKKI af browser-close / tab-lokun:
{
e.SessionToken.IsReferenceMode = true; // Cache on server
}
// Not needed, because if ReissueCookie = true; is set, it WILL to a WriteSessionTokenToCookie internally in WIF
//FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(e.SessionToken); // <---- er þetta e.t.v. bara það sem við þurfum ? Nei, á ekki að þurfa, er gert þegar tóki er búinn til með CreateSessionSecurityToken
}
else if (validTo < now)
{
// Fix
// http://blogs.planbsoftware.co.nz/?p=521
var sessionAuthenticationModule = (SessionAuthenticationModule)sender;
sessionAuthenticationModule.DeleteSessionTokenCookie(); // <--- is this really needed like the article says ? http://blogs.planbsoftware.co.nz/?p=521
e.Cancel = true; // This will allow a silent-login if the STS cookie is still valid, e.g. switching between RP's where we're switching from an active RP to a RP which has it's cookie outdated, but the STS's session is still alive. We don't want to prompt the user for a new login, beucase the STS session is still OK!
}
}
this post helped me, so it can help you and others those have this kind of error.
void Application_OnError()
{
var ex = Context.Error;
if (ex is SecurityTokenException){
Context.ClearError();
if (FederatedAuthentication.SessionAuthenticationModule != null){
FederatedAuthentication.SessionAuthenticationModule.SignOut();
}
Response.Redirect("~/");
}
}
From this link.
Hope it was useful!
---------- UPDATE, This is how Lord02 fixed the proplem -----------
The problem was that when users are coming in with stale cookies ( from a previous session, i.e. if they did NOT sign out from our system ... but instead just closed the tab ) and then logged in again,
our cookie which was in SessionMode = true ... tried to go to the DatabaseTokenCache to GET the whole token from database, but as I said our SSIS process deletes all Tokens which are OLDER than 12 hours old (outdated tokens!) so we don't have loads of orphan tokens, which are outdated in our database and are unusuable ... just taking up space in our database.
So after this deletion is done, each night, the DatabaseTokenCache GET‘s function would not return a valid Token ... and the user was signed out because of : ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.
So instead of NOT deleting the Tokens inside our database I created a special handler, which intercepts this error on the RP‘s site ... and redirects the user back to the STS – which will then Create a brand new token and Write that down to the DatabaseTokenCacheStore, like this below
The exception with ID4243 is thrown when the cookie is set as “reference mode” AND the token is not present in the cache –
I can confirm that is by-design and also by-design WIF does not redirect the call to the STS (to start over the authentication process)
To overcome this problem I intercept this exception and react properly.
I redirect to the issuer if this error comes up inside a customSessionAuthModule I created for this:
public class CustomSessionAuthenticationModule : SessionAuthenticationModule
{
protected override void OnAuthenticateRequest(object sender, EventArgs eventArgs)
{
try
{
base.OnAuthenticateRequest(sender, eventArgs);
}
catch (SecurityTokenException exc)
{
// ID4243: Could not create a SecurityToken. A token was not found in the token cache and no cookie was found in the context.
if (exc.Message.IndexOf("ID4243", StringComparison.OrdinalIgnoreCase) >= 0)
{
// Returning directly without setting any token will cause the FederationAuthenticationModule
// to redirect back to the token issuer.
return;
}
else
{
throw;
}
}
}
}

Resources