Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ADFS authentication and impersonation from a Java (Spring MVC under Jetty) application

I have a Java web app which provides a search service, and in some cases needs to check security for results. If it matters, it's implemented in Spring MVC and running under jetty.

I have a customer who would like the web app's authentication to:

  • Be done via Active Directory Federation Services (ADFS) instead of the existing build-in mechanism (to avoid a seperate login).
  • Be able to impersonate the remote user on the search server, such that security checks can be performed executing a separate application on the search server (which doesn't itself know anything about ADFS, but is able to perform the relevant checks when run as the user in question).

It this possible, and if so, how?

(Apologies if the Windows world terminology is a bit off - it's not something I know much about, but hopefully at least the intention is clear)


A few notes on pieces of the puzzle I've already looked at:

  1. Impersonating a user from a Java Servlet, is a question I had a number of years ago covering roughly the same ground, but without the ADFS requirement - I'm not sure how ADFS impacts things, but Waffle (the solution for that question) doesn't seem to provide any support for it.
  2. I've seen Java application with SSO (SAML) and ADFS and How do I talk to ADFS from Java?, which seem to provide a way forward for the ADFS authentication, but I'm unsure if that is compatible with subsequent impersonation.
  3. I've seen http://blogs.objectsharp.com/post/2010/09/10/Converting-Claims-to-Windows-Tokens-and-User-Impersonation.aspx and https://msdn.microsoft.com/en-au/library/ee517278.aspx but I'm unsure:

    1. If I'll have access to the necessary claims to do this if I follow the SAML or OAuth path above
    2. Whether it's possible to implement that from within Java
  4. I think the second (impersonation) part is roughly the same as Impersonating ASP.NET claims identity to windows identity, except that I want to do it from within Java rather than .Net.

like image 803
Matt Sheppard Avatar asked Mar 12 '16 00:03

Matt Sheppard


2 Answers

You don't mention the ADFS version?

You have three choices:

  • WS-Fed
  • SAML
  • OAuth2

In the Java world, SAML is normally used. Which implies a SAML stack.

The SO link above has an answer from me with links to a list of SAML stacks.

Since you are already using Spring, Spring Security seems a good fit.

Spring Security SAML Extension

ADFS currently does not support OpenID Connect which rules OAuth out.

Yes - Spring Security provides you with a list of the claims generated by ADFS.

ADFS does provide impersonation via Identity Delegation.

Unfortunately, this is typically done via WCF and WIF (both .NET constructs).

like image 66
rbrayb Avatar answered Sep 16 '22 15:09

rbrayb


I have a similar application- mine is a Swing client rather than web application, but the process should be similar. It needs to submit queries under an assumed role using an AWS API after first authenticating with an on-premise ADFS server. In our environment, ADFS has been configured to give out SAML assertions and AWS has been configured to recognise these. So, this is what I do:

  1. When required, the application prompts the user for their usual network credentials and these are used to request a SAML assertion from ADFS. I use Apache HttpClient to make the call:

    private String getAdfsResponse(String username, String password) throws Exception {
    
    log.debug("Trying to log onto ADFS server for {}", username);
    
    // Lax redirect policy is needed so that all HTTP 302 redirects are followed after hitting the initial ADFS URL.
    try (CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build()) {
    
        HttpUriRequest login = RequestBuilder.post()
                .setUri(new URI(ADFS_URL))
                .addParameter("UserName", username)
                .addParameter("Password", password)
                .build();
    
        CloseableHttpResponse response = httpClient.execute(login);
    
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
    
            HttpEntity responseEntity = response.getEntity();
            String adfsResponse = EntityUtils.toString(responseEntity, "UTF-8");
            log.debug("ADFS server responded with {}", adfsResponse);
            return adfsResponse;
    
        } else {
    
            throw new Exception("ADFS server responded with " + response.getStatusLine());
    
        }
    }
    }
    
  2. If the credentials are validated, ADFS returns a SAML response that looks like an HTML form but contains a input element with a SAMLResponse name/value pair.

  3. When the SAMLResponse value attribute is base64-decoded it will contain the SAML assertion. For AWS, I need to extract some role information and I use this, along with the full SAMLResponse, to call the AWS STS (security token service). If all is OK with AWS, I receive a set of temporary security credentials that I can use for the queries I really want to make. The whole round trip is described in http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_saml.html

All this depends on ADFS and the other party being SAML-configured, and for the other party to provide a suitable API that lets you assume a particular role on their side. Is this the sort of thing you're facing?

like image 32
craigcaulfield Avatar answered Sep 17 '22 15:09

craigcaulfield