Keycloak GSS Credential delegation when browser not in AD Domain - java

I have a Web App that uses Keycloak/OpenID Connect to authenticate a user to an Windows AD.
Users will not be using a browser on a workstation in the Windows AD domain.
The Web App server (Tomcat with Keycloak adapter) is running in the Windows AD domain.
The Web App is configured for Keycloak/OpenID Connect.
Keycloak realm is configured to use the Windows AD Kerberos/LDAP.
The user browser forwards to the keycloak login and following a successful login, forwards back to the web app.
The Web App needs to connect to an IBM i using Kerberos ticket/GSS Credential
The IBM i is configured for SSO/EIM using the Windows AD. It works.
I configured the Keycloak client for GSS Credential Forwarding.
I try to get the GSS Credential from the Servlet request using the Keycloak client
// Obtain accessToken in your application.
KeycloakPrincipal<KeycloakSecurityContext> kcp = (KeycloakPrincipal<KeycloakSecurityContext>)request.getUserPrincipal();
AccessToken at = kcp.getKeycloakSecurityContext().getToken();
String username = at.getPreferredUsername();
wtr.append("Windows User: ").append(username).append(newLine);
// Retrieve kerberos credential from accessToken and deserialize it
Map<String, Object> otherClaims = at.getOtherClaims();
Object otherClaim = otherClaims.get(KerberosConstants.GSS_DELEGATION_CREDENTIAL);
String serializedGSSCred = (String) otherClaim;
GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(serializedGSSCred);
The "otherClaims" map is empty.
So deserializing throws a null pointer exception with the message
org.keycloak.common.util.KerberosSerializationUtils$KerberosSerializationException: Null credential given as input. Did you enable kerberos credential delegation for your web browser and mapping of gss credential to access token?, Java version: 1.8.0_152, runtime version: 1.8.0_152-b16, vendor: Oracle Corporation, os: 6.2
at org.keycloak.common.util.KerberosSerializationUtils.deserializeCredential(KerberosSerializationUtils.java:70)
What am I missing?

As "Users will not be using a browser on a workstation in the Windows AD domain", Keycloak will never receive GSS credentials from browser and so cannot forward them to your Java Web App.
As far as I found in Keycloak documentation Kerberos section, Keycloak does not support Kerberos Constrained Delegation (yet) and so cannot impersonate user - i.e. generate a TGT on behalf on end-user based on its login name.
From my point of view, your Java Web App has to invoke Kerberos Constrained Delegation S4U2Self for impersonation and then request a TGS for expected SPN with S4U2Proxy to autenticate with IBM i service.
You can report to following examples to get that achieved:
https://github.com/ymartin59/java-kerberos-sfudemo
https://github.com/tellisnz/collared-kerberos

For the browser to be able to negotiate (SPNEGO) it needs to be on the AD domain (also the delegation needs to be setup at the AD level, using msDS-AllowedToDelegateTo field) in order for the KC to impersonate the user on the backend service.
I would expect you get a 401 (Unauthorized) to which your browser cannot respond as it won't be able to get a kerberos ticket.
You could in theory do basic authentication against the web server, get a kerberos ticket on your webapp and forward it to the backend...

Related

How can I make my spring boot application to log in to keycloak with given username and password?

