why is keycloak removing the SSL in the redirect uri? - redirect

We have a simple requirement where:
PS: https:/ === https://
When user hits https:/company_landing.company.com , they should be redirected to keycloak login page (at https:/ourcompany-keycloak.company.com). User enters his/her keycloak login credentials. Upon successful login to keycloak , they will be presented to the company_landing page.
The trouble is :
When User types - https:/company_landing.company.com
Keycloak tries to bring up the landing page but gives 500 Internal server error and says "Incorrect redirect uri" and in the browser I see this:
https:/ourcompany-keycloak.company.com/auth/realms/realm1/tokens/login?client_id=company_dev&state=aaaafffff-559d-4312-a8be-123412341234&redirect_uri=http%3A%2F%2Fcompany_landing.company.com%3A8081%2F%3Fauth_callback%3D1
If you observe the redirect uri above, I think the problem is that instead of https the redirect uri starts with http and http:/company-landing.company.com doesn't exist.
Settings:
keycloak settings: -
Realm --> settings --> login : Require SSL = all Requests (tried with "external" also)
Applications-->realm1-->settings-->Redirect URI = https://company_landing.company.com/*
AWS load balancer:
Port config: 443(https) forwarding to 8443
I am confused as to why it is stripping the SSL? The above works fine when testing on local environment(probably because its http://localhost) but this always gives an invalid redirect url when trying to access any link that is ssl encrypted.
-mm

You have to add the following property in the proxy configuration json file, (by default proxy.json) as an application attribute (same level as "adapter-config"):
"proxy-address-forwarding" : true,
This configuration attribute is not documented, however present in the sources of the proxy configuration: https://github.com/keycloak/keycloak/blob/master/proxy/proxy-server/src/main/java/org/keycloak/proxy/ProxyConfig.java

You don't need a certificate to be installed or use changes in adapter config.
This needs to be done in your standalone.xml, standalone-ha or domain.xml (as the case may be) as documented in the Keycloak document reverse proxy section https://www.keycloak.org/docs/latest/server_installation/index.html#_setting-up-a-load-balancer-or-proxy
Assuming that your reverse proxy doesn’t use port 8443 for SSL you also need to configure what port HTTPS traffic is redirected to.
<subsystem xmlns="urn:jboss:domain:undertow:4.0">
...
<http-listener name="default" socket-binding="http"
proxy-address-forwarding="true" redirect-socket="proxy-https"/>
...
</subsystem>
Add the redirect-socket attribute to the http-listener element. The value should be proxy-https which points to a socket binding you also need to define.
Then add a new socket-binding element to the socket-binding-group element:
<socket-binding-group name="standard-sockets" default-interface="public"
port-offset="${jboss.socket.binding.port-offset:0}">
...
<socket-binding name="proxy-https" port="443"/>
...
</socket-binding-group>

Related

Elytron programmatic login with FORM authentication

we are currently migrating from legacy security subsystem to Elytron and have a Struts2 based web application deployed in JBoss EAP 7.3.6 which should support multiple "flavors" of authentication.
The standard way of logging in should be that a user manually provides credentials in a login form (j_security_check) and clicks the corresponding button. This works well with Elytron in our setup.
The second possibility is, that the GET request to protected content of the web application can contain a custom cookie that contains a JWT token. This cookie is intercepted by a io.undertow.server.HttpHandler which deals with the incoming request in its io.undertow.server.HttpHandler#handleRequest method. This handler is registered by io.undertow.servlet.api.DeploymentInfo#addSecurityWrapper with a DeploymentInfo which is provided by an implementation of io.undertow.servlet.ServletExtension. The ServletExtension is registered as a service provider in META-INF/services/io.undertow.servlet.ServletExtension.
The request handling in our implementation of io.undertow.server.HttpHandler#handleRequest extracts the JWT token from the cookie, pre-validates it and determines the contained username. This username and the token as a password are used as inputs for a call to javax.servlet.http.HttpServletRequest#login.
With the legacy security subsystem, the behavior of the server was, that this call to login triggered the authentication against the configured legacy security domain AND created a session in Undertow so that the HTTP 200 response for the previous GET request contained a Set-Cookie header with a fresh JSESSIONID cookie.
With Elytron, javax.servlet.http.HttpServletRequest#login doesn't do anything, neither an authentication against an Elytron security domain and security realm nor the creation of a session is triggered. The browser simply shows the login form which should get skipped by the described interception process.
I debugged the implementation of javax.servlet.http.HttpServletRequest#login that comes with JBoss. We start in io.undertow.servlet.spec.HttpServletRequestImpl#login which calls login = sc.login(username, password). This SecurityContext, when using Elytron, is org.wildfly.elytron.web.undertow.server.SecurityContextImpl. org.wildfly.elytron.web.undertow.server.SecurityContextImpl#login first checks if (httpAuthenticator == null). The httpAuthenticator is only set in org.wildfly.elytron.web.undertow.server.SecurityContextImpl#authenticate which gets called by a call to javax.servlet.http.HttpServletRequest#authenticate.
This explains, why a plain call to io.undertow.servlet.spec.HttpServletRequestImpl#login was doing nothing. I tried to call javax.servlet.http.HttpServletRequest#authenticate first, to instantiate that httpAuthenticator internally, and then javax.servlet.http.HttpServletRequest#login. This at least finally triggered the authentication and authorization against the configured Elytron security domain and security realm. Authentication/authorization were successful but Undertow still didn't issue a new JSESSIONID cookie and the browser again showed the login form instead of proceeding to the protected resources.
I'm currently out of ideas, how to proceed with this issue und how to achieve the same behavior as with the legacy security subsystem. Why does the Elytron implementation of io.undertow.security.api.SecurityContext behave so differently compared to the one for legacy security (io.undertow.security.impl.SecurityContextImpl)? How am I supposed to log in programatically in a FORM based web application using Elytron with javax.servlet.http.HttpServletRequest#login and/or javax.servlet.http.HttpServletRequest#authenticate?
The relevant JBoss configuration for all this looks like this:
Undertow:
<application-security-domains>
<application-security-domain name="my_app_security_domain" http-authentication-factory="MyHttpAuthFactory"/>
</application-security-domains>
Elytron:
<security-domains>
<security-domain name="MySecurityDomain" default-realm="MyCachingRealm" permission-mapper="default-permission-mapper">
<realm name="MyCachingRealm" role-decoder="FromRolesAttributeDecoder"/>
</security-domain>
</security-domains>
<security-realms>
<custom-realm name="MyCustomRealm" module="module name redacted" class-name="class name redacted"/>
<caching-realm name="MyCachingRealm" realm="MyCustomRealm" maximum-age="300000"/>
<identity-realm name="local" identity="$local"/>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<constant-realm-mapper name="MyRealmMapper" realm-name="MyCachingRealm"/>
<simple-role-decoder name="FromRolesAttributeDecoder" attribute="Roles"/>
</mappers>
<http>
<http-authentication-factory name="MyHttpAuthFactory" security-domain="MySecurityDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="FORM" realm-mapper="MyRealmMapper">
<mechanism-realm realm-name="MyRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
This was a bug in JBoss EAP which has been fixed in EAP 7.3.8 and 7.4.1. See https://issues.redhat.com/browse/JBEAP-21737 and https://issues.redhat.com/browse/JBEAP-21738 for details.

OKTA(IdP) - Shibboleth(SP) with reverse proxy to Tomcat

I am spinning a big wheel now. please shed some light.
Reverse proxy is working with Apache. So, when I access https://hostname/app/default.html, it opens Tomcat app url. No issue.
The tomcat app currently redirects to https://hostname/app/login.html which has a login box.
1) Do I need to disable UserDatabase on Tomcat server.xml ?
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
2) Is this Shibboleth configuration correct ?
But, when I try configure this with OKTA- Shibboleth(3.0), it's looping OKTA SSO url.
In shibboleth2.xml
<ApplicationDefaults id="default"
entityID="https://hostname/shibboleth-sp"
REMOTE_USER="userid" >
<SSO entityID="http://www.okta.com/~~~~">
OKTA's metadata is downloaded and located with shibboleth2.xml file.
cert is also generated and placed in the same folder.
3) Is this OKTA configuration correct ?
In OKTA widget configuration menu,
- Single sign on url :https://hostname/Shibboleth.sso/SAML2/POST
- recipient url : https://hostname/Shibboleth.sso/SAML2/POST
- destination url :https://hostname/Shibboleth.sso/SAML2/POST
- audience restriction :https://hostname/shibboleth-sp <-- above SP entityID
- default relay state : ??
right now, when I click on the widget on OKTA, it's looping.
https://hostname/Shibboleth.sso/SAML2/POST
contains SAML response.
then, it redirects to OKTA SSO url. It never ends.
https:// xxx.oktapreview.com/app/xx_reverse_proxy_/xxxx/sso/saml?SAMLRequest=~~~&RelayState=~~~
This contains SAML request but it looks like this.
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://hostname/Shibboleth.sso/SAML2/POST"
Destination="https://okta sso/sso/saml"
ID="xx"
IssueInstant="2018-11-02T15:39:24Z"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Version="2.0">
<saml:Issuer
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://hostname/shibboleth-sp
</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="1"/>
Is this Issuer url correct? Why is it looping and how to fix ?
Re Q#1: You only need Tomcat users if you're going to protect an application with it, such as the Tomcat manager. Otherwise, no.
Re Q#2: You list <SSO entityID="http://www.okta.com/~~~~"> but Destination="https://okta sso/sso/saml" from the SAML. You might want to check http/https. This is a very common cause of looping. Eliminate any potential http/https inconsistency.
FWIW Issuer looks correct to me... that's what you specify in entityID="https://hostname/shibboleth-sp"

Keycloak state parameter invalid

When user clicks login, redirected to Keycloak login page & then after successful login, user comes back to application with 400 error page.
Server log shows following:
[Server:node-00] 13:40:00,709 WARN
[org.keycloak.adapters.OAuthRequestAuthenticator] (default task-30)
state parameter invalid
My application conf is:
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
<secure-deployment name="appWEB.war">
<realm>demo</realm>
<resource>app</resource>
<public-client>true</public-client>
<auth-server-url>http://localhost:8180/auth</auth-server-url>
<ssl-required>EXTERNAL</ssl-required>
</secure-deployment>
</subsystem>
Application URL is https://localhost:8443/app & redirect_url is https://localhost:8443/app/private.jsf.
When I use http, it works. But the error comes when I use same with https.
Any thoughts?
Here it can be many scenario which may failing with https
Keycloak running in https
Create self sign certification for keycloak.
Import this certificate to your local Java environment.SO handshake can be possible.
I hope you generate the certificates in keycloak you can find the the certificate inside keycloak/security/ssl.

Spring Security X.509 Preauth

I'm using Spring Security 2.x's Preauthentication with X.509 certificates.
I get the certificateText via HttpServletRequest.getAttribute("CERTIFICATE").
Sometimes, the above call returns "" (empty). I believe it occurs when the HTTP session has expired.
What would explain why HttpServletRequest.getAttribute("CERT") returns empty?
EDIT In Kerberos, for example, the ticket is available in every HTTP request. Is the cert not always in X.509 HTTP requests?
Please access to certificate using this code:
X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
Certificate is always populated to request after successful client certificate authentication.
Ensure your support long certificate chain:
Add the max_packet_size propery to the worker.properties file
worker.ajp13w.max_packet_size=65536
Add the packetSize propery to the configuration of Ajp connector in the Tomcat configuration \conf\server.xml
<Connector port="8089"
enableLookups="false" redirectPort="8443" protocol="AJP/1.3" packetSize="65536"/>
Apache logs:
http://httpd.apache.org/docs/2.2/logs.html#accesslog
http://httpd.apache.org/docs/2.2/logs.html#errorlog
http://httpd.apache.org/docs/2.2/mod/core.html#loglevel

link apache web server on port 80 and tomcat webapp on port 8080

On port 80 I have normal apache web server.
On port 8080 I have tomcat with client and server side stuff.
My goal is:
www.mydomain.com renders a static and SEO friendly index.html while javascript stuff is loading.
In the header of this index.html I load www.mydomain.com:8080/myapp/stuff.js
stuff.js is compiled with gwt and calls a RootLayoutPanel.get().add(nice_panel) which will remove static content and show dynamic widgets. It also calls servlets (server side code).
Problem: for security reasons, browsers wont let me load www.mydomain.com:8080/myapp/stuff.js because it is on a different port.
Wrong attempt: I tried to create a symlink from "normal" apache web server directory to the tomcat webapp containing stuff.js. I am now able to load stuff.js because its url is: www.mydomain.com/mysymlink_to_tomcat/stuff.js. But stuff.js is not able anymore to call servlets on server side again because of browsers security rules ("XMLHttpRequest cannot load ... origin ...is not allowed by Access-Control-Allow-Origin").
I would like to avoid the "crazy" solution of redirect from index.html to tomcat with header('location: http://mydomain.com:8080/another_index_on_tomcat.html'). This solution works but it has many drawbacks (SEO...)
What would be the best approach ?
Thanks.
You have basically two solutions:
make it work with the 2 origins: use the xsiframe linker in GWT to allow the page on :80 to load the script from :8080 (for readers: it's not about loading, it's about what the script does).
Add the following to your `gwt.xml:
<add-linker name='xsiframe' />
That unfortunately won't solve your issue with GWT-RPC (o whatever you use to talk to the server). For that, there's CORS.
use a single origin: use Apache's mod_proxy (or mod_jk) to proxy your Tomcat through your Apache. Nobody will ever use :8080, everything will go through :80. See Using Tomcat with Apache HTTPD and a proxy at https://developers.google.com/web-toolkit/doc/latest/DevGuideServerCommunication#DevGuideRPCDeployment
And of course there's also the solution of ditching the HTTPD and serving everything with Tomcat (recent Java and Tomcat versions have fixed their slowness issues).
I'm not sure if this would avoid the security error, but you could try an iframe. On apache, you have the index and an iframe to the tomcat, where the JS loads inside the iframe. Dunno if that will help with the SEO problem.
The best solution would be to redirect the port 80 calls to 8080 on apache when the client call is asking for a tomcat application.
Install mod_jk on apache and configure it to mount a context on the path you want
example: (edit /mods_enabled/jk.conf)
# Configure access to jk-status and jk-manager
# If you want to make this available in a virtual host,
# either move this block into the virtual host
# or copy it logically there by including "JkMountCopy On"
# in the virtual host.
# Add an appropriate authentication method here!
<Location /jk-status>
# Inside Location we can omit the URL in JkMount
JkMount jk-status
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Location>
<Location /jk-manager>
# Inside Location we can omit the URL in JkMount
JkMount jk-manager
Order deny,allow
Deny from all
Allow from 127.0.0.1
</Location>
JkMount /*/myAppDir/* ajp13
Then add a virtual host in your site settings (edit /apache2/sites-enabled/)
<VirtualHost *:80>
. Here is the rest of the
. of the config of
. the host
# Tomcat jk connector settings
JkMount /*.jsp ajp13_worker
JkMount /myAppDir/* ajp13_worker
JkMount /myAppDir* ajp13_worker
JKMount /manager* ajp13_worker
JkMount /manager/* ajp13_worker
</VirtualHost>
And you should also edit the server.xml file and inside the tag write and comment the previous Host name="localhost"
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Host name="localhost" appBase="webapps" unpackWARs="true"
autoDeploy="true" >
<Context path="/" docBase="/var/lib/tomcat7/webapps/myAppDir/"
debug="0" reloadable="true" />
<!-- please notes on logs down below -->
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="/var/lib/tomcat7/logs" prefix="tomcat_access_"
suffix=".log" pattern="common" resolveHosts="false" />
</Host>
The only thing left to do is edit the workers.properties file and add
worker.myapp2.port=8009
worker.myapp2.host=localhost
worker.myapp2.type=ajp13
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=ajp13_worker
Then you should be set to work, and when a url containing the myAppDir appears, the apache server will redirect the calls to tomcat the answer will come back from apache.