/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egovernment.moa.id.protocols.oauth20.protocol; import java.security.SignatureException; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.idp.IAction; import at.gv.egiz.eaaf.core.api.idp.IAuthData; import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.data.SLOInformationImpl; import at.gv.egiz.eaaf.core.impl.utils.Random; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20SessionObject; import at.gv.egovernment.moa.id.protocols.oauth20.attributes.OAuth20AttributeBuilder; import at.gv.egovernment.moa.id.protocols.oauth20.attributes.OpenIdExpirationTimeAttribute; import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ResponseTypeException; import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ServerErrorException; import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuth20SignatureUtil; import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuthJsonToken; import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuthSigner; import at.gv.egovernment.moa.logging.Logger; @Service("OAuth20AuthAction") public class OAuth20AuthAction implements IAction { @Autowired protected IRevisionLogger revisionsLogger; @Autowired protected ITransactionStorage transactionStorage; public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { OAuth20AuthRequest oAuthRequest = (OAuth20AuthRequest) req; String responseType = oAuthRequest.getResponseType(); revisionsLogger.logEvent(req, MOAIDEventConstants.AUTHPROTOCOL_OPENIDCONNECT_AUTHREQUEST); String code = Random.nextHexRandom32(); try { String accessToken = UUID.randomUUID().toString(); Logger.debug("Build OAuth20SessionObject from authenticationData."); OAuth20SessionObject o = new OAuth20SessionObject(); if (responseType.equals(OAuth20Constants.RESPONSE_CODE)) { o.setScope(oAuthRequest.getScope()); o.setCode(code); //generate idToken from MOASession Map idToken = generateIDToken(o, oAuthRequest, authData, accessToken); o.setAuthDataSession(idToken); } else if (responseType.equals(OAuth20Constants.RESPONSE_TOKEN)) { throw new OAuth20ResponseTypeException(); } // store data in oath session transactionStorage.put(code, o, -1); Logger.debug("Saved OAuth20SessionObject in session with id: " + code); // add code and state to redirect url httpResp.setStatus(HttpServletResponse.SC_FOUND); String redirectURI = oAuthRequest.getRedirectUri(); String state = oAuthRequest.getState(); redirectURI = this.addURLParameter(redirectURI, OAuth20Constants.RESPONSE_CODE, code); redirectURI = this.addURLParameter(redirectURI, OAuth20Constants.PARAM_STATE, state); String finalUrl = redirectURI; httpResp.addHeader("Location", finalUrl); Logger.debug("REDIRECT TO: " + finalUrl.toString()); //TODO: maybe add bPK / wbPK to SLO information SLOInformationInterface sloInformation = new SLOInformationImpl(req.getAuthURL(), req.getServiceProviderConfiguration().getUniqueIdentifier(), accessToken, null, null, req.requestedModule()); return sloInformation; } catch (Exception e) { Logger.warn("An error occur during OpenID-Connect idToken generation.", e); //remove OAuthSessionObject if it already exists if (transactionStorage.containsKey(code)) { transactionStorage.remove(code); } if (e instanceof OAuth20Exception) { throw (OAuth20Exception) e; } throw new OAuth20ServerErrorException(); } } public Map generateIDToken(OAuth20SessionObject auth20SessionObject, OAuth20AuthRequest oAuthRequest, IAuthData authData, String accessToken) throws SignatureException, MOAIDException { // create response Map params = new HashMap(); params.put(OAuth20Constants.RESPONSE_ACCESS_TOKEN, accessToken); params.put(OAuth20Constants.RESPONSE_TOKEN_TYPE, OAuth20Constants.RESPONSE_TOKEN_TYPE_VALUE_BEARER); params.put(OAuth20Constants.RESPONSE_EXPIRES_IN, OpenIdExpirationTimeAttribute.expirationTime); // build id token and scope Pair pair = buildIdToken(auth20SessionObject.getScope(), oAuthRequest, authData); params.put(OAuth20Constants.RESPONSE_ID_TOKEN, pair.getFirst()); params.put(OAuth20Constants.PARAM_SCOPE, pair.getSecond()); Logger.debug("OpenID-Connect ID_TOKEN completed"); Logger.trace("RESPONSE ID_TOKEN: " + pair.getFirst()); Logger.trace("RESPONSE SCOPE: " + pair.getSecond()); return params; } private Pair buildIdToken(String scope, OAuth20AuthRequest oAuthRequest, IAuthData authData) throws MOAIDException, SignatureException { ISPConfiguration oaParam = oAuthRequest.getServiceProviderConfiguration(); OAuthSigner signer = OAuth20SignatureUtil.loadSigner(authData.getAuthenticationIssuer()); OAuthJsonToken token = new OAuthJsonToken(signer); StringBuilder resultScopes = new StringBuilder(); // always fill with open id OAuth20AttributeBuilder.addScopeOpenId(token.getPayloadAsJsonObject(), oaParam, authData, oAuthRequest); resultScopes.append("openId"); if (scope != null) { for (String s : scope.split(" ")) { if (s.equalsIgnoreCase("profile")) { OAuth20AttributeBuilder.addScopeProfile(token.getPayloadAsJsonObject(), oaParam, authData); resultScopes.append(" profile"); } else if (s.equalsIgnoreCase("eID")) { OAuth20AttributeBuilder.addScopeEID(token.getPayloadAsJsonObject(), oaParam, authData); resultScopes.append(" eID"); } else if (s.equalsIgnoreCase("eID_gov")) { OAuth20AttributeBuilder.addScopeEIDGov(token.getPayloadAsJsonObject(), oaParam, authData); resultScopes.append(" eID_gov"); } else if (s.equalsIgnoreCase("mandate")) { OAuth20AttributeBuilder.addScopeMandate(token.getPayloadAsJsonObject(), oaParam, authData); resultScopes.append(" mandate"); } else if (s.equalsIgnoreCase("stork")) { OAuth20AttributeBuilder.addScopeSTORK(token.getPayloadAsJsonObject(), oaParam, authData); resultScopes.append(" stork"); } } } // add properties and sign // HmacSHA256Signer signer = new HmacSHA256Signer("testSigner", "key_id", // "super_secure_pwd".getBytes()); // Signer signer = OAuth20Util.loadSigner(authData.getIssuer(), oaParam.getoAuth20Config()); return Pair.newInstance(token.serializeAndSign(), resultScopes.toString()); } /* * (non-Javadoc) * @see * at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls * .IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { return true; } private String addURLParameter(String url, String name, String value) { String param = name + "=" + value; if (url.indexOf("?") < 0) { return url + "?" + param; } else { return url + "&" + param; } } /* * (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() */ public String getDefaultActionName() { return OAuth20Protocol.AUTH_ACTION; } }