I have a spring-boot application and I am using keycloak to authenticate users. I am planning to make an endpoint in my application that waits a username and a password, it logs-in to keycloak and if the username/password is valid ask for an access token and give it back to the user.
How can I make this log-in to keycloak?
As I see I need to have a public client to make my keycloak use username/password, but this does not contain any resource_id so I need an access_token from a confidential client.
Should I ask for an access_token from the public client, and if the response code is 200 (so the login is successful) then send another request to the confidential client to get back an access_token that is actually working?
Thanks in advance.
I am planning to make an endpoint in my application what waits a
username and a password, it logs into the keycloak and if the
username/password is valid ask for an access token and give it back to
the user.
If we assume that
the Spring App is configured correctly;
the Keycloak client that the users will be authenticating against uses Authorization Code Flow (i.e., Standard flow Enabled on Keycloak), or even better Authorization code flow with PCKE
then when a user logins into your app, the user gets redirected to Keycloak for authentication. The user is redirected (probably) back to your app (if the authentication is successful), and your app receives, among others, an access token. That access token can then be used to perform actions on behalf of the authenticated user.
As I see I need to have a public client to make my keycloak use
username/password, but this does not contain any resource_id so I need
an access_token from a confidential client.
From the RFC 6749 OAuth 2.0 specification one can read:
confidential
Clients capable of maintaining the confidentiality of their
credentials (e.g., client implemented on a secure server with
restricted access to the client credentials), or capable of secure
client authentication using other means.
public
Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
Since you are neither using a pure web browser application nor a mobile phone but instead a spring-boot application with a secure backend, you should use a confidential client.
You are mixing up concepts if you mean that you want to use "Resource Owner Password Credentials Grant" (i.e., Direct Access Grants Enabled in Keycloak). You can still used with your application without having to use a confidential client. With a public client the request for a token from Keycloak looks like:
POST -d "client_id=<client_id>"
-d "username=<username>"
-d "password=user_password"
-d "grant_type=password"
<KEYCLOAK_HOST>/auth/realms/<REALM_NAME>/protocol/openid-connect/token>
with a confidential client:
POST -d "client_id=<client_id>"
-d "client_secret=<client_secret>"
-d "username=<username>"
-d "password=user_password"
-d "grant_type=password"
<KEYCLOAK_HOST>/auth/realms/<REALM_NAME>/protocol/openid-connect/token>
you got the extra field -d "client_secret=<client_secret>".
Bear in mind, however, that:
The resource owner password credentials grant type is suitable in
cases where the resource owner has a trust relationship with the
client, such as the device operating system or a highly privileged
application. The authorization server should take special care when
enabling this grant type and only allow it when other flows are not
viable.

Java ADFS SSO migration

I to migrate my application to ADFS SSO.The flow is simple. Our application needs to get information from ADFS and send it to my server. I have no access to the ADFS. I have written a little application to test communication with ADFS, it uses OneLogin and it is deployed on Azure Ubuntu VM machine. But when I try to redirect to the ADFS server it says : "An error occurred. Contact your administrator for more information.". I guess it is because ADFS needs to know about my application. What information do I need to give to ADFS administration so that we can communicate? My app has no spring.
What client side protocol stack are you using?
SAML, WS-Fed, ... ?
Do you have protocol metadata you can give the admin.?
What error is showing in the ADFS event log?
Update
You need a client side stack - SAML : SAML connectivity / toolkit .
From the ADFS metadata, you need the signing certificate, the entityID and the SSO and SLO endpoints.
You add these to the config. options of whatever stack you choose.

AWS MQTT Websocket, authenticated access being restricted

I'm currently working on an AWS mobile app project in Android Studio, and I'm currently trying to set up a MQTT-connection to AWS IoT through AWS Cognito & IAM authentication.
If I run the MQTT-client as a stand-alone project without authentication, assuming an unauthenticated IAM role, I'm able to connect to MQTT, and subscribe & recieve from the given topic.
However, once I try to run my MQTT-client through an authenticated user (through a log-in on my mobile app), the client is restricted, and constantly tries to reconnect.
I enabled logging in AWS CloudWatch, and the following debug message is given:
"... EVENT:MQTT Client Connect MESSAGE:Connect Status: AUTHORIZATION_ERROR"
The unauth and auth roles in the IAM console have identical policies & resource access, yet something's stopping the MQTT connection.
PS. The Auth role can, in my app, access userfiles & S3, the probelem seems to be unique for IoT access.
Does anyone know if it's possible to allow connections through an authorized user without using cert-files, if so, how? Unless I'm mistaken Cognito and IAM should be able to perform the required authorization to access resources (and it does, as long as I'm not logged in to the auth role)
Appreciate any tips I can get at this point, been struggling for a while.
I eventually figured out the issue by reading the AWS documentation more thoroughly.
The AWS credentials provider does not store the required information to authorize access through AWS. So I solved the issue by retreiving the required user tokens into a hash, and setting these to the credentials provider, and the issue was finally solved.
AuthenticationResultType authenticationResultType = new AuthenticationResultType();
String idToken = authenticationResultType.getIdToken();
// Initialize the AWS Cognito credentials provider
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
COGNITO_POOL_ID,
MY_REGION
);
Map<String, String> logins = new HashMap<String, String>();
logins.put("yourAWSEndpoint", idToken);
credentialsProvider.setLogins(logins);

