All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rapidoid.oauth.OAuthTokenHandler Maven / Gradle / Ivy

The newest version!
/*-
 * #%L
 * rapidoid-oauth
 * %%
 * Copyright (C) 2014 - 2018 Nikolche Mihajlovski and contributors
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

package org.rapidoid.oauth;

import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder;
import org.apache.oltu.oauth2.client.response.GitHubTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.ctx.Ctxs;
import org.rapidoid.ctx.UserInfo;
import org.rapidoid.data.JSON;
import org.rapidoid.http.HttpUtils;
import org.rapidoid.http.Req;
import org.rapidoid.http.ReqHandler;
import org.rapidoid.http.customize.Customization;
import org.rapidoid.log.Log;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;
import org.rapidoid.value.Value;

import java.util.Map;
import java.util.Set;


@Authors("Nikolche Mihajlovski")
@Since("2.0.0")
public class OAuthTokenHandler extends RapidoidThing implements ReqHandler {

	private final OAuthProvider provider;
	private final Customization customization;
	private final Value oauthDomain;
	private final OAuthStateCheck stateCheck;
	private final Value clientId;
	private final Value clientSecret;
	private final String callbackPath;

	public OAuthTokenHandler(OAuthProvider provider, Customization customization, Value oauthDomain,
	                         OAuthStateCheck stateCheck, Value clientId, Value clientSecret, String callbackPath) {

		this.provider = provider;
		this.customization = customization;
		this.oauthDomain = oauthDomain;
		this.stateCheck = stateCheck;
		this.clientId = clientId;
		this.clientSecret = clientSecret;
		this.callbackPath = callbackPath;
	}

	@Override
	public Object execute(Req req) throws Exception {
		String code = req.param("code");
		String state = req.param("state");

		Log.debug("Received OAuth code", "code", code, "state", state);

		if (code != null && !U.isEmpty(state)) {

			String id = clientId.str().get();
			String secret = clientSecret.str().get();

			char statePrefix = state.charAt(0);
			U.must(statePrefix == 'P' || statePrefix == 'N', "Invalid OAuth state prefix!");
			state = state.substring(1);

			U.must(stateCheck.isValidState(state, secret, req.sessionId()), "Invalid OAuth state!");

			boolean popup = statePrefix == 'P';
			Log.debug("OAuth validated", "popup", popup);

			String domain = oauthDomain.getOrNull();
			String redirectUrl = U.notEmpty(domain) ? domain + callbackPath : HttpUtils.constructUrl(req, callbackPath);

			TokenRequestBuilder reqBuilder = OAuthClientRequest.tokenLocation(provider.getTokenEndpoint())
				.setGrantType(GrantType.AUTHORIZATION_CODE).setClientId(id).setClientSecret(secret)
				.setRedirectURI(redirectUrl).setCode(code);

			OAuthClientRequest request = paramsInBody() ? reqBuilder.buildBodyMessage() : reqBuilder.buildBodyMessage();

			OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());

			String accessToken = token(request, oAuthClient);

			String profileUrl = Msc.fillIn(provider.getProfileEndpoint(), "token", accessToken);

			OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest(profileUrl).setAccessToken(
				accessToken).buildQueryMessage();

			OAuthResourceResponse res = oAuthClient.resource(bearerClientRequest,
				org.apache.oltu.oauth2.common.OAuth.HttpMethod.GET, OAuthResourceResponse.class);

			U.must(res.getResponseCode() == 200, "OAuth response error!");

			Map auth = JSON.parseMap(res.getBody());

			String email = (String) U.or(auth.get("email"), auth.get("emailAddress"));
			String firstName = (String) U.or(auth.get("firstName"), U.or(auth.get("first_name"), auth.get("given_name")));
			String lastName = (String) U.or(auth.get("lastName"), U.or(auth.get("last_name"), auth.get("family_name")));
			String name = U.or((String) auth.get("name"), firstName + " " + lastName);

			String username = email;
			Set roles = customization.rolesProvider().getRolesForUser(req, username);

			UserInfo user = new UserInfo(username, roles, null);
			user.name = name;
			user.email = email;
			user.oauthProvider = provider.getName();
			user.oauthId = String.valueOf(auth.get("id"));

			Ctxs.required().setUser(user);

			// user.saveTo(x.token()); // FIXME use token

			return req.response().redirect("/"); // FIXME use page stack
		} else {
			String error = req.param("error");
			if (error != null) {
				Log.warn("OAuth error", "error", error);
				throw U.rte("OAuth error!");
			}
		}

		throw U.rte("Invalid OAuth request!");
	}

	private String token(OAuthClientRequest request, OAuthClient oAuthClient) throws Exception {
		String name = provider.getName();
		if (name.equalsIgnoreCase("facebook") || name.equalsIgnoreCase("github")) {
			// application/x-www-form-urlencoded
			GitHubTokenResponse oAuthResponse = oAuthClient.accessToken(request, GitHubTokenResponse.class);
			return oAuthResponse.getAccessToken();
		} else {
			// JSON encoded
			OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(request,
				OAuthJSONAccessTokenResponse.class);
			return oAuthResponse.getAccessToken();
		}
	}

	private boolean paramsInBody() {
		return provider.getName().equalsIgnoreCase("google");
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy