We're in the process of building a Worklight application which has a 2 step authentication process.
Step 1: Forms Authentication
We're using the out of the box WASLTPARealm which authenticates us against a custom JAAS module in WebSphere and returns an LTPA token. This works as expected.
Step 2: Custom Authenticator
The second step is a custom Authenticator and Login Module which:
Reads the LTPA cookie that were set in Step 1
Makes a POST request to another application with the LTPA cookie (these 2 apps are trusted via Single Sign-On)
The POST request returns a response with additional session cookies
The user is authenticated
The issue is that the Custom Authenticator doesn't fire when using the client code provided in the documentation. Basically
The customAuthenticator is created via the usual
var customAuthenticator = WL.Client.createChallengeHandler("MyCustomRealm");
Then further down in the client code
var reqURL = '/my_custom_auth_request_url';
var options = {};
options.parameters = {};
options.headers = {};
customAuthenticator.submitLoginForm(reqURL, options, customAuthenticator.submitLoginFormCallback);
Results in a 404
[27/05/13 10:35:07:616 NZST] 00000326 WebSphereForm I com.worklight.core.auth.ext.WebSphereFormBasedAuthenticator processRequest FWLSE0055I: Not recognized.
[27/05/13 10:35:07:616 NZST] 00000326 Authenticatio E com.worklight.core.auth.impl.AuthenticationFilter doFilter FWLSE0048E: Unhandled exception caught: SRVE0190E: File not found: /apps/services/my_custom_auth_request_url
java.io.FileNotFoundException: SRVE0190E: File not found: /apps/services/my_custom_auth_request_url
Which happens because the request gets picked up by the WebSphereFormBasedAuthenticator instead of our Custom Authenticator.
We're writing the request URL to the logs inside the custom authenticator via
public AuthenticationResult processRequest(HttpServletRequest request, HttpServletResponse response, boolean isAccessToProtectedResource) throws IOException, ServletException {
logger.info("Request URL is: " + request.getRequestURI());
But that line never gets hit.
Can 2 authenticators work side by side? The behaviour I'm seeing is that
var wlFormsAuthenticator = WL.Client.createChallengeHandler("WASLTPARealm");
and
var customAuthenticator = WL.Client.createChallengeHandler("MyCustomRealm");
seem to get mixed up. I thought that calling submitLoginForm on the customAuthenticator should not be getting picked up by the WebSphereForms authenticator and instead should go to our custom one.
Can you please clarify the expected behaviour and a potential Workaround?
Also, what DOES work is calling
WL.Client.login("MyCustomRealm", {
onSuccess: function() {
},
onFailure: function() {
}
});
In this case, the Java code gets hit and we successfully authenticate BUT, the URL is
http://localhosT:9080/worklight/apps/services/api/MyApp/common/login
Rather than my_custom_auth_request_url which means we can't filter out the requests in our Java code.
Hope this makes sense. Thanks in advance.
There seem to be a mix of terminology in your description.
Authenticator is server side entity. Custom Authenticator is implemented in Java.
Challenge handler is a client side entity. It is implemented in JavaScript.
From your description I'm guessing that you're trying to submit login form for custom authenticator before actually trying to get some resource from WL server. The problem is that instance of custom authenticator is only created once you try to access protected resource. Authenticator will refuse to receive credentials (or more generally - it will not really exist) unless you trigger authentication first. So your approach is correct, you might call WL.Client.login("realm") to trigger authentication process first. Other options are:
protect application environment using security test and set connectOnStartup:true
protect application environment using security test, set connectOnStartup:false and use WL.Client.connect() API.
Related
I am trying to implement Keycloak as an IAM, the Problem that I have is, that I need to authenticate the user (already working) but also authorize him. The authorization should be accomplished through keycloak directly, but the security information (like roles, etc.) is available over an REST interface externally.
The way it is working now goes as followed:
authentication request (default)
"authorization" request → keycloak server (with extra form param)
keycloak server → CustomProtocolMapper (calls external REST interface and adds claims to Token)
Token → frontend client
This worked until I used a refresh token to refresh the ID Token. The Cookie that is used to authenticate the user is not sent to the keycloak server, because of security reasons (Cookie labeled as "Secure" but connection over HTTP). To fix this I upgrade my keycloak server to use HTTPS/TLS and now i am getting errors because the "HttpRequest" is no longer available. Any ideas on how to get the Request Body of an HTTPS Request inside a CustomProtocolMapper? I know that the Authenticator Providers has access to it, but i dont know/ didnt find anyway to add claims to the Token inside it.
#Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession,
ClientSessionContext clientContext) {
String contextParamName = mappingModel.getConfig().get(CONTEXT_PARAMETER);
// worked with http
HttpRequest request = keycloakSession.getContext().getContextObject(HttpRequest.class);
String contextId = request.getFormParameters().getFirst("activeContext");
LOGGER.warn("activeContext: " + contextId);
}
Thanks in advance,
best regards
I have a requirement that I have to create a cookie on server side when page loads. In our AEM project I have a LocatorViewESBOfficeHelper.java file which is mapped to the JSP of the component (Say locateOffice). When page loads first time onGetData method of LocatorViewESBOfficeHelper.java is called.
In this method I want to create cookie using following code but I am unable to create it. When I use same create cookie code in a servlet and call it through ajax call, it successfully create cookie.
One possible reason could be when page is fully loaded, the response object could not have cookies. But I am unable to understand how to manage it.
You might have several problems.
Edit-Mode
You will not see session cookies in the editor, because your page is inside an iframe. Just do "View as published" (?wcmmode=disabled) to get your page directly in the browser window.
Wrong response object
The mapping LocatorViewESBOfficeHelper.java to the component sounds like someone tried to do some magic. Maybe your service doesn't get the real response object, but more a response wrapper. And the output of your service is somehow validated and written to the real response. In such cases, the http headers are often ignored.
Just create your own real AEM component, were the content is written by a SlingSafeMethodsServlet (registered via the resource type). If this works, you know where to go.
The following servlet does work good for me:
#Component(
service = Servlet.class,
property = {
SLING_SERVLET_RESOURCE_TYPES + "=/apps/myproject/components/cookie-test",
SLING_SERVLET_METHODS + "=GET",
SLING_SERVLET_EXTENSIONS + "=html"
})
public class CookieTestServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(#Nonnull SlingHttpServletRequest request, #Nonnull SlingHttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<h2>Cookie Test</h2>");
Cookie cookie = new Cookie("alex", "this-is-a-test-" + (System.currentTimeMillis() % 10000));
response.addCookie(cookie);
}
}
Response buffer already flushed
The HttpServletResponse is more like a buffered Writer or OutputStream. You can only add http headers (or cookies) as long as the response output buffer was not flushed yet.
To verify this, either use the test component on a smaller page or simply increase in the OSGi configs the buffer to something real high (e.g. 1MB - but on your local instance only). Therefore go to the web console / OSGi / Configuration / Apache Felix Jetty Based Http Service / Response buffer size
(http://localhost:4502/system/console/configMgr/org.apache.felix.http)
I don't recommend to change it in production. So you should find another way to be early in the content generation. As most extreme workaround you could use a Sling request filter.
Opt-Out-Filter
If your project uses the Adobe Granite Opt-Out Service, then you might have to whitelist your cookie in the OSGi configuration. By default this service should not do anything.
Try Other Alternatives
As alternative I would propose:
Use a Sling Filter
Create the Cookie front-end with JavaScript
I hope one of my ideas might point you in the right direction.
Alex
I have a problem with Vertx oauth2.
I followed this tutorial http://vertx.io/docs/vertx-web/java/#_oauth2authhandler_handler:
OAuth2Auth authProvider = OAuth2Auth.create(vertx, OAuth2FlowType.AUTH_CODE, new OAuth2ClientOptions()
.setClientID("CLIENT_ID")
.setClientSecret("CLIENT_SECRET")
.setSite("https://github.com/login")
.setTokenPath("/oauth/access_token")
.setAuthorizationPath("/oauth/authorize"));
// create a oauth2 handler on our domain: "http://localhost:8080"
OAuth2AuthHandler oauth2 = OAuth2AuthHandler.create(authProvider, "http://localhost:8080");
// setup the callback handler for receiving the GitHub callback
oauth2.setupCallback(router.get("/callback"));
// protect everything under /protected
router.route("/protected/*").handler(oauth2);
// mount some handler under the protected zone
router.route("/protected/somepage").handler(rc -> {
rc.response().end("Welcome to the protected resource!");
});
// welcome page
router.get("/").handler(ctx -> {
ctx.response().putHeader("content-type", "text/html").end("Hello<br>Protected by Github");
});
The ideas is to have in the protected folder all the webpages that requires auth.
When I want to access to protected webpage I get redirected to the microsoft login site and after the login I get redirected to my callback.
What I don´t understand is how to handle the callback now?
I get something like this as response:
https://localhost:8080/callback?code=AAABAAA...km1IgAA&session_state=....
How I understood (https://blog.mastykarz.nl/building-applications-office-365-apis-any-platform/) I need to extract somehow the code and the session-state and send back with a post to:
https://login.microsoftonline.com/common/oauth2/token
in order to get the token.
But I did not understand how this can be done with Vertx.
Any help? How to extract the code and session and send back to Microsoft?
I found some tutorials here: https://github.com/vert-x3/vertx-auth/blob/master/vertx-auth-oauth2/src/main/java/examples/AuthOAuth2Examples.java but did not help me.
I am doing this with Azure authentication (in tutorial is written Github but i changed all this to Microsoft).
Are you behind a proxy? The callback handler sends a request to the provider from the application and not from a browser. For me this froze the whole application. You can set the proxy with OAuth2ClientOptions given to the OAuth2Auth.create
As mentioned in the official vert.x-web document, the handling of the auth flow (including access token request to microsoft) is handled by OAuth2AuthHandler:
The OAuth2AuthHandler will setup a proper callback OAuth2 handler so the user does not need to deal with validation of the authority server response.
This being said, there is no need for application to manually handle it. Instead of using example from vertx-auth, try this one instead which actually uses OAuth2AuthHandler.
I'm struggling to successfully make a web service call to a SOAP web service from a web page. The web service is a Java web service that uses JAX-WS.
Here is the web method that I'm trying to call:
#WebMethod
public String sayHi(#WebParam(name="name") String name)
{
System.out.println("Hello "+name+"!");
return "Hello "+name+"!";
}
I've tried doing the web service call using the JQuery library jqSOAPClient (http://plugins.jquery.com/project/jqSOAPClient).
Here is the code that I've used:
var processResponse = function(respObj)
{
alert("Response received: "+respObj);
};
SOAPClient.Proxy = url;
var body = new SOAPObject("sayHi");
body.ns = ns;
body.appendChild(new SOAPObject("name").val("Bernhard"));
var sr = new SOAPRequest(ns+"sayHi",body);
SOAPClient.SendRequest(sr,processResponse);
No response seems to be coming back. When in jqSOAPClient.js I log the xData.responseXML data member I get 'undefined'. In the web service I see the warning
24 Mar 2011 10:49:51 AM com.sun.xml.ws.transport.http.server.WSHttpHandler handleExchange
WARNING: Cannot handle HTTP method: OPTIONS
I've also tried using a javascript library, soapclient.js (http://www.codeproject.com/kb/Ajax/JavaScriptSOAPClient.aspx). The client side code that I use here is
var processResponse = function(respObj)
{
alert("Response received: "+respObj);
};
var paramaters = new SOAPClientParameters();
paramaters.add("name","Bernhard");
SOAPClient.invoke(url,"sayHi",paramaters,true,processResponse);
I've bypassed the part in soapclient.js that fetches the WSDL, since it doesn't work
(I get an: IOException: An established connection was aborted by the software in your host machine on the web service side). The WSDL is only retrieved for the appropriate name space to use, so I've just replaced the variable ns with the actual name space.
I get exactly the same warning on the web service as before (cannot handle HTTP method: OPTIONS) and in the browser's error console I get the error "document is null". When I log the value of req.responseXML in soapclient.js I see that it is null.
Could anyone advise on what might be going wrong and what I should do to get this to work?
I found out what was going on here. It is the same scenario as in this thread: jQuery $.ajax(), $.post sending "OPTIONS" as REQUEST_METHOD in Firefox.
Basically I'm using Firefox and when one is doing a cross domain call (domain of the address of the web service is not the same as the domain of the web page) from Firefox using AJAX, Firefox first sends an OPTIONS HTTP-message (before it transmits the POST message), to determine from the web service if the call should be allowed or not. The web service must then respond to this OPTIONS message to tell if it allows the request to come through.
Now, the warning from JAX-WS ("Cannot handle HTTP method: OPTIONS") suggests that it won't handle any OPTIONS HTTP-messages. That's ok - the web service will eventually run on Glassfish.
The question now is how I can configure Glassfish to respond to the OPTIONS message.
In the thread referenced above Juha says that he uses the following code in Django:
def send_data(request):
if request.method == "OPTIONS":
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response['Access-Control-Max-Age'] = 1000
response['Access-Control-Allow-Headers'] = '*'
return response
if request.method == "POST":
# ...
Access-Control-Allow-Origin gives a pattern which indicates which origins (recipient addresses) will be accepted (mine might be a bit more strict than simply allowing any origin) and Access-Control-Max-Age tells after how many seconds the client will have to request permission again.
How do I do this in Glassfish?
Have you actually tested that ws is working properly?
You can use SoapUI for inspecting request/response etc.
When you confirm that ws is working from SoapUI, inspect what is format of raw Soap message. Then try to inspect how it looks before sending with .js method, and compare them.
It might help you understand what is wrong.
Check if this helps
http://bugs.jquery.com/attachment/ticket/6029/jquery-disable-firefox3-cross-domain-magic.patch
it's marked as invalid
http://bugs.jquery.com/ticket/6029
but it might give you some hint
On the other hand, instead to override proper settings for cross-domain scripting might be better if you can create and call local page that will do request to ws and return result.
Or even better, you can create page that will receive url as param and do request to that url and just return result. That way it will be more generic and reusable.
We have a JSF web application that uses Acegi security. We also have a standalone Java Swing application. One function of the Swing app is to load the user's home page in a browser window.
To do this we're currently using Commons HttpClient to authenticate the user with the web app:
String url = "http://someUrl/j_acegi_security_check";
HttpClient client = new HttpClient();
System.setProperty(trustStoreType, "Windows-ROOT");
PostMethod method = new PostMethod(url);
method.addParameter("j_username", "USERNAME");
method.addParameter("j_password", "PASSWORD");
int statusCode = client.executeMethod(method);
if (statusCode == HttpStatus.SC_MOVED_TEMPORARILY ) {
Header locationHeader= method.getResponseHeader("Location");
String redirectUrl = locationHeader.getValue();
BrowserLauncher launcher = new BrowserLauncher();
launcher.openURLinBrowser(redirectUrl);
}
This returns a HTTP 302 redirect response, from which we take the redirect url and open it using BrowserLauncher 2. The url contains the new session ID, something like:
http://someUrl/HomePage.jsf;jsessionid=C4FB2F643CE48AC2DE4A8A4C354033D4
The problem we're seeing is that Acegi processes the redirect but throws an AuthenticationCredentialsNotFoundException. It seems that for some reason the authenticated credentials cannot be found in the security context.
Does anyone have an idea as to why this is happening? If anyone needs more info then I'll be happy to oblige.
Many thanks,
Richard
I have never done Acegi/SpringSecurity, but the symptoms are clear enough: some important information is missing in the request. You at least need to investigate all the response headers if there isn't something new which needs to be passed back in the header of the subsequent request. Maybe another cookie entry which represents the Acegi credentials.
But another caveat is that you in fact cannot open just the URL in a local browser instance, because there's no way to pass the necessary request headers along it. You'll need to have your Swing application act as a builtin webbrowser. E.g. get HTML response in an InputStream and render/display it somehow in a Swing frame. I would check if there isn't already an existing API for that, because it would involve much more work than you'd initially think .. (understatement).
In this case you can do Basic Authentication and set this header in every request instead of sending the jsessionid:
AUTHORIZATION:Basic VVNFUk5BTUU6UEFTU1dPUkQ=
The token VVNFUk5BTUU6UEFTU1dPUkQ= is the username and the password encoded base64.
Example:
scott:tiger
is:
c2NvdHQ6dGlnZXI=
One more thing: use SSL.