Tuesday 14 July 2015

Working example on OAuth2 + Spring Security + Apache Oltu

Components:
  Spring MVC + Security  3.0.5.RELEASE
  Apache Oltu 1.0.1-SNAPSHOT

POM:
       <!-- add spring / security / mvc dependencies -->

        <!--DO NOT USE version 1.0.0 which causes oauth2 phrase2 auth problem with Google -->
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.client</artifactId>
            <version>1.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.common</artifactId>
            <version>1.0.1-SNAPSHOT</version>
        </dependency>

Step 1: send request to get authorization code

Apache Oltu has defined some oauth providers, e.g. google, facebook, then you need to do something like:
OAuthClientRequest request = OAuthClientRequest
                    .authorizationProvider(OAuthProviderType.GOOGLE)
                    .setClientId(YOUR_CLIENT_ID)
                    .setRedirectURI(YOUR_REDIRECT_URI)
                    .setResponseType("code")
                    .setScope("https://www.googleapis.com/auth/userinfo.email")
                    .buildQueryMessage();
response.sendRedirect(request.getLocationUri());

If the oauth provider is not defined in Oltu, then set authorization location manually:
OAuthClientRequest request = OAuthClientRequest
                        .authorizationLocation("https://api.login.yahoo.com/oauth2/request_auth")
                       .setClientId(YOUR_CLIENT_ID)
                       .setRedirectURI(YOUR_REDIRECT_URI)                        
                       .setResponseType("code")
                       .buildQueryMessage();
response.sendRedirect(request.getLocationUri());

For oauth providers not lised in Oltu, here are some URLs
YAHOO_AUTHORIZATION_URL = "https://api.login.yahoo.com/oauth2/request_auth";
YAHOO_TOKEN_URL  = "https://api.login.yahoo.com/oauth2/get_token";
YAHOO_GUID_URL     = "http://social.yahooapis.com/v1/me/guid";
YAHOO_USER_PROFILE_URL = "https://social.yahooapis.com/v1/user/__USERID__/profile?format=json";

TWITTER_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token";
TWITTER_AUTHORIZATION_URL = "https://api.twitter.com/oauth/authorize";
TWITTER_TOKEN_URL         = "https://api.twitter.com/oauth/access_token";
TWITTER_PROFILE_URL       = "https://api.twitter.com/1.1/users/profile_banner.json"

Step 2: get access code

Do the following in your code linking to REDIRECT_URI

2.1 Get authorization code:

OAuthAuthzResponse oar = OAuthAuthzResponse.oauthCodeAuthzResponse(httpRequest);
String code = oar.getCode();


2.2 Use Oltu to get access token, e.g. from Google


OAuthClientRequest oauthRequest = OAuthClientRequest
                .tokenProvider(OAuthProviderType.GOOGLE)
                .setClientId(CLIENT_ID)
                .setClientSecret(CLIENT_SECRET)
                .setRedirectURI(REDIRECT_URI)
                .setGrantType(GrantType.AUTHORIZATION_CODE)
                .setCode(code)
                .buildBodyMessage();
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
final OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(oauthRequest, "POST");
String accessToken = oAuthResponse.getAccessToken();
long expiresIn    = oAuthResponse.getExpiresIn();

Same code to get access token from FACEBOOK


2.3 Use Apache Httpclient to get access token from Yahoo

I cannot get Oltu working with Yahoo due to the data being sent from Oltu (HTTP header) is not accepted by Yahoo

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost     = new HttpPost(OAuth2Manager.YAHOO_TOKEN_URL);

