I am attempting to configure an UAA (https://github.com/cloudfoundry/uaa) SSO server to use CAS5 as an IDP.
The call to 'cas/authorize' is succeeding. The callback URL is called to the UAA server. The UAA server is then trying to call 'cas/accessToken' during the serving of the 'callback' URL endpoint to the browser, i.e. back-end HTTP request is being made back to CAS5.
I have traced through the execution of the 'cas/accessToken' request within CAS5. The request is failing on the Pac4j Profile lookup. The profile is trying to be accessed via either the request object or the session object. For the previous call to 'cas/authorize' the lookup succeeds via the session but for the 'cas/accessToken' the session does not contain the profile because the request is originating from the UAA server backend and not the user's browser.
Is this a bug with the OIDC support in CAS5? How do I get the Pac4j Profile lookup to succeed here?
Here is the stack trace where the profile lookup fails within CAS5:
ProfileManager<U>.retrieveAll(boolean) line: 69
ProfileManager<U>.get(boolean) line: 35
OAuth20AuthorizeEndpointController.isRequestAuthenticated(ProfileManager,J2EContext) line: 142
OidcAuthorizeEndpointController(OAuth20AuthorizeEndpointController).handleRequest(HttpServletRequest, HttpServletResponse) line: 109 OidcAuthorizeEndpointController.handleRequest(HttpServletRequest,HttpServletResponse) line: 86
I am using 5.1.0 not 5.2.2. I had rolledback to 5.1.0 around this time.
The problem still exists in the 5.1.0 code. I resolved by modified the OidcIdTokenGeneratorService to pull the CLAIM_PREFERRED_USERNAME from the TGT:
if (!claims.hasClaim(OidcConstants.CLAIM_PREFERRED_USERNAME)) {
String username = accessTokenId.getGrantingTicket().getAuthentication().getCredentials().get(0).getId();
claims.setClaim(OidcConstants.CLAIM_PREFERRED_USERNAME, username);
}
vs
if (!claims.hasClaim(OidcConstants.CLAIM_PREFERRED_USERNAME)) {
claims.setClaim(OidcConstants.CLAIM_PREFERRED_USERNAME, profile.getId());
}
There were some other calls upstream in the OAuth20AccessTokenEndpointController to get the CLIENT_ID via pac4j. The CLIENT_ID is also available as a request param. Changed calls similar to:
final String clientId = uProfile.getId();
to
final String clientId = request.getParameter(OAuth20Constants.CLIENT_ID);
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'm working on a Java API that functions as an endpoint API, and on production
it runs on the Google Cloud Platform. API methods are called by passing a Firebase token as part of the URL, and the token is used to create a User that's available inside the API method:
#ApiMethod(path = "myPath/{tokenId}/doSomething", httpMethod = "get")
public ResponseMessage ReturnSomething(#Named("tokenId") String tokenId, User user) throws UnauthorizedException, BadRequestException, InternalServerErrorException, FirebaseAuthException
{
if (user == null)
...
In production, when the URL is called from an Angular application on Firebase that passes the token in the URL, user is correctly created. I don't fully understand how the User is created from the token, I only know that it somehow happens "automatically" as part of Firebase integration with Google Cloud.
I want to debug the API locally by using Debug As > App Engine from inside Eclipse. When I do this however, and call the API from my local Angular application running using Firebase serve, the token is correctly passed to my locally running API, however user is always null.
#ApiMethod(path = "myPath/{tokenId}/doSomething", httpMethod = "get")
public ResponseMessage ReturnSomething(#Named("tokenId") String tokenId, User user) throws UnauthorizedException, BadRequestException, InternalServerErrorException, FirebaseAuthException
{
if (user == null)
// this is always null
I suspect this is a problem with my locally running Java API correctly authenticating to Firebase. I've looked at this guide, which suggests that the GOOGLE_APPLICATION_CREDENTIALS property on Windows should be set to the path of the JSON key of the App Engine default service account, which is the normal way to ensure that local access is granted to Google Cloud (and presumably Firebase) resources.
I've added this explicitly (I'd already run gcloud auth application-default login anyway, using the command line) however it's still not working. I just get null for the user and there's no indication of what's going on. I don't want to programatically authenticate as that means altering the API code to authenticate differently during debugging. How do I retrieve a User when debugging locally as App Engine?
UPDATE
I've realised that although the tokenId in the URL is present, I'm getting the following error when the API is called:
WARNING: Authentication failed: com.google.api.auth.UnauthenticatedException: No auth token is contained in the HTTP request
The tokenId value in the code below is a valid value, so I'm not sure why I'm getting this message:
#ApiMethod(path = "myPath/{tokenId}/doSomething", httpMethod = "get")
public ResponseMessage ReturnSomething(#Named("tokenId") String tokenId, User user)
I discovered that this was actually a problem with the Auth0 library that's being used in Angular to support authenticated HTTP requests to the Java API. The Auth0 library is used to inject the auth token into the Bearer of the request header whenever an Angular http.get is called from the Angular application. Creation of the User depends on this property being present in the HTTP header, with its value set to the value of the auth token.
I fixed this by altering the config for this library. I needed to temporarily whitelist localhost for the port (8080) that the API runs on, to allow Auth0 to inject the token into the HTTP header whenever there is a request to localhost:8080
const jwtConf: JwtModuleOptions = {
config: {
tokenGetter: getToken,
whitelistedDomains: ['localhost:8080']
}
};
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.
I am able to log in to pentaho user console by calling
protocal&hostname/pentaho/j_spring_security_check?j_username=joe&j_password=password
from java class
after that I want to call web service call with url
protocal&hostname/pentaho/SolutionRepositoryService?component=createNewFolder&solution=New_Report&path=&name=suzy_reports&desc=suzy_reports that java class itself
but on calling second url it's giving
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 401 for URL: protocal&hostname/pentaho/Login;jsessionid=F096F1C25268
D1A795A55892511D6506
but both url are working alone fine
-is there any way to have single url for both calls?
-calling first url and having session stored so that it doesn't ask for authentication for second?
or any other solution?
401 mean Unauthorized.
I think you need to support session at your side. After authorisation server create a session your client does not support.
You can try use HttpClient - it support session and cookies.
Do first call with HttpClient to autorize (will create a session and put cookies values)
Do second call with same HttpClient instance
I am using CXF to build client code for a JAX-RS REST service. This REST service unfortunately relies on cookies to authenticate each request and maintain other key session state. Accessing the user's account info involves two requests: one to login, and one to get account info. The session cookies retrieved in the first request must be sent with the second request.
Here is my code.
// Login (POST /sessions)
Response response = proxy.login(userCredentials);
assertEquals(200, response.getStatus());
// Get user's account info (GET /user)
response = proxy.getUser();
User user = response.readEntity(User.class);
The second request fails authentication because it does not include the required session cookies that were returned by the prior login operation.
I believe there is a way to configure the WebClient to maintain cookies across requests. I have searched high and low, but I cannot seem to find out how. I'm hoping someone can point me to an example. Thanks in advance!
I finally found a solution. I had to do the following before using the proxy.
WebClient.getConfig(proxy).getRequestContext().put(
org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE);