package org.atricore.idbus.capabilities.openidconnect.main.proxy.producers;
import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl;
import com.google.api.client.auth.openidconnect.IdToken;
import com.google.api.client.auth.openidconnect.IdTokenResponse;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.GenericUrl;
import com.google.api.services.oauth2.Oauth2;
import com.google.api.services.oauth2.model.Userinfoplus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atricore.idbus.capabilities.openidconnect.main.binding.AuthorizationCodeTokenIdRequest;
import org.atricore.idbus.capabilities.openidconnect.main.binding.OpenIDConnectBinding;
import org.atricore.idbus.capabilities.openidconnect.main.common.OpenIDConnectConstants;
import org.atricore.idbus.capabilities.openidconnect.main.common.OpenIDConnectException;
import org.atricore.idbus.capabilities.openidconnect.main.proxy.OpenIDConnectProxyMediator;
import org.atricore.idbus.capabilities.sso.support.auth.AuthnCtxClass;
import org.atricore.idbus.capabilities.sso.support.core.NameIDFormat;
import org.atricore.idbus.common.sso._1_0.protocol.*;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptor;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptorImpl;
import org.atricore.idbus.kernel.main.mediation.MediationMessageImpl;
import org.atricore.idbus.kernel.main.mediation.MediationState;
import org.atricore.idbus.kernel.main.mediation.camel.AbstractCamelEndpoint;
import org.atricore.idbus.kernel.main.mediation.camel.component.binding.CamelMediationExchange;
import org.atricore.idbus.kernel.main.mediation.camel.component.binding.CamelMediationMessage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by sgonzalez on 2/25/15.
*/
public class GoogleAuthzTokenConsumerProducer extends AuthzTokenConsumerProducer {
private static final Log logger = LogFactory.getLog(GoogleAuthzTokenConsumerProducer.class);
private static final int MAX_NUM_OF_USER_INFO_RETRIES = 1;
public GoogleAuthzTokenConsumerProducer(AbstractCamelEndpoint<CamelMediationExchange> endpoint) throws Exception {
super(endpoint);
}
protected void doProcessAuthzTokenResponse(CamelMediationExchange exchange, AuthorizationCodeResponseUrl authnResp ) throws Exception {
CamelMediationMessage in = (CamelMediationMessage) exchange.getIn();
CamelMediationMessage out = (CamelMediationMessage) exchange.getOut();
MediationState mediationState = in.getMessage().getState();
OpenIDConnectProxyMediator mediator = (OpenIDConnectProxyMediator) channel.getIdentityMediator();
// OpenID Connect authorization code response
String code = authnResp.getCode();
if (authnResp.getError() != null) {
// onError(req, resp, responseUrl);
logger.error("Error received [" + authnResp.getError() + "] " + authnResp.getErrorDescription() + ", uri:" + authnResp.getErrorDescription());
throw new OpenIDConnectException("OpenId Connect error: " + authnResp.getError() + " " + authnResp.getErrorDescription());
} else if (code == null) {
logger.error("Missing authorization code ");
throw new OpenIDConnectException("Illegal response, no authorization code received ");
}
// Validate relay state
String expectedRelayState = (String) mediationState.getLocalVariable("urn:OPENID-CONNECT:1.0:relayState");
String relayState = authnResp.getState();
if (!expectedRelayState.equals(relayState)) {
// Invalid response
if (logger.isDebugEnabled())
logger.debug("Invalid state [" + relayState + "], expected [" + expectedRelayState + "]");
throw new OpenIDConnectException("Illegal response, received OpenID Connect state is not valid");
}
// ---------------------------------------------------------------
// Request access token
// ---------------------------------------------------------------
EndpointDescriptor accessTokenConsumerLocation = resolveAccessTokenConsumerEndpoint(OpenIDConnectConstants.GoogleAuthzTokenConsumerService_QNAME.toString());
GenericUrl requestUrl = new GenericUrl(mediator.getAccessTokenServiceLocation());
// URL used to get the access token.
AuthorizationCodeTokenIdRequest request = new AuthorizationCodeTokenIdRequest(
mediator.getHttpTransport(),
mediator.getJacksonFactory(),
requestUrl,
code,
mediator.getClientId(),
mediator.getClientSecret());
request.setRedirectUri(accessTokenConsumerLocation.getLocation());
IdTokenResponse idTokenResponse = (IdTokenResponse) mediator.sendMessage(request, accessTokenConsumerLocation, channel);
IdToken idToken = idTokenResponse.parseIdToken();
String accessToken = idTokenResponse.getAccessToken();
Long accessTokenExpiresIn = idTokenResponse.getExpiresInSeconds();
String googleSubject = idToken.getPayload().getSubject();
String email = (String) idToken.getPayload().get("email");
if (logger.isDebugEnabled())
logger.debug("Authz token resolved to " + email);
if (logger.isTraceEnabled())
logger.trace("Access token [" + accessToken + "]");
if (logger.isTraceEnabled())
logger.trace("Access token expires in [" + accessTokenExpiresIn + "]");
if (logger.isTraceEnabled())
logger.trace("Subject [" + googleSubject + "]");
// get user info
Oauth2 userInfoService = new Oauth2.Builder(mediator.getHttpTransport(), mediator.getJacksonFactory(),
new GoogleCredential().setAccessToken(accessToken)).build();
int retry = 0;
Userinfoplus user = null;
while (retry <= MAX_NUM_OF_USER_INFO_RETRIES) {
try {
user = userInfoService.userinfo().get().execute();
break;
} catch (IOException e) {
retry++;
logger.error(e.getMessage(), e);
if (retry <= MAX_NUM_OF_USER_INFO_RETRIES) {
logger.debug("Getting Google user info, retry: " + retry);
} else {
logger.error("Failed to get Google user info!");
}
}
}
SubjectType subject;
List<SubjectAttributeType> attrs = new ArrayList<SubjectAttributeType>();
subject = new SubjectType();
SubjectNameIDType a = new SubjectNameIDType();
a.setName(email);
a.setFormat(NameIDFormat.EMAIL.getValue());
a.setLocalName(email);
a.setNameQualifier(getFederatedProvider().getName().toUpperCase());
a.setLocalNameQualifier(getFederatedProvider().getName().toUpperCase());
subject.getAbstractPrincipal().add(a);
SubjectAttributeType accessTokenAttr = new SubjectAttributeType();
accessTokenAttr.setName("accessToken");
accessTokenAttr.setValue(accessToken);
attrs.add(accessTokenAttr);
SubjectAttributeType accessTokenExpiresInAttr = new SubjectAttributeType();
accessTokenExpiresInAttr.setName("accessTokenExpiresIn");
accessTokenExpiresInAttr.setValue(accessTokenExpiresIn + "");
attrs.add(accessTokenExpiresInAttr);
SubjectAttributeType openIdSubjectAttr = new SubjectAttributeType();
openIdSubjectAttr.setName("openIdSubject");
openIdSubjectAttr.setValue(googleSubject);
attrs.add(openIdSubjectAttr);
SubjectAttributeType authnCtxClassAttr = new SubjectAttributeType();
authnCtxClassAttr.setName("authnCtxClass");
authnCtxClassAttr.setValue(AuthnCtxClass.PPT_AUTHN_CTX.getValue());
attrs.add(authnCtxClassAttr);
if (user != null) {
addUserAttributes(user, attrs);
}
SPAuthnResponseType ssoResponse = new SPAuthnResponseType();
ssoResponse.setID(uuidGenerator.generateId());
ssoResponse.setIssuer(getFederatedProvider().getName());
SPInitiatedAuthnRequestType ssoRequest =
(SPInitiatedAuthnRequestType) in.getMessage().getState().
getLocalVariable("urn:org:atricore:idbus:sso:protocol:SPInitiatedAuthnRequest");
if (ssoRequest != null) {
ssoResponse.setInReplayTo(ssoRequest.getID());
}
ssoResponse.setSessionIndex(uuidGenerator.generateId());
ssoResponse.setSubject(subject);
ssoResponse.getSubjectAttributes().addAll(attrs);
// ------------------------------------------------------------------------------
// Send SP Authentication response
// ------------------------------------------------------------------------------
SPInitiatedAuthnRequestType authnRequest = (SPInitiatedAuthnRequestType) mediationState.getLocalVariable("urn:OPENID-CONNECT:1.0:authnRequest");
// Send response back
String destinationLocation = resolveSpProxyACS(authnRequest);
if (logger.isTraceEnabled())
logger.trace("Sending response to " + destinationLocation);
EndpointDescriptor destination =
new EndpointDescriptorImpl("EmbeddedSPAcs",
"AssertionConsumerService",
OpenIDConnectBinding.SSO_ARTIFACT.getValue(),
destinationLocation, null);
out.setMessage(new MediationMessageImpl(ssoResponse.getID(),
ssoResponse, "SPAuthnResponse", "", destination, in.getMessage().getState()));
exchange.setOut(out);
return;
}
private void addUserAttributes(Userinfoplus user, List<SubjectAttributeType> attrs) {
addUserAttribute(EMAIL_USER_ATTR_NAME, user.getEmail(), attrs);
addUserAttribute(FIRST_NAME_USER_ATTR_NAME, user.getGivenName(), attrs);
addUserAttribute(LAST_NAME_USER_ATTR_NAME, user.getFamilyName(), attrs);
addUserAttribute(COMMON_NAME_USER_ATTR_NAME, user.getName(), attrs);
addUserAttribute(GENDER_USER_ATTR_NAME, user.getGender(), attrs);
addUserAttribute(LANGUAGE_USER_ATTR_NAME, user.getLocale(), attrs);
addUserAttribute(PICTURE_USER_ATTR_NAME, user.getPicture(), attrs);
addUserAttribute(PROFILE_LINK_USER_ATTR_NAME, user.getLink(), attrs);
addUserAttribute(IS_VERIFIED_USER_ATTR_NAME, String.valueOf(user.isVerifiedEmail()), attrs);
}
}