Multiple SSO providers in Spring Boot Auth server

I have read and implemented my own Auth server following this tutorial from Spring. There are multiple SSO providers - Facebook, Github and a custom auth server. In this tutorial, the auth server contains the handling of other SSO providers.
I have a separate resource server that links to my auth server using the following properties:
security.oauth2.resource.userInfoUri=http://localhost:9000/user
I am able to get the token from my auth server using a cUrl command:
curl acme:acmesecret#localhost:9000/oauth/token -d grant_type=password -d username=user -d password=...
{"access_token":"aa49e025-c4fe-4892-86af-15af2e6b72a2","token_type":"bearer","refresh_token":"97a9f978-7aad-4af7-9329-78ff2ce9962d","expires_in":43199,"scope":"read write"}
But what I fail to understand is how can I use the other SSO providers to get such token as well from the auth server? The resource server should not care how did I get the token and whether I am authenticated using Facebook or my custom auth server. It should simply ask the auth server what is the Principal (logged user) and then decide which resources to show him, right?
I don't have any UI and this will be backed for a mobile application so I need to udnerstand how to handle the authentication using REST reqeusts.
If I understand your question correctly,
how can I use the other SSO providers to get such token as well from
the auth server?
This custom Auth server is abstracting out your interaction with FB or Github and issuing you it's own token. The token that your custom Auth server spitting out is not an FB or Github token, it's a token generated by your custom Auth server (After authenticating with FB/Github token).
Then why do we need FB/github?
How else your custom Auth server can identify a person, It sure can use user Id and Password; consider 'login with FB' as another nice option it gives to the user.
How to add other SSO providers like digitalocean in addition to FB and github?
Just do the same as we did for FB and Github (register a client id with digital ocean and then in auth server application, Add client Id and secret in the properties/yaml file etc)
The resource server should not care how did I get the token and
whether I am authenticated using Facebook or my custom auth server. It
should simply ask the auth server what is the Principal (logged user)
and then decide which resources to show him, right?
Yes, your understanding is correct.
Edit (To answer question asked in the comment)
But lets say I log in with Facebook through my Auth server. Where do I
find the token that I can use with the Resource server? Let's say I
have a RestClient and want to make a request to obtain some resource
belonging to a user which went through the Facebook auth process via
my auth server. Where do I find the token to use?
If that's a requirement, I think you can use this example instead; you may not need a custom auth server as such. Whole point of having custom auth server is abstracting out the interaction with FB or github.
Or
If you still want to go with custom auth server direction, then expose an endpoint from Auth server (which will get you the resources you need from FB) and then make use of that from your resource server.

Java: Access an API that uses SAML authentication

I have a web project that needs to pull some data from a hosted versionone instance. The versionone instance uses SAML authentication for single sign-on.
The single sign-on itself uses a username and pin+one-time password for authentication so it is nearly impossible to hard-code those values into the application.
Is there a way to handle this situation? Can I set something in the header of the request to bypass the single-signon process?
As of release 13.1, VersionOne supports OAuth2 for authentication against the API endpoints. Both the Webserver flow (where the hosted instance POSTs auth tokens to your waiting HTTP server) and the Out-of-band flow (where the hosted instance gives the user a code to copy/paste to the client) are supported.
That may be a better match for your requirements, as the SAML process is difficult to complete without a web browser and user present.
Once the OAuth2 credentials are obtained by the app, it can operate without user intervention. At any time, the user may revoke the app's permissions inside of VersionOne.
With OAuth2, the process is:
Register your app with VersionOne, thus creating a client secret
Configure the app with the client secret data from the registration
Have the app request a permission grant, which sends you to the VersionOne server.
Login to the server as the user you wish the app to act as, and accept the grant.
Copy the grant code back to the app if using the out-of-band flow.
The app contacts the VersionOne instance and exchanges the code for a persistent token.
The app can apply the token as an "Authorization: Bearer " HTTP header to achieve access to the VersionOne endpoint
If the token has expired and the request returns Unauthorized, the client may attempt to refresh the token and try again, without user intervention.
There is some documentation available on the VersionOne community site.
And a few (in-progress) examples in the VersionOne OAuth2 Examples repository
We also have a (beta) HTTP proxy that can run on your app's server, forwarding requests to the VersionOne instance after wrapping them with OAuth2 and SSL.

Categories