List<NameValuePair> params = new ArrayList<NameValuePair>(2);
params.add(new BasicNameValuePair("client_id", CLIENT_ID);
params.add(new BasicNameValuePair("client_secret", CLIENT_SECRET);
params.add(new BasicNameValuePair("redirect_uri", REDIRECT_URI);
params.add(new BasicNameValuePair("code", code));
params.add(new BasicNameValuePair("grant_type", "authorization_code"));
params.add(new BasicNameValuePair("response_type", "code"));
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));

String encodedValue=new String(Base64.encodeBase64(CLIENT_ID+":"+CLIENT_SECRET).getBytes("UTF-8"));
httppost.setHeader("Authorization", "Basic "+encodedValue);
HttpResponse oresponse = httpclient.execute(httppost);

org.apache.http.HttpEntity entity = oresponse.getEntity();

String theString = IOUtils.toString(entity.getContent(), "UTF-8");
// parse JSON string here ...
final JSONObject obj = new JSONObject(theString);
String access_token = obj.getString("access_token");
String xoauth_yahoo_guid = obj.getString("xoauth_yahoo_guid");

Step 3: retrieve user information

3.1 Google

OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest("https://www.googleapis.com/plus/v1/people/me")
                .setAccessToken(accessToken).buildQueryMessage();
OAuthResourceResponse resourceResponse = oAuthClient.resource(bearerClientRequest, "GET", OAuthResourceResponse.class);
String userinfoJSON = resourceResponse.getBody();
final JSONObject obj    = new JSONObject(userinfoJSON);
final JSONArray  emails = obj.getJSONArray("emails");
final int n = emails.length();
for (int i = 0; i < n; ++i) {
  final JSONObject email = emails.getJSONObject(i);
  String type = email.getString("type");
  if("account".equals(type)) {
      System.out.println(email.getString("value"));
  }
}
String googleid = obj.getString("id");
String fname     = obj.getJSONObject("name").getString("givenName");
String lname     = obj.getJSONObject("name").getString("familyName");


3.2 Facebook

A bit different from 3.1,

// Facebook’s response is not fully compliant with the final version of the OAuth 2 specification, but it can be parsed using the class GitHubTokenResponse.
GitHubTokenResponse oAuthResponse = oAuthClient.accessToken(oauthRequest, GitHubTokenResponse.class);
String accessToken = oAuthResponse.getAccessToken();

OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest("https://graph.facebook.com/me?fields=id,first_name,last_name,email")
                .setAccessToken(accessToken).buildQueryMessage();
OAuthResourceResponse resourceResponse = oAuthClient.resource(bearerClientRequest, "GET", OAuthResourceResponse.class);
String userinfoJSON = resourceResponse.getBody();
final JSONObject obj    = new JSONObject(userinfoJSON);


3.3 Yahoo

HttpGet getrequest = new HttpGet("https://social.yahooapis.com/v1/user/__USERID__/profile?format=json".replace("__USERID__", xoauth_yahoo_guid));
getrequest.addHeader("Authorization", "Bearer " + access_token);
HttpResponse getresponse = httpclient.execute(getrequest);
String userinfoJSON = EntityUtils.toString(getresponse.getEntity());

final JSONObject profileobj = new JSONObject(userinfoJSON);
String yahooid = profileobj.getJSONObject("profile").getString("guid")

Step 4: automatic login in Spring Security


public static void autoLogin(
                    AuthenticationProvider authenticationProvider,
                    String oauthProvider,
                    OAuth2User oauth2User,
                    HttpServletRequest request) {
        try {
            Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
            grantedAuthorities.add(new GrantedAuthorityImpl("ROLE1"));
            grantedAuthorities.add(new GrantedAuthorityImpl("ROLE2"));

            // check if user is admin
            if (adminIDs.contains(oauth2User.getID())) {
                grantedAuthorities.add(new GrantedAuthorityImpl("admin"));
            }

            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("USER", "PASS", grantedAuthorities);
            token.setDetails(new WebAuthenticationDetails(request));
            Authentication authentication = authenticationProvider.authenticate(token);

            SecurityContextHolder.getContext().setAuthentication(authentication);
         
            // Create a new session and add the security context.
            HttpSession session = request.getSession(true);
            session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
        } catch (Exception e) {
            SecurityContextHolder.getContext().setAuthentication(null);
            logger.error("Failure in autoLogin", e);
        }
    }




2 comments:

  1. Hey Thanks very much for the post, can I have the complete source code please ?

    ReplyDelete
  2. Any example for Use Apache Httpclient to get access token from Twitter?

    ReplyDelete